Verifies source’s identifier doesn’t match existing sources when adding

This commit is contained in:
Riley Testut
2023-05-11 18:51:09 -05:00
committed by Magesh K
parent bd3beb5983
commit f884d72a8b
3 changed files with 43 additions and 6 deletions

View File

@@ -365,8 +365,12 @@ extension AppManager
let action = await UIAlertAction(title: NSLocalizedString("Add Source", comment: ""), style: .default) let action = await UIAlertAction(title: NSLocalizedString("Add Source", comment: ""), style: .default)
try await presentingViewController.presentConfirmationAlert(title: title, message: message, primaryAction: action) try await presentingViewController.presentConfirmationAlert(title: title, message: message, primaryAction: action)
// Wait for fetch to finish before saving context. // Wait for fetch to finish before saving context to make
_ = try await fetchedSource // sure there isn't already a source with this identifier.
let sourceExists = try await fetchedSource.isAdded
// This is just a sanity check, so pass nil for previousSourceName to keep code simple.
guard !sourceExists else { throw SourceError.duplicate(source, previousSourceName: nil) }
try await context.performAsync { try await context.performAsync {
try context.save() try context.save()

View File

@@ -23,6 +23,7 @@ extension SourceError
case duplicateVersion case duplicateVersion
case changedID case changedID
case duplicate
} }
static func unsupported(_ source: Source) -> SourceError { SourceError(code: .unsupported, source: source) } static func unsupported(_ source: Source) -> SourceError { SourceError(code: .unsupported, source: source) }
@@ -30,6 +31,7 @@ extension SourceError
static func duplicateVersion(_ version: String, for app: StoreApp, source: Source) -> SourceError { SourceError(code: .duplicateVersion, source: source, app: app, version: version) } static func duplicateVersion(_ version: String, for app: StoreApp, source: Source) -> SourceError { SourceError(code: .duplicateVersion, source: source, app: app, version: version) }
static func changedID(_ identifier: String, previousID: String, source: Source) -> SourceError { SourceError(code: .changedID, source: source, sourceID: identifier, previousSourceID: previousID) } 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) }
} }
struct SourceError: ALTLocalizedError struct SourceError: ALTLocalizedError
@@ -43,6 +45,8 @@ struct SourceError: ALTLocalizedError
var bundleID: String? var bundleID: String?
var version: String? var version: String?
@UserInfoValue var previousSourceName: String?
// Store in userInfo so they can be viewed from Error Log. // Store in userInfo so they can be viewed from Error Log.
@UserInfoValue var sourceID: String? @UserInfoValue var sourceID: String?
@UserInfoValue var previousSourceID: String? @UserInfoValue var previousSourceID: String?
@@ -79,6 +83,13 @@ struct SourceError: ALTLocalizedError
case .changedID: case .changedID:
let failureReason = String(format: NSLocalizedString("The identifier of the source “%@” has changed.", comment: ""), self.$source.name) let failureReason = String(format: NSLocalizedString("The identifier of the source “%@” has changed.", comment: ""), self.$source.name)
return failureReason return failureReason
case .duplicate:
let baseMessage = String(format: NSLocalizedString("A source with the identifier '%@' already exists", comment: ""), self.$source.identifier)
guard let previousSourceName else { return baseMessage + "." }
let failureReason = baseMessage + " (“\(previousSourceName)”)."
return failureReason
} }
} }
@@ -86,6 +97,10 @@ struct SourceError: ALTLocalizedError
switch self.code switch self.code
{ {
case .changedID: return NSLocalizedString("A source cannot change its identifier once added. This source can no longer be updated.", comment: "") 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
default: return nil default: return nil
} }
} }

View File

@@ -283,13 +283,31 @@ private extension SourcesViewController
AppManager.shared.fetchSource(sourceURL: url, dependencies: dependencies) { (result) in AppManager.shared.fetchSource(sourceURL: url, dependencies: dependencies) { (result) in
do do
{ {
// Use @Managed before calling perform() to keep
// strong reference to source.managedObjectContext.
@Managed var source = try result.get() @Managed var source = try result.get()
DispatchQueue.main.async { let backgroundContext = DatabaseManager.shared.persistentContainer.newBackgroundContext()
self.showSourceDetails(for: source) backgroundContext.perform {
do
{
let predicate = NSPredicate(format: "%K == %@", #keyPath(Source.identifier), $source.identifier)
if let existingSource = Source.first(satisfying: predicate, in: backgroundContext)
{
throw SourceError.duplicate(source, previousSourceName: existingSource.name)
}
DispatchQueue.main.async {
self.showSourceDetails(for: source)
}
finish(.success(()))
}
catch
{
finish(.failure(error))
}
} }
finish(.success(()))
} }
catch catch
{ {