From 39d8d1c71000963436dbbd86e60725cf767b126a Mon Sep 17 00:00:00 2001 From: duncangroenewald Date: Wed, 9 Sep 2015 19:53:34 +1000 Subject: [PATCH] - integrated CoreDataStackManager --- .../UserInterfaceState.xcuserstate | Bin 12271 -> 15510 bytes .../Sample Core Data App/AppDelegate.swift | 190 +++++++----- .../CoreDataStackManager.swift | 86 +++--- .../FileRepresentation.swift | 3 +- .../Sample Core Data App/Info.plist | 2 +- .../MasterViewController.swift | 273 +++++++++++------- 6 files changed, 324 insertions(+), 230 deletions(-) diff --git a/Sample Core Data App/Sample Core Data App.xcodeproj/project.xcworkspace/xcuserdata/duncangroenewald.xcuserdatad/UserInterfaceState.xcuserstate b/Sample Core Data App/Sample Core Data App.xcodeproj/project.xcworkspace/xcuserdata/duncangroenewald.xcuserdatad/UserInterfaceState.xcuserstate index 10309d03bab7a38b56a885c3389eda7f1cc4fdcc..841f6feaf6179abda76993c151bdad3c3c92670a 100644 GIT binary patch literal 15510 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 literal 12271 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 diff --git a/Sample Core Data App/Sample Core Data App/AppDelegate.swift b/Sample Core Data App/Sample Core Data App/AppDelegate.swift index 1ceb97f..e03d0c8 100644 --- a/Sample Core Data App/Sample Core Data App/AppDelegate.swift +++ b/Sample Core Data App/Sample Core Data App/AppDelegate.swift @@ -9,24 +9,72 @@ import UIKit import CoreData +var cdManager: CoreDataStackManager = CoreDataStackManager.sharedManager + @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate, UISplitViewControllerDelegate { var window: UIWindow? - + var splitViewController: UISplitViewController? = nil func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { - // Override point for customization after application launch. - let splitViewController = self.window!.rootViewController as! UISplitViewController - let navigationController = splitViewController.viewControllers[splitViewController.viewControllers.count-1] as! UINavigationController - navigationController.topViewController!.navigationItem.leftBarButtonItem = splitViewController.displayModeButtonItem() - splitViewController.delegate = self - - let masterNavigationController = splitViewController.viewControllers[0] as! UINavigationController - let controller = masterNavigationController.topViewController as! MasterViewController - controller.managedObjectContext = self.managedObjectContext + + splitViewController = self.window!.rootViewController as? UISplitViewController + self.splitViewController!.delegate = self + + // Set the version number in the Settings App settings page for this App + cdManager.setVersion() + + if (cdManager.checkCDModelVersion()) { + FLOG(" Upgrade is required"); + + FLOG(" Open the Update screens"); + + } else { + FLOG(" Upgrade is not required"); + FLOG(" Open the Menu screens"); + + openMainViews() + } return true } + func openMainViews() { + FLOG(" called") + //self.detailViewManager = DetailViewManager() + + //RebuildFromICloudViewController.classForCoder() + + let navigationController = self.splitViewController!.viewControllers[self.splitViewController!.viewControllers.count-1] as! UINavigationController + + navigationController.topViewController!.navigationItem.leftBarButtonItem = self.splitViewController!.displayModeButtonItem() + + let masterNavigationController = self.splitViewController!.viewControllers[0] as! UINavigationController + + let controller = masterNavigationController.topViewController as! MasterViewController + +// if let ovc = self.splitViewController!.viewControllers[1] as? UINavigationController { +// FLOG("detail view nav controller found") +// if let vc = ovc.viewControllers[0] as? OpeningViewControllerX { +// self.openingViewController = vc +// controller.openingViewController = vc +// } +// } + + return + } + +// func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { +// // Override point for customization after application launch. +// let splitViewController = self.window!.rootViewController as! UISplitViewController +// let navigationController = splitViewController.viewControllers[splitViewController.viewControllers.count-1] as! UINavigationController +// navigationController.topViewController!.navigationItem.leftBarButtonItem = splitViewController.displayModeButtonItem() +// splitViewController.delegate = self +// +// let masterNavigationController = splitViewController.viewControllers[0] as! UINavigationController +// let controller = masterNavigationController.topViewController as! MasterViewController +// // DG: controller.managedObjectContext = self.managedObjectContext +// return true +// } func applicationWillResignActive(application: UIApplication) { // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. @@ -49,7 +97,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UISplitViewControllerDele func applicationWillTerminate(application: UIApplication) { // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. // Saves changes in the application's managed object context before the application terminates. - self.saveContext() + // DG: self.saveContext() } // MARK: - Split view @@ -65,66 +113,66 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UISplitViewControllerDele } // MARK: - Core Data stack - lazy var applicationDocumentsDirectory: NSURL = { - // The directory the application uses to store the Core Data store file. This code uses a directory named "au.com.ossh.Sample_Core_Data_App" in the application's documents Application Support directory. - let urls = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask) - return urls[urls.count-1] - }() - - lazy var managedObjectModel: NSManagedObjectModel = { - // The managed object model for the application. This property is not optional. It is a fatal error for the application not to be able to find and load its model. - let modelURL = NSBundle.mainBundle().URLForResource("Sample_Core_Data_App", withExtension: "momd")! - return NSManagedObjectModel(contentsOfURL: modelURL)! - }() - - lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator = { - // The persistent store coordinator for the application. This implementation creates and returns a coordinator, having added the store for the application to it. This property is optional since there are legitimate error conditions that could cause the creation of the store to fail. - // Create the coordinator and store - let coordinator = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel) - let url = self.applicationDocumentsDirectory.URLByAppendingPathComponent("SingleViewCoreData.sqlite") - var failureReason = "There was an error creating or loading the application's saved data." - do { - try coordinator.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: url, options: nil) - } catch { - // Report any error we got. - var dict = [String: AnyObject]() - dict[NSLocalizedDescriptionKey] = "Failed to initialize the application's saved data" - dict[NSLocalizedFailureReasonErrorKey] = failureReason - - dict[NSUnderlyingErrorKey] = error as NSError - let wrappedError = NSError(domain: "YOUR_ERROR_DOMAIN", code: 9999, userInfo: dict) - // Replace this with code to handle the error appropriately. - // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. - NSLog("Unresolved error \(wrappedError), \(wrappedError.userInfo)") - abort() - } - - return coordinator - }() - - lazy var managedObjectContext: NSManagedObjectContext = { - // Returns the managed object context for the application (which is already bound to the persistent store coordinator for the application.) This property is optional since there are legitimate error conditions that could cause the creation of the context to fail. - let coordinator = self.persistentStoreCoordinator - var managedObjectContext = NSManagedObjectContext(concurrencyType: .MainQueueConcurrencyType) - managedObjectContext.persistentStoreCoordinator = coordinator - return managedObjectContext - }() - - // MARK: - Core Data Saving support - - func saveContext () { - if managedObjectContext.hasChanges { - do { - try managedObjectContext.save() - } catch { - // Replace this implementation with code to handle the error appropriately. - // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. - let nserror = error as NSError - NSLog("Unresolved error \(nserror), \(nserror.userInfo)") - abort() - } - } - } +// lazy var applicationDocumentsDirectory: NSURL = { +// // The directory the application uses to store the Core Data store file. This code uses a directory named "au.com.ossh.Sample_Core_Data_App" in the application's documents Application Support directory. +// let urls = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask) +// return urls[urls.count-1] +// }() +// +// lazy var managedObjectModel: NSManagedObjectModel = { +// // The managed object model for the application. This property is not optional. It is a fatal error for the application not to be able to find and load its model. +// let modelURL = NSBundle.mainBundle().URLForResource("Sample_Core_Data_App", withExtension: "momd")! +// return NSManagedObjectModel(contentsOfURL: modelURL)! +// }() +// +// lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator = { +// // The persistent store coordinator for the application. This implementation creates and returns a coordinator, having added the store for the application to it. This property is optional since there are legitimate error conditions that could cause the creation of the store to fail. +// // Create the coordinator and store +// let coordinator = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel) +// let url = self.applicationDocumentsDirectory.URLByAppendingPathComponent("SingleViewCoreData.sqlite") +// var failureReason = "There was an error creating or loading the application's saved data." +// do { +// try coordinator.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: url, options: nil) +// } catch { +// // Report any error we got. +// var dict = [String: AnyObject]() +// dict[NSLocalizedDescriptionKey] = "Failed to initialize the application's saved data" +// dict[NSLocalizedFailureReasonErrorKey] = failureReason +// +// dict[NSUnderlyingErrorKey] = error as NSError +// let wrappedError = NSError(domain: "YOUR_ERROR_DOMAIN", code: 9999, userInfo: dict) +// // Replace this with code to handle the error appropriately. +// // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. +// NSLog("Unresolved error \(wrappedError), \(wrappedError.userInfo)") +// abort() +// } +// +// return coordinator +// }() +// +// lazy var managedObjectContext: NSManagedObjectContext = { +// // Returns the managed object context for the application (which is already bound to the persistent store coordinator for the application.) This property is optional since there are legitimate error conditions that could cause the creation of the context to fail. +// let coordinator = self.persistentStoreCoordinator +// var managedObjectContext = NSManagedObjectContext(concurrencyType: .MainQueueConcurrencyType) +// managedObjectContext.persistentStoreCoordinator = coordinator +// return managedObjectContext +// }() +// +// // MARK: - Core Data Saving support +// +// func saveContext () { +// if managedObjectContext.hasChanges { +// do { +// try managedObjectContext.save() +// } catch { +// // Replace this implementation with code to handle the error appropriately. +// // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. +// let nserror = error as NSError +// NSLog("Unresolved error \(nserror), \(nserror.userInfo)") +// abort() +// } +// } +// } } diff --git a/Sample Core Data App/Sample Core Data App/CoreDataStackManager.swift b/Sample Core Data App/Sample Core Data App/CoreDataStackManager.swift index 197f28b..53edb73 100644 --- a/Sample Core Data App/Sample Core Data App/CoreDataStackManager.swift +++ b/Sample Core Data App/Sample Core Data App/CoreDataStackManager.swift @@ -84,19 +84,19 @@ class CoreDataStackManager: NSObject { // MARK: Types private struct Constants { - static let applicationDocumentsDirectoryName = "au.com.ossh.Info2" - static let iCloudContainerID = "iCloud.au.com.ossh.iWallet2" + static let applicationDocumentsDirectoryName = "au.com.ossh.Sample_Core_Data_App" + static let iCloudContainerID = "iCloud.au.com.ossh.Sample_Core_Data_App" static let errorDomain = "CoreDataStackManager" - static let modelName = "Info2" + static let modelName = "Sample_Core_Data_App" static let persistentStoreName = "persistentStore" static let iCloudPersistentStoreName = "persistentStore_ICLOUD" - static let storefileExtension = "iwallet_sqlite" - static let iCloudPreferenceKey = "au.com.ossh.Info2.UseICloudStorage" - static let iCloudPreferenceSelected = "au.com.ossh.Info2.iCloudStoragePreferenceSelected" // Records whether user has actually selected a preference - static let makeBackupPreferenceKey = "au.com.ossh.Info2.MakeBackup" - static let iCloudStoreFilenameKey = "au.com.ossh.Info2.iCloudStoreFileName" - static let ubiquityContainerKey = "au.com.ossh.Info2.ubiquityContainerID" - static let ubiquityTokenKey = "au.com.ossh.Info2.ubiquityToken" + static let storefileExtension = "sqlite" + static let iCloudPreferenceKey = "au.com.ossh.Sample_Core_Data_App.UseICloudStorage" + static let iCloudPreferenceSelected = "au.com.ossh.Sample_Core_Data_App.iCloudStoragePreferenceSelected" // Records whether user has actually selected a preference + static let makeBackupPreferenceKey = "au.com.ossh.Sample_Core_Data_App.MakeBackup" + static let iCloudStoreFilenameKey = "au.com.ossh.Sample_Core_Data_App.iCloudStoreFileName" + static let ubiquityContainerKey = "au.com.ossh.Sample_Core_Data_App.ubiquityContainerID" + static let ubiquityTokenKey = "au.com.ossh.Sample_Core_Data_App.ubiquityToken" static let timerPeriod: NSTimeInterval = 2.0 } @@ -164,7 +164,7 @@ class CoreDataStackManager: NSObject { FLOG(" Error getting managedObjectContext because persistentStoreCoordinator is nil") return nil } - var managedObjectContext = NSManagedObjectContext() + var managedObjectContext = NSManagedObjectContext(concurrencyType: NSManagedObjectContextConcurrencyType.MainQueueConcurrencyType) managedObjectContext.persistentStoreCoordinator = coordinator // Set the MergePolicy to prioritise external inputs let mergePolicy = NSMergePolicy(mergeType:NSMergePolicyType.MergeByPropertyStoreTrumpMergePolicyType ) @@ -177,7 +177,6 @@ class CoreDataStackManager: NSObject { func saveContext () { if let moc = managedObjectContext { - var error: NSError? = nil if moc.hasChanges { do { @@ -215,8 +214,7 @@ class CoreDataStackManager: NSObject { func localStoreExists() -> Bool { //FLOG("localStoreExists called") - let exists = "exists" - let doesnotexist = "does not exist" + var isDir: ObjCBool = false @@ -278,7 +276,7 @@ class CoreDataStackManager: NSObject { createFileQuery() - var fileManager: NSFileManager = NSFileManager.defaultManager() + let fileManager: NSFileManager = NSFileManager.defaultManager() let model: NSManagedObjectModel = managedObjectModel @@ -330,8 +328,9 @@ class CoreDataStackManager: NSObject { } do { - - let metaData: NSDictionary = try NSPersistentStoreCoordinator.metadataForPersistentStoreOfType(NSSQLiteStoreType, URL: storeURL!) + + let metaData: NSDictionary = NSDictionary() + try NSPersistentStoreCoordinator.metadataForPersistentStoreOfType(NSSQLiteStoreType, URL: storeURL!, options: metaData as [NSObject : AnyObject]) let result: Bool = model.isConfiguration(nil, compatibleWithStoreMetadata: metaData as! [String : AnyObject] ) @@ -409,11 +408,9 @@ class CoreDataStackManager: NSObject { } - - var error: NSError? = nil - + let metaData: NSDictionary = NSDictionary() do { - let metaData: NSDictionary = try NSPersistentStoreCoordinator.metadataForPersistentStoreOfType(NSSQLiteStoreType, URL:storeURL!) + try NSPersistentStoreCoordinator.metadataForPersistentStoreOfType(NSSQLiteStoreType, URL: storeURL!, options: metaData as [NSObject : AnyObject]) let result: Bool = model.isConfiguration(nil, compatibleWithStoreMetadata:metaData as! [String : AnyObject]) @@ -779,7 +776,7 @@ class CoreDataStackManager: NSObject { for document in docs { - if let url = document as? NSURL { + let url = document if let name: NSString = url.lastPathComponent { //FLOG(" local file = \(name)") @@ -792,7 +789,7 @@ class CoreDataStackManager: NSObject { } } - } + } let sortNameDescriptor: NSSortDescriptor = NSSortDescriptor.init(key:"path", ascending: false) @@ -906,9 +903,8 @@ class CoreDataStackManager: NSObject { if let path = iCloudContainerURL()?.path { //FLOG(" icloudStoreURL is " + path) - var isDir: Bool = false - var fileExists: Bool = NSFileManager.defaultManager().fileExistsAtPath(path) + //var fileExists: Bool = NSFileManager.defaultManager().fileExistsAtPath(path) //FLOG(" iCloudStoreURL " + (fileExists ? "exists" : "does not exist")) } else { @@ -927,7 +923,7 @@ class CoreDataStackManager: NSObject { // This function returns true if the user is logged in to iCloud, otherwise // it return false func isICloudContainerAvailable()->Bool { - if let currentToken = NSFileManager.defaultManager().ubiquityIdentityToken { + if let _ = NSFileManager.defaultManager().ubiquityIdentityToken { return true } else { @@ -1041,21 +1037,21 @@ class CoreDataStackManager: NSObject { // Lets use the existing PSC let migrationPSC: NSPersistentStoreCoordinator = NSPersistentStoreCoordinator(managedObjectModel: managedObjectModel) - var error: NSError? = nil + // Open the store do { - let sourceStore: NSPersistentStore = try migrationPSC.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: localStoreURL(), options: (localStoreOptions() as! [NSObject : AnyObject])) + let sourceStore: NSPersistentStore = try migrationPSC.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: localStoreURL(), options: localStoreOptions() as [NSObject : AnyObject]) //FLOG(" Successfully added store to migrate"); - var error2: NSError? = nil + //FLOG(" About to migrate the store..."); let migratedStore: NSPersistentStore? do { - migratedStore = try migrationPSC.migratePersistentStore(sourceStore, toURL:backupStoreURL(), options:(localStoreOptions() as! [NSObject : AnyObject]), withType:NSSQLiteStoreType) + migratedStore = try migrationPSC.migratePersistentStore(sourceStore, toURL:backupStoreURL(), options:(localStoreOptions() as [NSObject : AnyObject]), withType:NSSQLiteStoreType) } catch { migratedStore = nil @@ -1095,18 +1091,17 @@ class CoreDataStackManager: NSObject { // Open the store do { let sourceStore: NSPersistentStore = try migrationPSC.addPersistentStoreWithType(NSSQLiteStoreType, configuration:nil, URL: iCloudStoreURL(), - options: iCloudStoreOptions() as! [NSObject : AnyObject]) + options: iCloudStoreOptions() as [NSObject : AnyObject]) //FLOG(" Successfully added store to migrate"); - - var error: NSError? = nil + //FLOG(" About to migrate the store..."); let migratedStore: NSPersistentStore? do { - migratedStore = try migrationPSC.migratePersistentStore(sourceStore, toURL:backupStoreURL(), options:localStoreOptions() as! [NSObject : AnyObject], withType:NSSQLiteStoreType) - } catch var error1 as NSError { - error = error1 + migratedStore = try migrationPSC.migratePersistentStore(sourceStore, toURL:backupStoreURL(), options:localStoreOptions() as [NSObject : AnyObject], withType:NSSQLiteStoreType) + } catch let error as NSError { + migratedStore = nil } @@ -1146,7 +1141,6 @@ class CoreDataStackManager: NSObject { isOpening = true - let error: NSError? = nil let aPersistentStoreCoordinator: NSPersistentStoreCoordinator = NSPersistentStoreCoordinator(managedObjectModel: managedObjectModel) @@ -1201,7 +1195,7 @@ class CoreDataStackManager: NSObject { // Set the moc here because its defined as Lazy it may be initialised to nil already by // something! - let newMoc = NSManagedObjectContext() + let newMoc = NSManagedObjectContext(concurrencyType: NSManagedObjectContextConcurrencyType.MainQueueConcurrencyType) newMoc.persistentStoreCoordinator = persistentStoreCoordinator // Set the MergePolicy to prioritise external inputs let mergePolicy = NSMergePolicy(mergeType:NSMergePolicyType.MergeByPropertyStoreTrumpMergePolicyType ) @@ -1256,7 +1250,7 @@ class CoreDataStackManager: NSObject { func loadSeedData() { //FLOG(" called"); - let bgContext:NSManagedObjectContext = NSManagedObjectContext(concurrencyType: NSManagedObjectContextConcurrencyType.ConfinementConcurrencyType) + let bgContext:NSManagedObjectContext = NSManagedObjectContext(concurrencyType: NSManagedObjectContextConcurrencyType.PrivateQueueConcurrencyType) // Register for saves in order to merge any data from background threads NSNotificationCenter.defaultCenter().addObserver(self, selector:"storesDidSave:", name: NSManagedObjectContextDidSaveNotification, object:bgContext) @@ -1505,7 +1499,7 @@ class CoreDataStackManager: NSObject { // Open the store do { - let sourceStore:NSPersistentStore = try migrationPSC.addPersistentStoreWithType(NSSQLiteStoreType, configuration:nil, URL:iCloudStoreURL(), options:iCloudStoreOptions() as! [NSObject : AnyObject]) + let sourceStore:NSPersistentStore = try migrationPSC.addPersistentStoreWithType(NSSQLiteStoreType, configuration:nil, URL:iCloudStoreURL(), options:iCloudStoreOptions() as [NSObject : AnyObject]) //FLOG(" Successfully added store to migrate"); @@ -1515,7 +1509,7 @@ class CoreDataStackManager: NSObject { //FLOG(" About to migrate the store...") do { - let migratedStore: NSPersistentStore = try migrationPSC.migratePersistentStore(sourceStore, toURL:localStoreURL(), options:localStoreOptions() as! [NSObject : AnyObject], withType:NSSQLiteStoreType) + let migratedStore: NSPersistentStore = try migrationPSC.migratePersistentStore(sourceStore, toURL:localStoreURL(), options:localStoreOptions() as [NSObject : AnyObject], withType:NSSQLiteStoreType) moveSuccess = true //FLOG("store successfully migrated") @@ -1608,7 +1602,7 @@ class CoreDataStackManager: NSObject { //FLOG(" ") //FLOG(" ICLOUD DOCUMENTS (\(docs.count))") //FLOG(" =====================") - for document in docs { + for _ in docs { //FLOG(" \(document.lastPathComponent)") } //FLOG(" ") @@ -2590,7 +2584,7 @@ class CoreDataStackManager: NSObject { try fm.copyItemAtPath(sourcePath, toPath:destPath) copySuccess = true - } catch var error1 as NSError { + } catch let error1 as NSError { error = error1 copySuccess = false } catch { @@ -2976,7 +2970,7 @@ class CoreDataStackManager: NSObject { do { // Now delete the iCloud content and file try NSPersistentStoreCoordinator.removeUbiquitousContentAndPersistentStoreAtURL(iCloudStoreURL(), - options:(iCloudStoreOptions() as! [NSObject : AnyObject])) + options:(iCloudStoreOptions() as [NSObject : AnyObject])) // Now delete the iCloud content and file result = true } catch { @@ -3011,7 +3005,7 @@ class CoreDataStackManager: NSObject { // Open the existing local store using the original options let sourceStore: NSPersistentStore? do { - sourceStore = try migrationPSC.addPersistentStoreWithType(NSSQLiteStoreType, configuration:nil, URL:fileURL, options:(localStoreOptions() as! [NSObject : AnyObject])) + sourceStore = try migrationPSC.addPersistentStoreWithType(NSSQLiteStoreType, configuration:nil, URL:fileURL, options:(localStoreOptions() as [NSObject : AnyObject])) } catch _ { sourceStore = nil } @@ -3032,7 +3026,7 @@ class CoreDataStackManager: NSObject { // Now migrate the store using the iCloud options let newStore:NSPersistentStore? do { - newStore = try migrationPSC.migratePersistentStore(sourceStore!, toURL:iCloudStoreURL(), options:(iCloudStoreOptions() as! [NSObject : AnyObject]), withType:NSSQLiteStoreType) + newStore = try migrationPSC.migratePersistentStore(sourceStore!, toURL:iCloudStoreURL(), options:(iCloudStoreOptions() as [NSObject : AnyObject]), withType:NSSQLiteStoreType) } catch { newStore = nil diff --git a/Sample Core Data App/Sample Core Data App/FileRepresentation.swift b/Sample Core Data App/Sample Core Data App/FileRepresentation.swift index ec2d784..6494725 100644 --- a/Sample Core Data App/Sample Core Data App/FileRepresentation.swift +++ b/Sample Core Data App/Sample Core Data App/FileRepresentation.swift @@ -1,10 +1,11 @@ // // FileRepresentation.swift -// iWallet2 // // Created by Duncan Groenewald on 5/10/2014. // Copyright (c) 2014 Duncan Groenewald. All rights reserved. // +// This class is used for iCloud MetaData Queries +// import Foundation diff --git a/Sample Core Data App/Sample Core Data App/Info.plist b/Sample Core Data App/Sample Core Data App/Info.plist index fd1ab7c..cf96cd1 100644 --- a/Sample Core Data App/Sample Core Data App/Info.plist +++ b/Sample Core Data App/Sample Core Data App/Info.plist @@ -19,7 +19,7 @@ CFBundleSignature ???? CFBundleVersion - 1 + 2 LSRequiresIPhoneOS UILaunchStoryboardName diff --git a/Sample Core Data App/Sample Core Data App/MasterViewController.swift b/Sample Core Data App/Sample Core Data App/MasterViewController.swift index 778535d..85a7e8a 100644 --- a/Sample Core Data App/Sample Core Data App/MasterViewController.swift +++ b/Sample Core Data App/Sample Core Data App/MasterViewController.swift @@ -10,191 +10,242 @@ import UIKit import CoreData class MasterViewController: UITableViewController, NSFetchedResultsControllerDelegate { - + var detailViewController: DetailViewController? = nil var managedObjectContext: NSManagedObjectContext? = nil - - + + override func viewDidLoad() { super.viewDidLoad() + + NSNotificationCenter.defaultCenter().addObserver(self, selector:"storeOpened", name:CDConstants.OSStoreOpenedNotification, + object:cdManager) + NSNotificationCenter.defaultCenter().addObserver(self, selector:"refreshUI", name:CDConstants.OSDataUpdatedNotification, + object:cdManager) + + // Now open Core Data + // This will post a StoreChanged notification when done + + cdManager.checkUserICloudPreferenceAndSetupIfNecessary({ + FLOG("checkUserICloudPreferenceAndSetupIfNecessary done.") + }) + // Do any additional setup after loading the view, typically from a nib. self.navigationItem.leftBarButtonItem = self.editButtonItem() - + let addButton = UIBarButtonItem(barButtonSystemItem: .Add, target: self, action: "insertNewObject:") self.navigationItem.rightBarButtonItem = addButton + if let split = self.splitViewController { let controllers = split.viewControllers self.detailViewController = (controllers[controllers.count-1] as! UINavigationController).topViewController as? DetailViewController } } - + override func viewWillAppear(animated: Bool) { self.clearsSelectionOnViewWillAppear = self.splitViewController!.collapsed super.viewWillAppear(animated) } - + // Call this when the store is opened + func storeOpened() { + FLOG(" called") + FLOG("Core Data store has been opened") + self.managedObjectContext = cdManager.managedObjectContext + self.reloadB() + } + func refreshUI() { + FLOG(" called") + self.reloadB() + } + // + func reloadB() { + self.tableView.reloadData() + } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } - + func insertNewObject(sender: AnyObject) { - let context = self.fetchedResultsController.managedObjectContext - let entity = self.fetchedResultsController.fetchRequest.entity! - let newManagedObject = NSEntityDescription.insertNewObjectForEntityForName(entity.name!, inManagedObjectContext: context) - - // If appropriate, configure the new managed object. - // Normally you should use accessor methods, but using KVC here avoids the need to add a custom class to the template. - newManagedObject.setValue(NSDate(), forKey: "timeStamp") - - // Save the context. - do { - try context.save() - } catch { - // Replace this implementation with code to handle the error appropriately. - // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. - //print("Unresolved error \(error), \(error.userInfo)") - abort() + + if let frc = self.fetchedResultsController { + + let context = frc.managedObjectContext + let entity = frc.fetchRequest.entity! + let newManagedObject = NSEntityDescription.insertNewObjectForEntityForName(entity.name!, inManagedObjectContext: context) + + // If appropriate, configure the new managed object. + // Normally you should use accessor methods, but using KVC here avoids the need to add a custom class to the template. + newManagedObject.setValue(NSDate(), forKey: "timeStamp") + + // Save the context. + do { + try context.save() + } catch let error as NSError { + + print("Unresolved error \(error), \(error.userInfo)") + + } } } - + // MARK: - Segues - + override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { if segue.identifier == "showDetail" { if let indexPath = self.tableView.indexPathForSelectedRow { - let object = self.fetchedResultsController.objectAtIndexPath(indexPath) - let controller = (segue.destinationViewController as! UINavigationController).topViewController as! DetailViewController - controller.detailItem = object - controller.navigationItem.leftBarButtonItem = self.splitViewController?.displayModeButtonItem() - controller.navigationItem.leftItemsSupplementBackButton = true + if let frc = self.fetchedResultsController { + let object = frc.objectAtIndexPath(indexPath) + let controller = (segue.destinationViewController as! UINavigationController).topViewController as! DetailViewController + controller.detailItem = object + controller.navigationItem.leftBarButtonItem = self.splitViewController?.displayModeButtonItem() + controller.navigationItem.leftItemsSupplementBackButton = true + } } } } - + // MARK: - Table View - + override func numberOfSectionsInTableView(tableView: UITableView) -> Int { - return self.fetchedResultsController.sections?.count ?? 0 + if let frc = self.fetchedResultsController { + return frc.sections?.count ?? 0 + } else { + return 0 + } } - + override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - let sectionInfo = self.fetchedResultsController.sections![section] - return sectionInfo.numberOfObjects + if let frc = self.fetchedResultsController { + let sectionInfo = frc.sections![section] + return sectionInfo.numberOfObjects + } else { + return 0 + } } - + override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) self.configureCell(cell, atIndexPath: indexPath) return cell } - + override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool { // Return false if you do not want the specified item to be editable. return true } - + override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) { if editingStyle == .Delete { - let context = self.fetchedResultsController.managedObjectContext - context.deleteObject(self.fetchedResultsController.objectAtIndexPath(indexPath) as! NSManagedObject) + if let frc = self.fetchedResultsController { + let context = frc.managedObjectContext + context.deleteObject(frc.objectAtIndexPath(indexPath) as! NSManagedObject) - do { - try context.save() - } catch { - // Replace this implementation with code to handle the error appropriately. - // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. - //print("Unresolved error \(error), \(error.userInfo)") - abort() + do { + try context.save() + } catch let error as NSError { + + print("Unresolved error while saving \(error), \(error.userInfo)") + + } } } } - + // Check if FRC is not nil, if its nil then Core Data store is not initialised yet func configureCell(cell: UITableViewCell, atIndexPath indexPath: NSIndexPath) { - let object = self.fetchedResultsController.objectAtIndexPath(indexPath) - cell.textLabel!.text = object.valueForKey("timeStamp")!.description + if let frc = self.fetchedResultsController { + let object = frc.objectAtIndexPath(indexPath) + cell.textLabel!.text = object.valueForKey("timeStamp")!.description + } } - + // MARK: - Fetched results controller - - var fetchedResultsController: NSFetchedResultsController { + + // We have to check for a nil MOC here because the store may not be initialised yet + var fetchedResultsController: NSFetchedResultsController? { if _fetchedResultsController != nil { return _fetchedResultsController! } - let fetchRequest = NSFetchRequest() - // Edit the entity name as appropriate. - let entity = NSEntityDescription.entityForName("Event", inManagedObjectContext: self.managedObjectContext!) - fetchRequest.entity = entity - - // Set the batch size to a suitable number. - fetchRequest.fetchBatchSize = 20 - - // Edit the sort key as appropriate. - let sortDescriptor = NSSortDescriptor(key: "timeStamp", ascending: false) - - fetchRequest.sortDescriptors = [sortDescriptor] - - // Edit the section name key path and cache name if appropriate. - // nil for section name key path means "no sections". - let aFetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: self.managedObjectContext!, sectionNameKeyPath: nil, cacheName: "Master") - aFetchedResultsController.delegate = self - _fetchedResultsController = aFetchedResultsController - - do { - try _fetchedResultsController!.performFetch() - } catch { - // Replace this implementation with code to handle the error appropriately. - // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. - //print("Unresolved error \(error), \(error.userInfo)") - abort() + if let moc = cdManager.managedObjectContext { + + let fetchRequest = NSFetchRequest() + // Edit the entity name as appropriate. + let entity = NSEntityDescription.entityForName("Event", inManagedObjectContext: moc) + fetchRequest.entity = entity + + // Set the batch size to a suitable number. + fetchRequest.fetchBatchSize = 20 + + // Edit the sort key as appropriate. + let sortDescriptor = NSSortDescriptor(key: "timeStamp", ascending: false) + + fetchRequest.sortDescriptors = [sortDescriptor] + + // Edit the section name key path and cache name if appropriate. + // nil for section name key path means "no sections". + let aFetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: self.managedObjectContext!, sectionNameKeyPath: nil, cacheName: "Master") + aFetchedResultsController.delegate = self + _fetchedResultsController = aFetchedResultsController + + do { + try _fetchedResultsController!.performFetch() + } catch let error as NSError { + print("Unresolved error while fetching data \(error), \(error.userInfo)") + + } + + return _fetchedResultsController! + + } else { + + return nil + } - - return _fetchedResultsController! - } + } var _fetchedResultsController: NSFetchedResultsController? = nil - + func controllerWillChangeContent(controller: NSFetchedResultsController) { self.tableView.beginUpdates() } - + func controller(controller: NSFetchedResultsController, didChangeSection sectionInfo: NSFetchedResultsSectionInfo, atIndex sectionIndex: Int, forChangeType type: NSFetchedResultsChangeType) { switch type { - case .Insert: - self.tableView.insertSections(NSIndexSet(index: sectionIndex), withRowAnimation: .Fade) - case .Delete: - self.tableView.deleteSections(NSIndexSet(index: sectionIndex), withRowAnimation: .Fade) - default: - return + case .Insert: + self.tableView.insertSections(NSIndexSet(index: sectionIndex), withRowAnimation: .Fade) + case .Delete: + self.tableView.deleteSections(NSIndexSet(index: sectionIndex), withRowAnimation: .Fade) + default: + return } } - + func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) { switch type { - case .Insert: - tableView.insertRowsAtIndexPaths([newIndexPath!], withRowAnimation: .Fade) - case .Delete: - tableView.deleteRowsAtIndexPaths([indexPath!], withRowAnimation: .Fade) - case .Update: - self.configureCell(tableView.cellForRowAtIndexPath(indexPath!)!, atIndexPath: indexPath!) - case .Move: - tableView.deleteRowsAtIndexPaths([indexPath!], withRowAnimation: .Fade) - tableView.insertRowsAtIndexPaths([newIndexPath!], withRowAnimation: .Fade) + case .Insert: + tableView.insertRowsAtIndexPaths([newIndexPath!], withRowAnimation: .Fade) + case .Delete: + tableView.deleteRowsAtIndexPaths([indexPath!], withRowAnimation: .Fade) + case .Update: + self.configureCell(tableView.cellForRowAtIndexPath(indexPath!)!, atIndexPath: indexPath!) + case .Move: + tableView.deleteRowsAtIndexPaths([indexPath!], withRowAnimation: .Fade) + tableView.insertRowsAtIndexPaths([newIndexPath!], withRowAnimation: .Fade) } } - + func controllerDidChangeContent(controller: NSFetchedResultsController) { self.tableView.endUpdates() } - + /* - // Implementing the above methods to update the table view in response to individual changes may have performance implications if a large number of changes are made simultaneously. If this proves to be an issue, you can instead just implement controllerDidChangeContent: which notifies the delegate that all section and object changes have been processed. - - func controllerDidChangeContent(controller: NSFetchedResultsController) { - // In the simplest, most efficient, case, reload the table view. - self.tableView.reloadData() - } - */ - + // Implementing the above methods to update the table view in response to individual changes may have performance implications if a large number of changes are made simultaneously. If this proves to be an issue, you can instead just implement controllerDidChangeContent: which notifies the delegate that all section and object changes have been processed. + + func controllerDidChangeContent(controller: NSFetchedResultsController) { + // In the simplest, most efficient, case, reload the table view. + self.tableView.reloadData() + } + */ + }