Refactors DatabaseManager into NSPersistentStore subclass

This commit is contained in:
Riley Testut 2016-08-09 19:48:21 -05:00
parent 0d562da309
commit da7705aaff
12 changed files with 133 additions and 288 deletions

View File

@ -64,7 +64,7 @@ class GameCollectionViewDataSource: NSObject
fetchRequest.sortDescriptors = [NSSortDescriptor(key: Game.Attributes.type.rawValue, ascending: true), NSSortDescriptor(key: Game.Attributes.name.rawValue, ascending: true)]
self.fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: DatabaseManager.sharedManager.managedObjectContext, sectionNameKeyPath: Game.Attributes.type.rawValue, cacheName: nil)
self.fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: DatabaseManager.shared.viewContext, sectionNameKeyPath: Game.Attributes.type.rawValue, cacheName: nil)
self.fetchedResultsController.delegate = previousDelegate
self.update()

View File

@ -6,114 +6,54 @@
// Copyright © 2015 Riley Testut. All rights reserved.
//
import Foundation
import CoreData
// Workspace
import Roxas
import DeltaCore
// Pods
import FileMD5Hash
class DatabaseManager
final class DatabaseManager: NSPersistentContainer
{
static let sharedManager = DatabaseManager()
let managedObjectContext: NSManagedObjectContext
private let privateManagedObjectContext: NSManagedObjectContext
private let validationManagedObjectContext: NSManagedObjectContext
// MARK: - Initialization -
/// Initialization
static let shared = DatabaseManager()
private init()
{
let modelURL = Bundle.main.url(forResource: "Model", withExtension: "momd")
let managedObjectModel = NSManagedObjectModel(contentsOf: modelURL!)
let persistentStoreCoordinator = NSPersistentStoreCoordinator(managedObjectModel: managedObjectModel!)
guard
let modelURL = Bundle(for: DatabaseManager.self).url(forResource: "Model", withExtension: "momd"),
let managedObjectModel = NSManagedObjectModel(contentsOf: modelURL)
else { fatalError("Core Data model cannot be found. Aborting.") }
self.privateManagedObjectContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
self.privateManagedObjectContext.persistentStoreCoordinator = persistentStoreCoordinator
self.privateManagedObjectContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
super.init(name: "Delta", managedObjectModel: managedObjectModel)
self.managedObjectContext = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
self.managedObjectContext.parent = self.privateManagedObjectContext
self.managedObjectContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
self.validationManagedObjectContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
self.validationManagedObjectContext.parent = self.managedObjectContext
self.validationManagedObjectContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
NotificationCenter.default.addObserver(self, selector: #selector(DatabaseManager.managedObjectContextWillSave(_:)), name: NSNotification.Name.NSManagedObjectContextWillSave, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(DatabaseManager.managedObjectContextDidSave(_:)), name: NSNotification.Name.NSManagedObjectContextDidSave, object: nil)
self.viewContext.automaticallyMergesChangesFromParent = true
}
func startWithCompletion(_ completionBlock: ((performingMigration: Bool) -> Void)?)
{
DispatchQueue.global(qos: .userInitiated).async {
let storeURL = DatabaseManager.databaseDirectoryURL.appendingPathComponent("Delta.sqlite")
}
let options = [NSMigratePersistentStoresAutomaticallyOption: true, NSInferMappingModelAutomaticallyOption: true]
var performingMigration = false
if
let sourceMetadata = try? NSPersistentStoreCoordinator.metadataForPersistentStore(ofType: NSSQLiteStoreType, at: storeURL, options: options),
let managedObjectModel = self.privateManagedObjectContext.persistentStoreCoordinator?.managedObjectModel
{
performingMigration = !managedObjectModel.isConfiguration(withName: nil, compatibleWithStoreMetadata: sourceMetadata)
}
do
{
try self.privateManagedObjectContext.persistentStoreCoordinator?.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: storeURL, options: options)
}
catch let error as NSError
{
if error.code == NSMigrationMissingSourceModelError
{
print("Migration failed. Try deleting \(storeURL)")
}
else
{
print(error)
}
abort()
}
if let completionBlock = completionBlock
{
completionBlock(performingMigration: performingMigration)
}
}
}
// MARK: - Importing -
/// Importing
func importGamesAtURLs(_ URLs: [URL], withCompletion completion: (([String]) -> Void)?)
//MARK: - Importing -
/// Importing
extension DatabaseManager
{
func importGames(at urls: [URL], completion: (([String]) -> Void)?)
{
let managedObjectContext = self.backgroundManagedObjectContext()
managedObjectContext.perform() {
self.performBackgroundTask { (context) in
var identifiers: [String] = []
for URL in URLs
for url in urls
{
let identifier = FileHash.sha1HashOfFile(atPath: URL.path) as String
let identifier = FileHash.sha1HashOfFile(atPath: url.path) as String
let filename = identifier + "." + URL.pathExtension
let filename = identifier + "." + url.pathExtension
let game = Game.insertIntoManagedObjectContext(managedObjectContext)
game.name = URL.deletingPathExtension().lastPathComponent ?? NSLocalizedString("Game", comment: "")
let game = Game.insertIntoManagedObjectContext(context)
game.name = url.deletingPathExtension().lastPathComponent ?? NSLocalizedString("Game", comment: "")
game.identifier = identifier
game.filename = filename
let gameCollection = GameCollection.gameSystemCollectionForPathExtension(URL.pathExtension, inManagedObjectContext: managedObjectContext)
let gameCollection = GameCollection.gameSystemCollectionForPathExtension(url.pathExtension, inManagedObjectContext: context)
game.type = GameType(rawValue: gameCollection.identifier)
game.gameCollections.insert(gameCollection)
@ -123,11 +63,11 @@ class DatabaseManager
if FileManager.default.fileExists(atPath: destinationURL.path)
{
try FileManager.default.removeItem(at: URL)
try FileManager.default.removeItem(at: url)
}
else
{
try FileManager.default.moveItem(at: URL, to: destinationURL)
try FileManager.default.moveItem(at: url, to: destinationURL)
}
identifiers.append(game.identifier)
@ -141,9 +81,9 @@ class DatabaseManager
do
{
try managedObjectContext.save()
try context.save()
}
catch let error as NSError
catch
{
print("Failed to save import context:", error)
@ -154,27 +94,16 @@ class DatabaseManager
{
completion(identifiers)
}
}
}
// MARK: - Background Contexts -
/// Background Contexts
func backgroundManagedObjectContext() -> NSManagedObjectContext
{
let managedObjectContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
managedObjectContext.parent = self.validationManagedObjectContext
managedObjectContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
return managedObjectContext
}
}
//MARK: - File URLs -
/// File URLs
extension DatabaseManager
{
class var databaseDirectoryURL: URL
override class func defaultDirectoryURL() -> URL
{
let documentsDirectoryURL: URL
@ -188,23 +117,23 @@ extension DatabaseManager
}
let databaseDirectoryURL = documentsDirectoryURL.appendingPathComponent("Database")
self.createDirectoryAtURLIfNeeded(databaseDirectoryURL)
self.createDirectory(at: databaseDirectoryURL)
return databaseDirectoryURL
}
class var gamesDirectoryURL: URL
{
let gamesDirectoryURL = DatabaseManager.databaseDirectoryURL.appendingPathComponent("Games")
self.createDirectoryAtURLIfNeeded(gamesDirectoryURL)
let gamesDirectoryURL = DatabaseManager.defaultDirectoryURL().appendingPathComponent("Games")
self.createDirectory(at: gamesDirectoryURL)
return gamesDirectoryURL
}
class var saveStatesDirectoryURL: URL
{
let saveStatesDirectoryURL = DatabaseManager.databaseDirectoryURL.appendingPathComponent("Save States")
self.createDirectoryAtURLIfNeeded(saveStatesDirectoryURL)
let saveStatesDirectoryURL = DatabaseManager.defaultDirectoryURL().appendingPathComponent("Save States")
self.createDirectory(at: saveStatesDirectoryURL)
return saveStatesDirectoryURL
}
@ -212,141 +141,20 @@ extension DatabaseManager
class func saveStatesDirectoryURLForGame(_ game: Game) -> URL
{
let gameDirectoryURL = DatabaseManager.saveStatesDirectoryURL.appendingPathComponent(game.identifier)
self.createDirectoryAtURLIfNeeded(gameDirectoryURL)
self.createDirectory(at: gameDirectoryURL)
return gameDirectoryURL
}
}
//MARK: - Private -
private extension DatabaseManager
{
// MARK: - Saving -
func save()
{
let backgroundTaskIdentifier = RSTBeginBackgroundTask("Save Database Task")
self.validationManagedObjectContext.performAndWait {
do
{
try self.validationManagedObjectContext.save()
}
catch let error as NSError
{
print("Failed to save validation context:", error)
}
// Update main managed object context
self.managedObjectContext.performAndWait() {
do
{
try self.managedObjectContext.save()
}
catch let error as NSError
{
print("Failed to save main context:", error)
}
// Save to disk
self.privateManagedObjectContext.perform() {
do
{
try self.privateManagedObjectContext.save()
}
catch let error as NSError
{
print("Failed to save private context to disk:", error)
}
RSTEndBackgroundTask(backgroundTaskIdentifier)
}
}
}
}
// MARK: - Validation -
func validateManagedObjectContextSave(_ managedObjectContext: NSManagedObjectContext)
{
// Remove deleted files from disk
for object in managedObjectContext.deletedObjects
{
var fileURLs = Set<URL>()
let temporaryObject = self.validationManagedObjectContext.object(with: object.objectID)
switch temporaryObject
{
case let game as Game:
fileURLs.insert(game.fileURL as URL)
case let saveState as SaveState:
fileURLs.insert(saveState.fileURL as URL)
fileURLs.insert(saveState.imageFileURL as URL)
default: break
}
for URL in fileURLs
{
do
{
try FileManager.default.removeItem(at: URL)
}
catch let error as NSError
{
print(error)
}
}
}
// Remove empty collections
let collections = GameCollection.instancesWithPredicate(NSPredicate(format: "%K.@count == 0", GameCollection.Attributes.games.rawValue), inManagedObjectContext: self.validationManagedObjectContext, type: GameCollection.self)
for collection in collections
{
self.validationManagedObjectContext.delete(collection)
}
}
// MARK: - Notifications -
@objc func managedObjectContextWillSave(_ notification: Notification)
{
guard
let managedObjectContext = notification.object as? NSManagedObjectContext,
managedObjectContext.parent == self.validationManagedObjectContext
else { return }
self.validationManagedObjectContext.performAndWait {
self.validateManagedObjectContextSave(managedObjectContext)
}
}
@objc func managedObjectContextDidSave(_ notification: Notification)
{
guard
let managedObjectContext = notification.object as? NSManagedObjectContext,
managedObjectContext.parent == self.validationManagedObjectContext
else { return }
self.save()
}
// MARK: - File Management -
class func createDirectoryAtURLIfNeeded(_ URL: Foundation.URL)
class func createDirectory(at url: URL)
{
do
{
try FileManager.default.createDirectory(at: URL, withIntermediateDirectories: true, attributes: nil)
try FileManager.default.createDirectory(at: url, withIntermediateDirectories: true, attributes: nil)
}
catch
{

View File

@ -62,6 +62,39 @@ class Game: NSManagedObject, GameProtocol
}
}
extension Game
{
override public func prepareForDeletion()
{
super.prepareForDeletion()
guard FileManager.default.fileExists(atPath: self.fileURL.path) else { return }
do
{
try FileManager.default.removeItem(at: self.fileURL)
}
catch
{
print(error)
}
if let managedObjectContext = self.managedObjectContext
{
for collection in self.gameCollections where collection.games.count == 1
{
// Once this game is deleted, collection will have 0 games, so we should delete it
managedObjectContext.delete(collection)
}
if managedObjectContext.hasChanges
{
managedObjectContext.saveWithErrorLogging()
}
}
}
}
extension Game
{
class func supportedTypeIdentifiers() -> Set<String>

View File

@ -70,6 +70,25 @@ class SaveState: NSManagedObject, SaveStateProtocol
}
}
extension SaveState
{
override public func prepareForDeletion()
{
super.prepareForDeletion()
guard FileManager.default.fileExists(atPath: self.fileURL.path) else { return }
do
{
try FileManager.default.removeItem(at: self.fileURL)
}
catch
{
print(error)
}
}
}
extension SaveState
{
@NSManaged private var primitiveFilename: String

View File

@ -53,15 +53,14 @@ class GamePickerController: NSObject
let importAction = UIAlertAction(title: NSLocalizedString("Import", comment: ""), style: .default) { action in
let documentsDirectoryURL = DatabaseManager.databaseDirectoryURL.deletingLastPathComponent()
let documentsDirectoryURL = DatabaseManager.defaultDirectoryURL().deletingLastPathComponent()
do
{
let contents = try FileManager.default.contentsOfDirectory(at: documentsDirectoryURL, includingPropertiesForKeys: nil, options: .skipsHiddenFiles)
let managedObjectContext = DatabaseManager.sharedManager.backgroundManagedObjectContext()
managedObjectContext.perform() {
let gameURLs = contents.filter({ GameCollection.gameSystemCollectionForPathExtension($0.pathExtension, inManagedObjectContext: managedObjectContext).identifier != GameType.delta.rawValue })
DatabaseManager.shared.performBackgroundTask { (context) in
let gameURLs = contents.filter({ GameCollection.gameSystemCollectionForPathExtension($0.pathExtension, inManagedObjectContext: context).identifier != GameType.delta.rawValue })
self.importGamesAtURLs(gameURLs)
}
@ -87,12 +86,12 @@ class GamePickerController: NSObject
private func importGamesAtURLs(_ URLs: [URL])
{
DatabaseManager.sharedManager.importGamesAtURLs(URLs) { identifiers in
DatabaseManager.shared.importGames(at: URLs) { identifiers in
DatabaseManager.sharedManager.managedObjectContext.perform() {
DatabaseManager.shared.viewContext.perform() {
let predicate = NSPredicate(format: "%K IN (%@)", Game.Attributes.identifier.rawValue, identifiers)
let games = Game.instancesWithPredicate(predicate, inManagedObjectContext: DatabaseManager.sharedManager.managedObjectContext, type: Game.self)
let games = Game.instancesWithPredicate(predicate, inManagedObjectContext: DatabaseManager.shared.viewContext, type: Game.self)
self.delegate?.gamePickerController(self, didImportGames: games)

View File

@ -35,7 +35,6 @@
BF65E8631CEE5C6A00CD3247 /* Cheat.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF65E8621CEE5C6A00CD3247 /* Cheat.swift */; };
BF70798C1B6B464B0019077C /* ZipZap.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BF70798B1B6B464B0019077C /* ZipZap.framework */; };
BF70798D1B6B464B0019077C /* ZipZap.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = BF70798B1B6B464B0019077C /* ZipZap.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
BF762E9E1BC19D31002C8866 /* DatabaseManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF762E9D1BC19D31002C8866 /* DatabaseManager.swift */; };
BF762EAB1BC1B076002C8866 /* NSManagedObject+Conveniences.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF762EAA1BC1B076002C8866 /* NSManagedObject+Conveniences.swift */; };
BF797A2D1C2D339F00F1A000 /* UILabel+FontSize.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF797A2C1C2D339F00F1A000 /* UILabel+FontSize.swift */; };
BF7AE8081C2E858400B1B5BC /* PauseMenuViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF7AE8041C2E858400B1B5BC /* PauseMenuViewController.swift */; };
@ -51,6 +50,7 @@
BFAA1FED1B8AA4FA00495943 /* Settings.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFAA1FEC1B8AA4FA00495943 /* Settings.swift */; };
BFAA1FF41B8AD7F900495943 /* ControllersSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFAA1FF31B8AD7F900495943 /* ControllersSettingsViewController.swift */; };
BFB141181BE46934004FBF46 /* GameCollectionViewDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFB141171BE46934004FBF46 /* GameCollectionViewDataSource.swift */; };
BFBAA86A1D5A483900A29C1B /* DatabaseManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFBAA8691D5A483900A29C1B /* DatabaseManager.swift */; };
BFC2731A1BE6152200D22B05 /* GameCollection.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC273171BE6152200D22B05 /* GameCollection.swift */; };
BFC9B7391CEFCD34008629BB /* CheatsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC9B7381CEFCD34008629BB /* CheatsViewController.swift */; };
BFCEA67E1D56FF640061A534 /* UIViewControllerContextTransitioning+Conveniences.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFCEA67D1D56FF640061A534 /* UIViewControllerContextTransitioning+Conveniences.swift */; };
@ -126,7 +126,6 @@
BF65E8621CEE5C6A00CD3247 /* Cheat.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Cheat.swift; sourceTree = "<group>"; };
BF6BB2451BB73FE800CCF94A /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
BF70798B1B6B464B0019077C /* ZipZap.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = ZipZap.framework; sourceTree = BUILT_PRODUCTS_DIR; };
BF762E9D1BC19D31002C8866 /* DatabaseManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DatabaseManager.swift; sourceTree = "<group>"; };
BF762EAA1BC1B076002C8866 /* NSManagedObject+Conveniences.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSManagedObject+Conveniences.swift"; sourceTree = "<group>"; };
BF797A2C1C2D339F00F1A000 /* UILabel+FontSize.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UILabel+FontSize.swift"; sourceTree = "<group>"; };
BF7AE8041C2E858400B1B5BC /* PauseMenuViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = PauseMenuViewController.swift; path = "Pause Menu/PauseMenuViewController.swift"; sourceTree = "<group>"; };
@ -138,6 +137,7 @@
BFAA1FEC1B8AA4FA00495943 /* Settings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Settings.swift; sourceTree = "<group>"; };
BFAA1FF31B8AD7F900495943 /* ControllersSettingsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ControllersSettingsViewController.swift; sourceTree = "<group>"; };
BFB141171BE46934004FBF46 /* GameCollectionViewDataSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = GameCollectionViewDataSource.swift; path = "Collection View/GameCollectionViewDataSource.swift"; sourceTree = "<group>"; };
BFBAA8691D5A483900A29C1B /* DatabaseManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DatabaseManager.swift; sourceTree = "<group>"; };
BFC134E01AAD82460087AD7B /* SNESDeltaCore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = SNESDeltaCore.framework; sourceTree = BUILT_PRODUCTS_DIR; };
BFC273171BE6152200D22B05 /* GameCollection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GameCollection.swift; sourceTree = "<group>"; };
BFC9B7381CEFCD34008629BB /* CheatsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = CheatsViewController.swift; path = "Pause Menu/Cheats/CheatsViewController.swift"; sourceTree = "<group>"; };
@ -229,7 +229,7 @@
BF4566E41BC0902E007BFA1A /* Database */ = {
isa = PBXGroup;
children = (
BF762E9D1BC19D31002C8866 /* DatabaseManager.swift */,
BFBAA8691D5A483900A29C1B /* DatabaseManager.swift */,
BF4566E51BC09033007BFA1A /* Model */,
);
path = Database;
@ -583,6 +583,7 @@
BFAA1FF41B8AD7F900495943 /* ControllersSettingsViewController.swift in Sources */,
BF353FF91C5D870B00C1184C /* PauseItem.swift in Sources */,
BF27CC971BCC890700A20D89 /* GamesCollectionViewController.swift in Sources */,
BFBAA86A1D5A483900A29C1B /* DatabaseManager.swift in Sources */,
BFFA71DD1AAC406100EE9DD1 /* AppDelegate.swift in Sources */,
BF7AE81E1C2E984300B1B5BC /* GridCollectionViewCell.swift in Sources */,
BF34FA111CF1899D006624C7 /* CheatTextView.swift in Sources */,
@ -600,7 +601,6 @@
BF02BD001D361BD1000892F2 /* NSFetchedResultsController+Conveniences.m in Sources */,
BFFC464C1D5998D600AF2CC6 /* CheatTableViewCell.swift in Sources */,
BF7AE80A1C2E8C7600B1B5BC /* UIColor+Delta.swift in Sources */,
BF762E9E1BC19D31002C8866 /* DatabaseManager.swift in Sources */,
BF090CF41B490D8300DCAB45 /* UIDevice+Vibration.m in Sources */,
BF797A2D1C2D339F00F1A000 /* UILabel+FontSize.swift in Sources */,
BFD097211D3A01B8005A44C2 /* SaveStatesViewController.swift in Sources */,

View File

@ -26,14 +26,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate
// Database
let semaphore = DispatchSemaphore(value: 0)
DatabaseManager.sharedManager.startWithCompletion { performingMigration in
semaphore.signal()
DatabaseManager.shared.loadPersistentStores { (description, error) in
}
semaphore.wait()
// Controllers
ExternalControllerManager.shared.startMonitoringExternalControllers()

View File

@ -453,7 +453,7 @@ extension GameViewController: CheatsViewControllerDelegate
self.pauseEmulation()
}
let backgroundContext = DatabaseManager.sharedManager.backgroundManagedObjectContext()
let backgroundContext = DatabaseManager.shared.newBackgroundContext()
backgroundContext.performAndWait {
let predicate = NSPredicate(format: "%K == %@", Cheat.Attributes.game.rawValue, game)

View File

@ -45,7 +45,7 @@ class GamesViewController: UIViewController
let fetchRequest = GameCollection.rst_fetchRequest()
fetchRequest.sortDescriptors = [NSSortDescriptor(key: GameCollection.Attributes.index.rawValue, ascending: true)]
self.fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: DatabaseManager.sharedManager.managedObjectContext, sectionNameKeyPath: nil, cacheName: nil)
self.fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: DatabaseManager.shared.viewContext, sectionNameKeyPath: nil, cacheName: nil)
super.init(coder: aDecoder)

