From 558a3fc865ea708192c58832851fb24d38783506 Mon Sep 17 00:00:00 2001 From: Riley Testut Date: Fri, 4 Jun 2021 13:57:40 -0700 Subject: [PATCH] [AltServer] Improves error messages --- AltServer/AppDelegate.swift | 87 ++++++++++++++----- .../ALTDeviceManager+Installation.swift | 14 ++- AltServer/Devices/ALTDeviceManager.mm | 5 ++ AltStore.xcodeproj/project.pbxproj | 4 +- Shared/Categories/NSError+ALTServerError.m | 6 +- .../Extensions/NSError+AltStore.swift | 38 +++++++- 6 files changed, 122 insertions(+), 32 deletions(-) rename {AltStore => Shared}/Extensions/NSError+AltStore.swift (65%) diff --git a/AltServer/AppDelegate.swift b/AltServer/AppDelegate.swift index c5a4cc08..8895159e 100644 --- a/AltServer/AppDelegate.swift +++ b/AltServer/AppDelegate.swift @@ -139,7 +139,7 @@ private extension AppDelegate switch result { case .failure(let error): - self.showErrorAlert(title: String(format: NSLocalizedString("JIT compilation could not be enabled for %@.", comment: ""), app.name), error: error) + self.showErrorAlert(error: error, localizedFailure: String(format: NSLocalizedString("JIT compilation could not be enabled for %@.", comment: ""), app.name)) case .success: let alert = NSAlert() @@ -234,28 +234,8 @@ private extension AppDelegate // Ignore break - case .failure(let error as NSError): - - let alert = NSAlert() - alert.alertStyle = .critical - alert.messageText = NSLocalizedString("Installation Failed", comment: "") - - if let underlyingError = error.userInfo[NSUnderlyingErrorKey] as? Error - { - alert.informativeText = underlyingError.localizedDescription - } - else if let recoverySuggestion = error.localizedRecoverySuggestion - { - alert.informativeText = error.localizedDescription + "\n\n" + recoverySuggestion - } - else - { - alert.informativeText = error.localizedDescription - } - - NSRunningApplication.current.activate(options: .activateIgnoringOtherApps) - - alert.runModal() + case .failure(let error): + self.showErrorAlert(error: error, localizedFailure: String(format: NSLocalizedString("Could not install app to %@.", comment: ""), device.name)) } } } @@ -289,6 +269,67 @@ private extension AppDelegate } } + func showErrorAlert(error: Error, localizedFailure: String) + { + let nsError = error as NSError + + let alert = NSAlert() + alert.alertStyle = .critical + alert.messageText = localizedFailure + + var messageComponents = [String]() + + if let errorFailure = nsError.localizedFailure + { + if let failureReason = nsError.localizedFailureReason + { + if nsError.localizedDescription.starts(with: errorFailure) + { + alert.messageText = errorFailure + messageComponents.append(failureReason) + } + else + { + alert.messageText = errorFailure + messageComponents.append(nsError.localizedDescription) + } + } + else + { + // No failure reason given. + + if nsError.localizedDescription.starts(with: errorFailure) + { + // No need to duplicate errorFailure in both title and message. + alert.messageText = localizedFailure + messageComponents.append(nsError.localizedDescription) + } + else + { + alert.messageText = errorFailure + messageComponents.append(nsError.localizedDescription) + } + } + } + else + { + alert.messageText = localizedFailure + messageComponents.append(nsError.localizedDescription) + } + + if let recoverySuggestion = nsError.localizedRecoverySuggestion + { + messageComponents.append(recoverySuggestion) + } + + let informativeText = messageComponents.joined(separator: " ") + alert.informativeText = informativeText + + NSRunningApplication.current.activate(options: .activateIgnoringOtherApps) + + alert.runModal() + } + @objc func toggleLaunchAtLogin(_ item: NSMenuItem) { LaunchAtLogin.isEnabled.toggle() diff --git a/AltServer/Devices/ALTDeviceManager+Installation.swift b/AltServer/Devices/ALTDeviceManager+Installation.swift index a51145bf..6d15b93c 100644 --- a/AltServer/Devices/ALTDeviceManager+Installation.swift +++ b/AltServer/Devices/ALTDeviceManager+Installation.swift @@ -38,10 +38,22 @@ extension ALTDeviceManager { let destinationDirectoryURL = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString) + var appName = (url.isFileURL) ? url.deletingPathExtension().lastPathComponent : NSLocalizedString("AltStore", comment: "") + func finish(_ result: Result, title: String = "") { DispatchQueue.main.async { - completion(result) + switch result + { + case .success(let app): completion(.success(app)) + case .failure(var error as NSError): + if error.localizedFailure == nil + { + error = error.withLocalizedFailure(String(format: NSLocalizedString("Could not install %@ to %@.", comment: ""), appName, altDevice.name)) + } + + completion(.failure(error)) + } } try? FileManager.default.removeItem(at: destinationDirectoryURL) diff --git a/AltServer/Devices/ALTDeviceManager.mm b/AltServer/Devices/ALTDeviceManager.mm index 23be4260..7ab11b77 100644 --- a/AltServer/Devices/ALTDeviceManager.mm +++ b/AltServer/Devices/ALTDeviceManager.mm @@ -1126,6 +1126,11 @@ NSNotificationName const ALTDeviceManagerDeviceDidDisconnectNotification = @"ALT idevice_free(device); } + if (error) + { + error = [error alt_errorWithLocalizedFailure:[NSString stringWithFormat:NSLocalizedString(@"The Developer disk image could not be installed onto %@.", @""), altDevice.name]]; + } + completionHandler(error == nil, error); }; diff --git a/AltStore.xcodeproj/project.pbxproj b/AltStore.xcodeproj/project.pbxproj index e930b9cf..3d82d1d5 100644 --- a/AltStore.xcodeproj/project.pbxproj +++ b/AltStore.xcodeproj/project.pbxproj @@ -31,6 +31,7 @@ BF1E312B229F474900370A3C /* RequestHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF1E3129229F474900370A3C /* RequestHandler.swift */; }; BF1FE358251A9FB000C3CE09 /* NSXPCConnection+MachServices.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF1FE357251A9FB000C3CE09 /* NSXPCConnection+MachServices.swift */; }; BF1FE359251A9FB000C3CE09 /* NSXPCConnection+MachServices.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF1FE357251A9FB000C3CE09 /* NSXPCConnection+MachServices.swift */; }; + BF265D1925F843A000080DC9 /* NSError+AltStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF6C336124197D700034FD24 /* NSError+AltStore.swift */; }; BF29012F2318F6B100D88A45 /* AppBannerView.xib in Resources */ = {isa = PBXBuildFile; fileRef = BF29012E2318F6B100D88A45 /* AppBannerView.xib */; }; BF2901312318F7A800D88A45 /* AppBannerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF2901302318F7A800D88A45 /* AppBannerView.swift */; }; BF340E9A250AD39500A192CB /* ViewApp.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = BF989191250AAE86002ACF50 /* ViewApp.intentdefinition */; }; @@ -1556,7 +1557,6 @@ isa = PBXGroup; children = ( BFF0B6992322D7D0007A79E1 /* UIScreen+CompactHeight.swift */, - BF6C336124197D700034FD24 /* NSError+AltStore.swift */, BF663C4E2433ED8200DAA738 /* FileManager+DirectorySize.swift */, BF8CAE4D248AEABA004D6CCE /* UIDevice+Jailbreak.swift */, BFE00A1F2503097F00EB4D0C /* INInteraction+AltStore.swift */, @@ -1665,6 +1665,7 @@ BFF767CB2489AB5C0097E58C /* ALTServerError+Conveniences.swift */, BF1FE357251A9FB000C3CE09 /* NSXPCConnection+MachServices.swift */, BFF435D7255CBDAB00DD724F /* ALTApplication+AltStoreApp.swift */, + BF6C336124197D700034FD24 /* NSError+AltStore.swift */, ); path = Extensions; sourceTree = ""; @@ -2298,6 +2299,7 @@ BFECAC8124FD950B0077C41F /* ALTServerError+Conveniences.swift in Sources */, BFECAC7F24FD950B0077C41F /* CodableServerError.swift in Sources */, BFECAC8624FD950B0077C41F /* Result+Conveniences.swift in Sources */, + BF265D1925F843A000080DC9 /* NSError+AltStore.swift in Sources */, BF904DEA265DAE9A00E86C2A /* InstalledApp.swift in Sources */, BFF435D9255CBDAB00DD724F /* ALTApplication+AltStoreApp.swift in Sources */, BF718BD523C928A300A89F2D /* ALTNotificationConnection.mm in Sources */, diff --git a/Shared/Categories/NSError+ALTServerError.m b/Shared/Categories/NSError+ALTServerError.m index 13ed5115..6a60dce7 100644 --- a/Shared/Categories/NSError+ALTServerError.m +++ b/Shared/Categories/NSError+ALTServerError.m @@ -75,7 +75,7 @@ NSErrorUserInfoKey const ALTDeviceNameErrorKey = @"deviceName"; case ALTServerErrorConnectionFailed: #if TARGET_OS_OSX - return NSLocalizedString(@"Could not connect to device.", @""); + return NSLocalizedString(@"There was an error connecting to the device.", @""); #else return NSLocalizedString(@"Could not connect to AltServer.", @""); #endif @@ -114,10 +114,10 @@ NSErrorUserInfoKey const ALTDeviceNameErrorKey = @"deviceName"; return NSLocalizedString(@"Received an unknown response from AltServer.", @""); case ALTServerErrorInvalidAnisetteData: - return NSLocalizedString(@"Invalid anisette data.", @""); + return NSLocalizedString(@"The provided anisette data is invalid.", @""); case ALTServerErrorPluginNotFound: - return NSLocalizedString(@"Could not connect to Mail plug-in.", @""); + return NSLocalizedString(@"AltServer could not connect to Mail plug-in.", @""); case ALTServerErrorProfileNotFound: return [self profileErrorLocalizedDescriptionWithBaseDescription:NSLocalizedString(@"Could not find profile", "")]; diff --git a/AltStore/Extensions/NSError+AltStore.swift b/Shared/Extensions/NSError+AltStore.swift similarity index 65% rename from AltStore/Extensions/NSError+AltStore.swift rename to Shared/Extensions/NSError+AltStore.swift index 90b9346d..f3ae0eb1 100644 --- a/AltStore/Extensions/NSError+AltStore.swift +++ b/Shared/Extensions/NSError+AltStore.swift @@ -16,13 +16,38 @@ extension NSError return localizedFailure } + @objc(alt_errorWithLocalizedFailure:) func withLocalizedFailure(_ failure: String) -> NSError { var userInfo = self.userInfo userInfo[NSLocalizedFailureErrorKey] = failure - userInfo[NSLocalizedDescriptionKey] = self.localizedDescription - userInfo[NSLocalizedFailureReasonErrorKey] = self.localizedFailureReason - userInfo[NSLocalizedRecoverySuggestionErrorKey] = self.localizedRecoverySuggestion + + if let failureReason = self.localizedFailureReason + { + userInfo[NSLocalizedFailureReasonErrorKey] = failureReason + } + else if self.localizedFailure == nil && self.localizedFailureReason == nil && self.localizedDescription.contains(self.localizedErrorCode) + { + // Default localizedDescription, so replace with just the localized error code portion. + userInfo[NSLocalizedFailureReasonErrorKey] = "(\(self.localizedErrorCode).)" + } + else + { + userInfo[NSLocalizedFailureReasonErrorKey] = self.localizedDescription + } + + if let localizedDescription = NSError.userInfoValueProvider(forDomain: self.domain)?(self, NSLocalizedDescriptionKey) as? String + { + userInfo[NSLocalizedDescriptionKey] = localizedDescription + } + + // Don't accidentally remove localizedDescription from dictionary + // userInfo[NSLocalizedDescriptionKey] = NSError.userInfoValueProvider(forDomain: self.domain)?(self, NSLocalizedDescriptionKey) as? String + + if let recoverySuggestion = self.localizedRecoverySuggestion + { + userInfo[NSLocalizedRecoverySuggestionErrorKey] = recoverySuggestion + } let error = NSError(domain: self.domain, code: self.code, userInfo: userInfo) return error @@ -50,6 +75,11 @@ extension Error let underlyingError = (self as NSError).userInfo[NSUnderlyingErrorKey] as? Error return underlyingError } + + var localizedErrorCode: String { + let localizedErrorCode = String(format: NSLocalizedString("%@ error %@", comment: ""), (self as NSError).domain, (self as NSError).code as NSNumber) + return localizedErrorCode + } } protocol ALTLocalizedError: LocalizedError, CustomNSError @@ -86,7 +116,7 @@ extension ALTLocalizedError return errorDescription } - var failureReason: String? { (self.underlyingError as NSError?)?.localizedFailureReason ?? (self.underlyingError as NSError?)?.localizedDescription } + var failureReason: String? { (self.underlyingError as NSError?)?.localizedDescription } var recoverySuggestion: String? { (self.underlyingError as NSError?)?.localizedRecoverySuggestion } var helpAnchor: String? { (self.underlyingError as NSError?)?.helpAnchor } }