Allows explicitly downloading corrupted record versions from RecordVersionsViewController

This commit is contained in:
Riley Testut 2023-08-10 19:21:51 -05:00
parent be047b28a6
commit 061f5abd3e
2 changed files with 67 additions and 23 deletions

View File

@ -23,6 +23,12 @@ public class GameSave: _GameSave
} }
} }
extension GameSave
{
// Hacky, but YOLO I'm under time crunch.
public static var ignoredCorruptedIDs = Set<String>()
}
extension GameSave: Syncable extension GameSave: Syncable
{ {
public static var syncablePrimaryKey: AnyKeyPath { public static var syncablePrimaryKey: AnyKeyPath {
@ -71,27 +77,46 @@ extension GameSave: Syncable
public func awakeFromSync(_ record: AnyRecord) throws public func awakeFromSync(_ record: AnyRecord) throws
{ {
guard let game = self.game else { throw SyncValidationError.incorrectGame(nil) } do
if game.identifier != self.identifier
{ {
let fetchRequest = GameSave.fetchRequest() guard let game = self.game else { throw SyncValidationError.incorrectGame(nil) }
fetchRequest.predicate = NSPredicate(format: "%K == %@", #keyPath(GameSave.identifier), game.identifier)
if let misplacedGameSave = try self.managedObjectContext?.fetch(fetchRequest).first, misplacedGameSave.game == nil if game.identifier != self.identifier
{ {
// Relink game with its correct gameSave, in case we accidentally misplaced it. let fetchRequest = GameSave.fetchRequest()
// Otherwise, corrupted records might displace already-downloaded GameSaves fetchRequest.predicate = NSPredicate(format: "%K == %@", #keyPath(GameSave.identifier), game.identifier)
// due to automatic Core Data relationship propagation, despite us throwing error.
game.gameSave = misplacedGameSave if let misplacedGameSave = try self.managedObjectContext?.fetch(fetchRequest).first, misplacedGameSave.game == nil
{
// Relink game with its correct gameSave, in case we accidentally misplaced it.
// Otherwise, corrupted records might displace already-downloaded GameSaves
// due to automatic Core Data relationship propagation, despite us throwing error.
game.gameSave = misplacedGameSave
}
else
{
// Either there is no misplacedGameSave, or there is but it's linked to another game somehow.
game.gameSave = nil
}
throw SyncValidationError.incorrectGame(game.name)
}
}
catch let error as SyncValidationError
{
guard GameSave.ignoredCorruptedIDs.contains(self.identifier) else { throw error }
let fetchRequest = Game.fetchRequest()
fetchRequest.predicate = NSPredicate(format: "%K == %@", #keyPath(Game.identifier), self.identifier)
if let correctGame = try self.managedObjectContext?.fetch(fetchRequest).first
{
self.game = correctGame
} }
else else
{ {
// Either there is no misplacedGameSave, or there is but it's linked to another game somehow. throw ValidationError.nilRelationshipObjects(keys: [#keyPath(GameSave.game)])
game.gameSave = nil
} }
throw SyncValidationError.incorrectGame(game.name)
} }
} }
} }

View File

@ -258,6 +258,8 @@ private extension RecordVersionsViewController
{ {
DispatchQueue.main.async { DispatchQueue.main.async {
GameSave.ignoredCorruptedIDs.remove(self.record.recordID.identifier)
CATransaction.begin() CATransaction.begin()
CATransaction.setCompletionBlock { CATransaction.setCompletionBlock {
@ -287,17 +289,34 @@ private extension RecordVersionsViewController
} }
catch catch
{ {
let title: String switch error
switch self.mode
{ {
case .restoreVersion: title = NSLocalizedString("Failed to Restore Version", comment: "") case RecordError.other(let record, let error as SyncValidationError):
case .resolveConflict: title = NSLocalizedString("Failed to Resolve Conflict", comment: "") // Only allow restoring corrupted records with incorrect games.
} guard case .incorrectGame = error else { fallthrough }
let alertController = UIAlertController(title: title, message: error.localizedDescription, preferredStyle: .alert) let message = NSLocalizedString("Would you like to download this version anyway?", comment: "")
alertController.addAction(.ok) let alertController = UIAlertController(title: NSLocalizedString("Record Version Corrupted", comment: ""), message: message, preferredStyle: .alert)
self.present(alertController, animated: true, completion: nil) alertController.addAction(.cancel)
alertController.addAction(UIAlertAction(title: NSLocalizedString("Download Anyway", comment: ""), style: .destructive) { _ in
GameSave.ignoredCorruptedIDs.insert(record.recordID.identifier)
self.restoreVersion()
})
self.present(alertController, animated: true, completion: nil)
default:
let title: String
switch self.mode
{
case .restoreVersion: title = NSLocalizedString("Failed to Restore Version", comment: "")
case .resolveConflict: title = NSLocalizedString("Failed to Resolve Conflict", comment: "")
}
let alertController = UIAlertController(title: title, message: error.localizedDescription, preferredStyle: .alert)
alertController.addAction(.ok)
self.present(alertController, animated: true, completion: nil)
}
} }
CATransaction.commit() CATransaction.commit()