View File

@ -97,7 +97,7 @@ private extension CheatsViewController
fetchRequest.predicate = NSPredicate(format: "%K == %@", Cheat.Attributes.game.rawValue, self.game)
fetchRequest.sortDescriptors = [NSSortDescriptor(key: Cheat.Attributes.name.rawValue, ascending: true)]
self.fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: DatabaseManager.sharedManager.managedObjectContext, sectionNameKeyPath: nil, cacheName: nil)
self.fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: DatabaseManager.shared.viewContext, sectionNameKeyPath: nil, cacheName: nil)
self.fetchedResultsController.delegate = self
}
@ -130,11 +130,10 @@ private extension CheatsViewController
{
self.delegate?.cheatsViewController(self, deactivateCheat: cheat)
let backgroundContext = DatabaseManager.sharedManager.backgroundManagedObjectContext()
backgroundContext.perform {
let temporaryCheat = backgroundContext.object(with: cheat.objectID)
backgroundContext.delete(temporaryCheat)
backgroundContext.saveWithErrorLogging()
DatabaseManager.shared.performBackgroundTask { (context) in
let temporaryCheat = context.object(with: cheat.objectID)
context.delete(temporaryCheat)
context.saveWithErrorLogging()
}
}
}
@ -193,7 +192,7 @@ extension CheatsViewController
{
let cheat = self.fetchedResultsController.object(at: indexPath) as! Cheat
let backgroundContext = DatabaseManager.sharedManager.backgroundManagedObjectContext()
let backgroundContext = DatabaseManager.shared.newBackgroundContext()
backgroundContext.performAndWait {
let temporaryCheat = backgroundContext.object(with: cheat.objectID) as! Cheat
temporaryCheat.enabled = !temporaryCheat.enabled

View File

@ -49,7 +49,7 @@ class EditCheatViewController: UITableViewController
}
private var mutableCheat: Cheat!
private var managedObjectContext = DatabaseManager.sharedManager.backgroundManagedObjectContext()
private var managedObjectContext = DatabaseManager.shared.newBackgroundContext()
@IBOutlet private var nameTextField: UITextField!
@IBOutlet private var typeSegmentedControl: UISegmentedControl!
@ -76,11 +76,10 @@ class EditCheatViewController: UITableViewController
let deleteAction = UIPreviewAction(title: NSLocalizedString("Delete", comment: ""), style: .destructive) { [unowned self] (action, viewController) in
self.delegate?.editCheatViewController(self, deactivateCheat: cheat)
let backgroundContext = DatabaseManager.sharedManager.backgroundManagedObjectContext()
backgroundContext.perform {
let temporaryCheat = backgroundContext.object(with: cheat.objectID)
backgroundContext.delete(temporaryCheat)
backgroundContext.saveWithErrorLogging()
DatabaseManager.shared.performBackgroundTask { (context) in
let temporaryCheat = context.object(with: cheat.objectID)
context.delete(temporaryCheat)
context.saveWithErrorLogging()
}
}

View File

@ -168,7 +168,7 @@ private extension SaveStatesViewController
fetchRequest.predicate = NSPredicate(format: "%K == %@", SaveState.Attributes.game.rawValue, self.game)
fetchRequest.sortDescriptors = [NSSortDescriptor(key: SaveState.Attributes.type.rawValue, ascending: true), NSSortDescriptor(key: SaveState.Attributes.creationDate.rawValue, ascending: true)]
self.fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: DatabaseManager.sharedManager.managedObjectContext, sectionNameKeyPath: SaveState.Attributes.type.rawValue, cacheName: nil)
self.fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: DatabaseManager.shared.viewContext, sectionNameKeyPath: SaveState.Attributes.type.rawValue, cacheName: nil)
self.fetchedResultsController.delegate = self
}
@ -275,7 +275,7 @@ private extension SaveStatesViewController
{
var saveState: SaveState!
let backgroundContext = DatabaseManager.sharedManager.backgroundManagedObjectContext()
let backgroundContext = DatabaseManager.shared.newBackgroundContext()
backgroundContext.performAndWait {
let game = backgroundContext.object(with: self.game.objectID) as! Game
@ -324,14 +324,12 @@ private extension SaveStatesViewController
let confirmationAlertController = UIAlertController(title: NSLocalizedString("Delete Save State?", comment: ""), message: NSLocalizedString("Are you sure you want to delete this save state? This cannot be undone.", comment: ""), preferredStyle: .alert)
confirmationAlertController.addAction(UIAlertAction(title: NSLocalizedString("Delete", comment: ""), style: .default, handler: { action in
let backgroundContext = DatabaseManager.sharedManager.backgroundManagedObjectContext()
backgroundContext.perform {
let temporarySaveState = backgroundContext.object(with: saveState.objectID)
backgroundContext.delete(temporarySaveState)
backgroundContext.saveWithErrorLogging()
DatabaseManager.shared.performBackgroundTask { (context) in
let temporarySaveState = context.object(with: saveState.objectID)
context.delete(temporarySaveState)
context.saveWithErrorLogging()
}
}))
confirmationAlertController.addAction(UIAlertAction(title: NSLocalizedString("Cancel", comment: ""), style: .cancel, handler: nil))
@ -370,13 +368,11 @@ private extension SaveStatesViewController
text = nil
}
let backgroundContext = DatabaseManager.sharedManager.backgroundManagedObjectContext()
backgroundContext.perform {
let saveState = backgroundContext.object(with: selectedSaveState.objectID) as! SaveState
DatabaseManager.shared.performBackgroundTask { (context) in
let saveState = context.object(with: selectedSaveState.objectID) as! SaveState
saveState.name = text
backgroundContext.saveWithErrorLogging()
context.saveWithErrorLogging()
}
self.selectedSaveState = nil
@ -388,14 +384,12 @@ private extension SaveStatesViewController
alertController.addAction(UIAlertAction(title: NSLocalizedString("Cancel", comment: ""), style: .cancel, handler: nil))
alertController.addAction(UIAlertAction(title: NSLocalizedString("Change", comment: ""), style: .default, handler: { (action) in
let backgroundContext = DatabaseManager.sharedManager.backgroundManagedObjectContext()
backgroundContext.perform {
let game = backgroundContext.object(with: self.game.objectID) as! Game
DatabaseManager.shared.performBackgroundTask { (context) in
let game = context.object(with: self.game.objectID) as! Game
if let saveState = saveState
{
let previewSaveState = backgroundContext.object(with: saveState.objectID) as! SaveState
let previewSaveState = context.object(with: saveState.objectID) as! SaveState
game.previewSaveState = previewSaveState
}
else
@ -403,9 +397,8 @@ private extension SaveStatesViewController
game.previewSaveState = nil
}
backgroundContext.saveWithErrorLogging()
context.saveWithErrorLogging()
}
}))
self.present(alertController, animated: true, completion: nil)
@ -413,7 +406,7 @@ private extension SaveStatesViewController
func lockSaveState(_ saveState: SaveState)
{
let backgroundContext = DatabaseManager.sharedManager.backgroundManagedObjectContext()
let backgroundContext = DatabaseManager.shared.newBackgroundContext()
backgroundContext.performAndWait() {
let temporarySaveState = backgroundContext.object(with: saveState.objectID) as! SaveState
temporarySaveState.type = .locked
@ -423,7 +416,7 @@ private extension SaveStatesViewController
func unlockSaveState(_ saveState: SaveState)
{
let backgroundContext = DatabaseManager.sharedManager.backgroundManagedObjectContext()
let backgroundContext = DatabaseManager.shared.newBackgroundContext()
backgroundContext.performAndWait() {
let temporarySaveState = backgroundContext.object(with: saveState.objectID) as! SaveState
temporarySaveState.type = .general
@ -692,7 +685,7 @@ extension SaveStatesViewController
{
case .auto: break
case .general:
let backgroundContext = DatabaseManager.sharedManager.backgroundManagedObjectContext()
let backgroundContext = DatabaseManager.shared.newBackgroundContext()
backgroundContext.performAndWait() {
let temporarySaveState = backgroundContext.object(with: saveState.objectID) as! SaveState
self.updateSaveState(temporarySaveState)