Updates SourceError.blocked recovery suggestion to list installed/blocked apps

If source is already added, the error message will list all installed apps from the source.

If adding source for first time, the error message will mention exactly which apps have been blocked from the source (if provided).
This commit is contained in:
Riley Testut
2023-05-16 15:39:38 -05:00
committed by Magesh K
parent 177d453491
commit 5a2f32704c
8 changed files with 202 additions and 86 deletions

View File

@@ -29,9 +29,9 @@ extension SourceError
static func duplicateBundleID(_ bundleID: String, source: Source) -> SourceError { SourceError(code: .duplicateBundleID, source: source, bundleID: bundleID) }
static func duplicateVersion(_ version: String, for app: StoreApp, source: Source) -> SourceError { SourceError(code: .duplicateVersion, source: source, app: app, version: version) }
static func blocked(_ source: Source) -> SourceError { SourceError(code: .blocked, source: source) }
static func blocked(_ source: Source, bundleIDs: [String]?, existingSource: Source?) -> SourceError { SourceError(code: .blocked, source: source, existingSource: existingSource, bundleIDs: bundleIDs) }
static func changedID(_ identifier: String, previousID: String, source: Source) -> SourceError { SourceError(code: .changedID, source: source, sourceID: identifier, previousSourceID: previousID) }
static func duplicate(_ source: Source, previousSourceName: String?) -> SourceError { SourceError(code: .duplicate, source: source, previousSourceName: previousSourceName) }
static func duplicate(_ source: Source, existingSource: Source?) -> SourceError { SourceError(code: .duplicate, source: source, existingSource: existingSource) }
static func missingPermissionUsageDescription(for permission: any ALTAppPermission, app: StoreApp, source: Source) -> SourceError {
SourceError(code: .missingPermissionUsageDescription, source: source, app: app, permission: permission)
@@ -45,12 +45,13 @@ struct SourceError: ALTLocalizedError
var errorFailure: String?
@Managed var source: Source
@Managed var app: StoreApp?
var bundleID: String?
@Managed var existingSource: Source?
var version: String?
@UserInfoValue var previousSourceName: String?
var bundleID: String?
var bundleIDs: [String]?
// Store in userInfo so they can be viewed from Error Log.
@UserInfoValue var sourceID: String?
@UserInfoValue var previousSourceID: String?
@@ -97,9 +98,9 @@ struct SourceError: ALTLocalizedError
case .duplicate:
let baseMessage = String(format: NSLocalizedString("A source with the identifier '%@' already exists", comment: ""), self.$source.identifier)
guard let previousSourceName else { return baseMessage + "." }
guard let existingSourceName = self.$existingSource.name else { return baseMessage + "." }
let failureReason = baseMessage + " (“\(previousSourceName)”)."
let failureReason = baseMessage + " (“\(existingSourceName)”)."
return failureReason
case .missingPermissionUsageDescription:
@@ -117,13 +118,75 @@ struct SourceError: ALTLocalizedError
var recoverySuggestion: String? {
switch self.code
{
case .blocked: return NSLocalizedString("For your protection, please remove the source and uninstall all apps downloaded from it.", comment: "")
case .blocked:
if self.existingSource != nil
{
// Source already added, so tell them to remove it + any installed apps.
let baseMessage = NSLocalizedString("For your protection, please remove the source and uninstall", comment: "")
if let blockedAppNames = self.blockedAppNames
{
let recoverySuggestion = baseMessage + " " + NSLocalizedString("the following apps:", comment: "") + "\n\n" + blockedAppNames.joined(separator: "\n")
return recoverySuggestion
}
else
{
let recoverySuggestion = baseMessage + " " + NSLocalizedString("all apps downloaded from it.", comment: "")
return recoverySuggestion
}
}
else
{
// Source is not already added, so no need to tell users to remove it.
// Instead, we just list all affected apps (if provided).
guard let blockedAppNames else { return nil }
let recoverySuggestion = NSLocalizedString("The following apps have been flagged:", comment: "") + "\n\n" + blockedAppNames.joined(separator: "\n")
return recoverySuggestion
}
case .changedID: return NSLocalizedString("A source cannot change its identifier once added. This source can no longer be updated.", comment: "")
case .duplicate:
let failureReason = NSLocalizedString("Please remove the existing source in order to add this one.", comment: "")
return failureReason
let recoverySuggestion = NSLocalizedString("Please remove the existing source in order to add this one.", comment: "")
return recoverySuggestion
default: return nil
}
}
}
private extension SourceError
{
var blockedAppNames: [String]? {
let blockedAppNames: [String]?
if let existingSource
{
// Blocked apps = all installed apps from this source.
blockedAppNames = self.$existingSource.perform { _ in
let storeApps = existingSource.apps.lazy.filter { $0.installedApp != nil }
guard !storeApps.isEmpty else { return nil }
let appNames = storeApps.map { "\($0.name) (\($0.bundleIdentifier))" }
return Array(appNames)
}
}
else if let bundleIDs
{
// Blocked apps = explicitly listed bundleIDs in blocked source JSON entry.
blockedAppNames = self.$source.perform { source in
bundleIDs.compactMap { (bundleID) in
guard let storeApp = source._apps.lazy.compactMap({ $0 as? StoreApp }).first(where: { $0.bundleIdentifier == bundleID }) else { return nil }
return "\(storeApp.name) (\(storeApp.bundleIdentifier))"
}
}
}
else
{
blockedAppNames = nil
}
let sortedNames = blockedAppNames?.sorted { $0.localizedCompare($1) == .orderedAscending }
return sortedNames
}
}