[Shared] Refactors error handling based on ALTLocalizedError protocol (#1115)

* [Shared] Revises ALTLocalizedError protocol

* Refactors errors to conform to revised ALTLocalizedError protocol

* [Missing Commit] Remaining changes for ALTLocalizedError

* [AltServer] Refactors errors to conform to revised ALTLocalizedError protocol

* [Missing Commit] Declares ALTLocalizedTitleErrorKey + ALTLocalizedDescriptionKey

* Updates Objective-C errors to match revised ALTLocalizedError

* [Missing Commit] Unnecessary ALTLocalizedDescription logic

* [Shared] Refactors NSError.withLocalizedFailure to properly support ALTLocalizedError

* [Shared] Supports adding localized titles to errors via NSError.withLocalizedTitle()

* Revises ErrorResponse logic to support arbitrary errors and user info values

* [Missed Commit] Renames CodableServerError to CodableError

* Merges ConnectionError into OperationError

* [Missed Commit] Doesn’t check ALTWrappedError’s userInfo for localizedDescription

* [Missed] Fixes incorrect errorDomain for ALTErrorEnums

* [Missed] Removes nonexistent ALTWrappedError.h

* Includes source file and line number in OperationError.unknown failureReason

* Adds localizedTitle to AppManager operation errors

* Fixes adding localizedTitle + localizedFailure to ALTWrappedError

* Updates ToastView to use error’s localizedTitle as title

* [Shared] Adds NSError.formattedDetailedDescription(with:)

Returns formatted NSAttributedString containing all user info values intended for displaying to the user.

* [Shared] Updates Error.localizedErrorCode to say “code” instead of “error”

* Conforms ALTLocalizedError to CustomStringConvertible

* Adds “View More Details” option to Error Log context menu to view detailed error description

* [Shared] Fixes NSError.formattedDetailedDescription appearing black in dark mode

* [AltServer] Updates error alert to match revised error logic

Uses error’s localizedTitle as alert title.

* [AltServer] Adds “View More Details” button to error alert to view detailed error info

* [AltServer] Renames InstallError to OperationError and conforms to ALTErrorEnum

* [Shared] Removes CodableError support for Date user info values

Not currently used, and we don’t want to accidentally parse a non-Date as a Date in the meantime.

* [Shared] Includes dynamic UserInfoValueProvider values in NSError.formattedDetailedDescription()

* [Shared] Includes source file + line in NSError.formattedDetailedDescription()

Automatically captures source file + line when throwing ALTErrorEnums.

* [Shared] Captures source file + line for unknown errors

* Removes sourceFunction from OperationError

* Adds localizedTitle to AuthenticationViewController errors

* [Shared] Moves nested ALTWrappedError logic to ALTWrappedError initializer

* [AltServer] Removes now-redundant localized failure from JIT errors

All JIT errors now have a localizedTitle which effectively says the same thing.

* Makes OperationError.Code start at 1000

“Connection errors” subsection starts at 1200.

* [Shared] Updates Error domains to revised [Source].[ErrorType] format

* Updates ALTWrappedError.localizedDescription to prioritize using wrapped NSLocalizedDescription as failure reason

* Makes ALTAppleAPIError codes start at 3000

* [AltServer] Adds relevant localizedFailures to ALTDeviceManager.installApplication() errors

* Revises OperationError failureReasons and recovery suggestions

All failure reasons now read correctly when preceded by a failure reason and “because”.

* Revises ALTServerError error messages
All failure reasons now read correctly when preceded by a failure reason and “because”.

* Most failure reasons now read correctly when preceded by a failure reason and “because”.
* ALTServerErrorUnderlyingError forwards all user info provider calls to underlying error.

* Revises error messages for ALTAppleAPIErrorIncorrectCredentials

* [Missed] Removes NSError+AltStore.swift from AltStore target

* [Shared] Updates AltServerErrorDomain to revised [Source].[ErrorType] format

* [Shared] Removes “code” from Error.localizedErrorCode

* [Shared] Makes ALTServerError codes (appear to) start at 2000

We can’t change the actual error codes without breaking backwards compatibility, so instead we just add 2000 whenever we display ALTServerError codes to the user.

* Moves VerificationError.errorFailure to VerifyAppOperation

* Supports custom failure reason for OperationError.unknown

* [Shared] Changes AltServerErrorDomain to “AltServer.ServerError”

* [Shared] Converts ALTWrappedError to Objective-C class

NSError subclasses must be written in ObjC for Swift.Error <-> NSError bridging to work correctly.

# Conflicts:
#	AltStore.xcodeproj/project.pbxproj

* Fixes decoding CodableError nested user info values
This commit is contained in:
Riley Testut
2023-01-24 13:56:41 -06:00
committed by GitHub
parent 0c4fe98370
commit 3b38d725d7
44 changed files with 1707 additions and 644 deletions

View File

@@ -14,34 +14,21 @@ private let appGroupsSemaphore = DispatchSemaphore(value: 1)
private let developerDiskManager = DeveloperDiskManager()
enum InstallError: Int, LocalizedError, _ObjectiveCBridgeableError
typealias OperationError = OperationErrorCode.Error
enum OperationErrorCode: Int, ALTErrorEnum
{
case cancelled
case noTeam
case missingPrivateKey
case missingCertificate
var errorDescription: String? {
var errorFailureReason: String {
switch self
{
case .cancelled: return NSLocalizedString("The operation was cancelled.", comment: "")
case .noTeam: return "You are not a member of any developer teams."
case .missingPrivateKey: return "The developer certificate's private key could not be found."
case .missingCertificate: return "The developer certificate could not be found."
}
}
init?(_bridgedNSError error: NSError)
{
guard error.domain == InstallError.cancelled._domain else { return nil }
if let installError = InstallError(rawValue: error.code)
{
self = installError
}
else
{
return nil
case .noTeam: return NSLocalizedString("You are not a member of any developer teams.", comment: "")
case .missingPrivateKey: return NSLocalizedString("The developer certificate's private key could not be found.", comment: "")
case .missingCertificate: return NSLocalizedString("The developer certificate could not be found.", comment: "")
}
}
}
@@ -54,16 +41,18 @@ extension ALTDeviceManager
var appName = (url.isFileURL) ? url.deletingPathExtension().lastPathComponent : NSLocalizedString("AltStore", comment: "")
func finish(_ result: Result<ALTApplication, Error>, title: String = "")
func finish(_ result: Result<ALTApplication, Error>, failure: String? = nil)
{
DispatchQueue.main.async {
switch result
{
case .success(let app): completion(.success(app))
case .failure(var error as NSError):
if error.localizedFailure == nil
error = error.withLocalizedTitle(String(format: NSLocalizedString("%@ could not be installed onto %@.", comment: ""), appName, altDevice.name))
if let failure, error.localizedFailure == nil
{
error = error.withLocalizedFailure(String(format: NSLocalizedString("Could not install %@ to %@.", comment: ""), appName, altDevice.name))
error = error.withLocalizedFailure(failure)
}
completion(.failure(error))
@@ -144,24 +133,25 @@ extension ALTDeviceManager
let profiles = try result.get()
self.install(application, to: device, team: team, certificate: certificate, profiles: profiles) { (result) in
finish(result.map { application }, title: "Failed to Install AltStore")
finish(result.map { application })
}
}
catch
{
finish(.failure(error), title: "Failed to Fetch Provisioning Profiles")
finish(.failure(error), failure: NSLocalizedString("AltServer could not fetch new provisioning profiles.", comment: ""))
}
}
}
catch
{
finish(.failure(error), title: "Failed to Refresh Anisette Data")
finish(.failure(error))
}
}
}
catch
{
finish(.failure(error), title: "Failed to Download AltStore")
let failure = String(format: NSLocalizedString("%@ could not be downloaded.", comment: ""), appName)
finish(.failure(error), failure: failure)
}
}
}
@@ -169,31 +159,31 @@ extension ALTDeviceManager
}
catch
{
finish(.failure(error), title: "Failed to Fetch Certificate")
finish(.failure(error), failure: NSLocalizedString("A valid signing certificate could not be created.", comment: ""))
}
}
}
catch
{
finish(.failure(error), title: "Failed to Register Device")
finish(.failure(error), failure: NSLocalizedString("Your device could not be registered with your development team.", comment: ""))
}
}
}
catch
{
finish(.failure(error), title: "Failed to Fetch Team")
finish(.failure(error))
}
}
}
catch
{
finish(.failure(error), title: "Failed to Authenticate")
finish(.failure(error), failure: NSLocalizedString("AltServer could not sign in with your Apple ID.", comment: ""))
}
}
}
catch
{
finish(.failure(error), title: "Failed to Fetch Anisette Data")
finish(.failure(error))
}
}
}
@@ -306,7 +296,7 @@ private extension ALTDeviceManager
}
else
{
completionHandler(.failure(error ?? ALTAppleAPIError(.unknown)))
completionHandler(.failure(error ?? ALTAppleAPIError.unknown()))
}
}
}
@@ -332,7 +322,7 @@ private extension ALTDeviceManager
}
else
{
throw InstallError.noTeam
throw OperationError(.noTeam)
}
}
catch
@@ -384,7 +374,7 @@ private extension ALTDeviceManager
}
}
guard !isCancelled else { return completionHandler(.failure(InstallError.cancelled)) }
guard !isCancelled else { return completionHandler(.failure(OperationError(.cancelled))) }
}
func addCertificate()
@@ -393,7 +383,7 @@ private extension ALTDeviceManager
do
{
let certificate = try Result(certificate, error).get()
guard let privateKey = certificate.privateKey else { throw InstallError.missingPrivateKey }
guard let privateKey = certificate.privateKey else { throw OperationError(.missingPrivateKey) }
ALTAppleAPI.shared.fetchCertificates(for: team, session: session) { (certificates, error) in
do
@@ -401,7 +391,7 @@ private extension ALTDeviceManager
let certificates = try Result(certificates, error).get()
guard let certificate = certificates.first(where: { $0.serialNumber == certificate.serialNumber }) else {
throw InstallError.missingCertificate
throw OperationError(.missingCertificate)
}
certificate.privateKey = privateKey
@@ -454,7 +444,7 @@ private extension ALTDeviceManager
}
}
guard !isCancelled else { return completionHandler(.failure(InstallError.cancelled)) }
guard !isCancelled else { return completionHandler(.failure(OperationError(.cancelled))) }
}
ALTAppleAPI.shared.revoke(certificate, for: team, session: session) { (success, error) in

View File

@@ -1217,7 +1217,11 @@ NSNotificationName const ALTDeviceManagerDeviceDidDisconnectNotification = @"ALT
// ALTServerErrorUnderlyingError uses its underlying error's failure reason as its error description (if one exists),
// so assign our recovery suggestion to NSLocalizedFailureReasonErrorKey to make sure it's always displayed on client.
NSError *underlyingError = [NSError errorWithDomain:AltServerConnectionErrorDomain code:ALTServerConnectionErrorUnknown userInfo:@{NSLocalizedFailureReasonErrorKey: recoverySuggestion}];
NSError *underlyingError = [NSError errorWithDomain:AltServerConnectionErrorDomain code:ALTServerConnectionErrorUnknown userInfo:@{
NSLocalizedFailureReasonErrorKey: recoverySuggestion,
ALTSourceFileErrorKey: @(__FILE__).lastPathComponent,
ALTSourceLineErrorKey: @(__LINE__)
}];
returnError = [NSError errorWithDomain:AltServerErrorDomain code:ALTServerErrorUnderlyingError userInfo:@{NSUnderlyingErrorKey: underlyingError}];
}