App sometimes crashes when trying to merge private context into main context - ios

So this is pretty hard to debug. First, that's the basic setup:
I have a method that runs every second:
#objc private func trigger() {
appDelegate.persistentContainer.performBackgroundTask { (privateContext) in
/* modifying database */
do {
// I tried setting a mergePolicy, but it didn't help
privateContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
try privateContext.save()
} catch let error as NSError {
fatalError("Couldn't save private context in Handler: \(error.debugDescription)")
}
}
}
As you see, it's using a private context.
In my appDelegate (where I have the main context) I set this up:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
/* more stuff */
NotificationCenter.default.addObserver(self, selector: #selector(mergeToMainViewContext(notification:)), name: NSNotification.Name.NSManagedObjectContextDidSave, object: nil)
return true
}
func saveContext () {
let context = persistentContainer.viewContext
context.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy // That's what I added to try. Nothing changed though
if context.hasChanges {
do {
try context.save()
} catch {
// Replace this implementation with code to handle the error appropriately.
// fatalError() 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
fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
}
}
}
func mergeToMainViewContext(notification: Notification) {
persistentContainer.viewContext.mergeChanges(fromContextDidSave: notification)
}
You see, I added an observer that should fire every time the private context (or any other context) saves.
However, occasionally the app crashes. Most of the time, it crashes without any error messages. Xcode just tells me that it crashes at this line:
persistentContainer.viewContext.mergeChanges(fromContextDidSave: notification)
And that's it :/.
Sometimes though, it crashes and says that there has been a merge conflict. The differences that are listed there are tiny. There are value differences of 0.1 or something. That's why I tried using mergePolicy but it doesn't help.
(I would post this error message, but I don't know how to reproduce this bug)
I have no idea where to look, how to start debugging this. Does anyone have an idea?
Let me know, if you need any more information! Thanks for any help

Related

I am getting records from core data in ios 10 but I am not getting same records for iOS 9

In iOS 10 I am getting records from core data but the same records I am not getting for iOS 9.
Here is my code:
let request:NSFetchRequest<NSFetchRequestResult> = NSFetchRequest(entityName: "Airport")
let predicate = NSPredicate(format: "iata == %#", arrayData[i])
request.predicate = predicate
do {
airportData = try appDel?.databaseContext.fetch(request) as! [Airport]
print("data:\(airportData.count)")
} catch {
print("Fetching Failed")
}
Actually it's duplicated question. For iOS 8 and 9 you should implement another app delegate and use 2 different ways for inserting data into database.
Xcode 8 Core Data Template for iOS 8,9
Let me show you example from my project.
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
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.
// Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
}
func applicationDidEnterBackground(_ application: UIApplication) {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}
func applicationWillEnterForeground(_ application: UIApplication) {
// Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
}
func applicationDidBecomeActive(_ application: UIApplication) {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}
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.
if #available(iOS 10.0, *) {
self.saveContext()
} else {
self.saveOldStyleContext()
}
}
// MARK: - Core Data stack
#available(iOS 10.0, *)
lazy var persistentContainer: NSPersistentContainer = {
let container = NSPersistentContainer(name: "CafeManager")
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
//Error handling
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
return container
}()
#available(iOS 10.0, *)
static var persistantContainer: NSPersistentContainer {
return (UIApplication.shared.delegate as! AppDelegate).persistentContainer
}
static var viewContext: NSManagedObjectContext {
if #available(iOS 10.0, *) {
return persistantContainer.viewContext
} else {
return AppDelegate.managedObjectContext
}
}
// MARK: - Core Data stack for iOS 8+
static var managedObjectContext: NSManagedObjectContext = {
var applicationDocumentsDirectory: URL = {
// The directory the application uses to store the Core Data store file. This code uses a directory named "com.cadiridris.coreDataTemplate" in the application's documents Application Support directory.
let urls = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
return urls[urls.count-1]
}()
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 = Bundle.main.url(forResource: "CafeManager", withExtension: "momd")!
return NSManagedObjectModel(contentsOf: modelURL)!
}()
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: managedObjectModel)
let url = applicationDocumentsDirectory.appendingPathComponent("SingleViewCoreData.sqlite")
var failureReason = "There was an error creating or loading the application's saved data."
do {
try coordinator.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: url, options: nil)
} catch {
// Report any error we got.
var dict = [String: AnyObject]()
dict[NSLocalizedDescriptionKey] = "Failed to initialize the application's saved data" as AnyObject?
dict[NSLocalizedFailureReasonErrorKey] = failureReason as AnyObject?
dict[NSUnderlyingErrorKey] = error as NSError
let wrappedError = NSError(domain: "iGlock.CafeManager.com", 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
}()
// 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 = persistentStoreCoordinator
var managedObjectContext = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
managedObjectContext.persistentStoreCoordinator = coordinator
return managedObjectContext
}()
// MARK: - Core Data Saving support for iOS 10
#available(iOS 10.0, *)
func saveContext () {
let context = persistentContainer.viewContext
if context.hasChanges {
do {
try context.save()
} catch {
// Replace this implementation with code to handle the error appropriately.
// fatalError() 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
fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
}
}
}
// MARK: - Core Data Saving support for iOS 8+
func saveOldStyleContext () {
if AppDelegate.managedObjectContext.hasChanges {
do {
try AppDelegate.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()
}
}
}
}
And how I handled insertion requests for iOS 8-9 and 10+:
class func addNewCustomGuest (guestName: String, tableSession: TableSessionTable) {
if #available(iOS 10.0, *) {
let newGuest = GuestsTable(context: context)
newGuest.guestName = guestName
newGuest.openTime = Date() as NSDate
newGuest.closeTime = nil
tableSession.addToGuest(newGuest)
} else {
let newGuest = GuestsTable(entity: NSEntityDescription.entity(forEntityName: "GuestsTable", in: context)!, insertInto: context)
newGuest.guestName = guestName
newGuest.openTime = Date() as NSDate
newGuest.closeTime = nil
tableSession.addToGuest(newGuest)
}
try? context.save()
}
In the same time fetch requests will work for both versions:
class func getActiveGuestsForTable (tableSession: TableSessionTable) -> [GuestsTable]? {
guestTableRequest.predicate = NSPredicate(format: "table = %# and closeTime = %#", tableSession, NSNull() as CVarArg)
guestTableRequest.sortDescriptors = [NSSortDescriptor(key: "openTime", ascending: true, selector: #selector(NSDate.compare(_:)))]
let matchedGuests = try? context.fetch(guestTableRequest)
return matchedGuests
}

'NSPersistentContainer' is only available on iOS 10.0 or newer

I am new to IOS app development. I have developed an app which shows a website on user login. It works with deployment target set to 10.1. Inorder to make it comptaible with IOS 8 I am trying to set deployment target to 8 because of which I am below error.
'NSPersistentContainer' is only available on iOS 10.0 or newer
Can anyone help with code what I need to change to make it compatible with IOS 8,9 & 10.
Xcode:8.2.1
Swift version: 3.2
import UIKit
import CoreData
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication,didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
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.
// Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
}
func applicationDidEnterBackground(_ application: UIApplication) {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}
func applicationWillEnterForeground(_ application: UIApplication) {
// Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
}
func applicationDidBecomeActive(_ application: UIApplication) {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}
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()
}
// MARK: - Core Data stack
lazy var persistentContainer: NSPersistentContainer = {
/*
The persistent container for the application. This implementation
creates and returns a container, having loaded 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.
*/
let container = NSPersistentContainer(name: "testProject")
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
// Replace this implementation with code to handle the error appropriately.
// fatalError() 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.
/*
Typical reasons for an error here include:
* The parent directory does not exist, cannot be created, or disallows writing.
* The persistent store is not accessible, due to permissions or data protection when the device is locked.
* The device is out of space.
* The store could not be migrated to the current model version.
Check the error message to determine what the actual problem was.
*/
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
return container
}()
// MARK: - Core Data Saving support
func saveContext () {
let context = persistentContainer.viewContext
if context.hasChanges {
do {
try context.save()
} catch {
// Replace this implementation with code to handle the error appropriately.
// fatalError() 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
fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
}
}
}
}
After days of search here I got solution...
Just replaced with below content in appdelegate.swift file...It worked..
// MARK: - Core Data stack
#available(iOS 10.0, *)
lazy var persistentContainer: NSPersistentContainer = {
/*
The persistent container for the application. This implementation
creates and returns a container, having loaded 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.
*/
let container = NSPersistentContainer(name: "coreDataTestForPreOS")
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
// Replace this implementation with code to handle the error appropriately.
// fatalError() 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.
/*
Typical reasons for an error here include:
* The parent directory does not exist, cannot be created, or disallows writing.
* The persistent store is not accessible, due to permissions or data protection when the device is locked.
* The device is out of space.
* The store could not be migrated to the current model version.
Check the error message to determine what the actual problem was.
*/
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
return container
}()
// iOS 9 and below
lazy var applicationDocumentsDirectory: URL = {
let urls = FileManager.default.urls(for: .documentDirectory, in: .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 = Bundle.main.url(forResource: "coreDataTestForPreOS", withExtension: "momd")!
return NSManagedObjectModel(contentsOf: 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.appendingPathComponent("SingleViewCoreData.sqlite")
var failureReason = "There was an error creating or loading the application's saved data."
do {
try coordinator.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: url, options: nil)
} catch {
// Report any error we got.
var dict = [String: AnyObject]()
dict[NSLocalizedDescriptionKey] = "Failed to initialize the application's saved data" as AnyObject?
dict[NSLocalizedFailureReasonErrorKey] = failureReason as AnyObject?
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 #available(iOS 10.0, *) {
let context = persistentContainer.viewContext
if context.hasChanges {
do {
try context.save()
} catch {
// Replace this implementation with code to handle the error appropriately.
// fatalError() 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
fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
}
}
} else {
// iOS 9.0 and below - however you were previously handling it
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()
}
}
}
}
}

Initiating NSManagedObjectContext gives fatal error?

I am working on the last leg of my app and this seems to be the last portion I'm having issue with. Fingers crossed for that, anyway.
I have CoreData all set up and ready to be displayed in a table (hopefully, I can't really view it in a table yet to verify).
The issue I'm having right now is that when the views that implement CoreData load, there is a fatal error that occurs when it initiates my NSManagedObjectContext. Please note that I use two separate ViewControllers to get data in addition to the TableViewController, therefore I have declared a total of 3 NSManagedContextObjects in my app - which I'm not sure if that's the issue or not. The line that is causing it is in my viewDidLoad function. See the code below.
override func viewDidLoad() {
super.viewDidLoad()
managedObjectContext = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
}
I'm not entirely sure how to tackle this, for I was following a tutorial for the whole process.
EDIT: I am going to add all the code that is affected by CoreData so maybe it'll help the problem be solved.
Here is a screenshot of my core data model
AppDelegate
import UIKit
import CoreData
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
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.
// Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
}
func applicationDidEnterBackground(_ application: UIApplication) {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}
func applicationWillEnterForeground(_ application: UIApplication) {
// Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
}
func applicationDidBecomeActive(_ application: UIApplication) {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}
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()
}
// MARK: - Core Data Stack
lazy var persistentContainer: NSPersistentContainer = {
/*
The persistent container for the application. This implementation
creates and returns a container, having loaded 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.
*/
let container = NSPersistentContainer(name: "RSDB_1_0_COREDATA")
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
// Replace this implementation with code to handle the error appropriately.
// fatalError() 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.
/*
Typical reasons for an error here include:
* The parent directory does not exist, cannot be created, or disallows writing.
* The persistent store is not accessible, due to permissions or data protection when the device is locked.
* The device is out of space.
* The store could not be migrated to the current model version.
Check the error message to determine what the actual problem was.
*/
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
return container
}()
// MARK: - Core Data Saving support
func saveContext () {
let context = persistentContainer.viewContext
if context.hasChanges {
do {
try context.save()
} catch {
// Replace this implementation with code to handle the error appropriately.
// fatalError() 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
fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
}
}
}
}
First View Controller
let newInfo = GeneralInfo(context:managedObjectContext1)
newInfo.courseNumber = studCourse
newInfo.scenarioNumber = studScen
do {
try self.managedObjectContext1.save()
}catch {
print("Could not save data \(error.localizedDescription)")
}
performSegue(withIdentifier: "segue3", sender: self)
Different View Controller
let newEvaluation = GeneralEvaluation(context:managedObjectContext)
newEvaluation.idScore = String(ID)
newEvaluation.washScore = String(WASH)
newEvaluation.totalScore = String(total)
do {
try self.managedObjectContext.save()
}catch {
print("Could not save data \(error.localizedDescription)")
}
performSegue(withIdentifier: "segue4", sender: self)
TableViewController
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! TheTableViewCell
let info = courseInfo[indexPath.row]
let score = scores[indexPath.row]
cell.CLASS.text = info.courseNumber
cell.SCENARIO.text = info.scenarioNumber
cell.WASH.text = score.washScore
cell.ID.text = score.idScore
cell.TOTAL.text = score.totalScore
It would be helpful to see the text of the error message, and the stack trace.
You could start by breaking down that chain of calls, to narrow down where you're crashing.
let appDelegate = (UIApplication.shared.delegate as! AppDelegate)
let container = appDelegate.persistentContainer
managedObjectContext = container.viewContext
Is your stack (persistent container/persistent store coordinator/managed object model/managed object context instantiated before you load the view controller? It appears not. Since you crash so early, I'm going out on a limb and guessing that you have a spelling error or name mismatch somewhere (a filename that doesn't match what's in your defining code). Or perhaps your MOM is not included as a resource in your bundle?
Looks like you're throwing an error in your lazy var persistentContainer. That's good. That means you can rule out the view controllers as the cause. Can we see a screenshot of the files section of Xcode (command-1)?

How to use Ensembles Framework in Swift Project

I have a project which already uses Core Data. I have added support for the iPad, but now will need to use iCloud & Core Data to sync the data.
I came across Ensembles, it seems like an easy and robust framework to add to my project. Found here: https://github.com/drewmccormack/ensembles
However there are no Swift example projects with the Ensembles project so have attempted to do it myself. Here are the steps I have taken,
Step 1
Manually add Ensembles to iOS projects.
Step 2
Create new CoreDataStack using existing persistent store .sql file.
import UIKit
import CoreData
class CoreDataStack: NSObject, CDEPersistentStoreEnsembleDelegate {
static let defaultStack = CoreDataStack()
var ensemble : CDEPersistentStoreEnsemble? = nil
var cloudFileSystem : CDEICloudFileSystem? = nil
// MARK: - Core Data stack
lazy var storeName : String = {
return NSBundle.mainBundle().objectForInfoDictionaryKey(kCFBundleNameKey as String) as! String
}()
lazy var sqlName : String = {
return "SingleViewCoreData.sqlite"
}()
lazy var icloudStoreName : String = {
return self.storeName + "CloudStore"
}()
lazy var storeDescription : String = {
return "Core data stack of " + self.storeName
}()
lazy var iCloudAppID : String = {
return "iCloud." + NSBundle.mainBundle().bundleIdentifier!
}()
lazy var modelURL : NSURL = {
return NSBundle.mainBundle().URLForResource(self.storeName, withExtension: "momd")!
}()
lazy var storeDirectoryURL : NSURL = {
var directoryURL : NSURL? = nil
do {
try directoryURL = NSFileManager.defaultManager().URLForDirectory(NSSearchPathDirectory.ApplicationSupportDirectory, inDomain: .UserDomainMask, appropriateForURL: nil, create: true)
directoryURL = directoryURL!.URLByAppendingPathComponent(NSBundle.mainBundle().bundleIdentifier!, isDirectory: true)
} catch {
NSLog("Unresolved error: Application's document directory is unreachable")
abort()
}
return directoryURL!
}()
lazy var storeURL : NSURL = {
return self.storeDirectoryURL.URLByAppendingPathComponent(self.sqlName)
// return self.applicationDocumentsDirectory.URLByAppendingPathComponent(self.sqlName)
}()
lazy var applicationDocumentsDirectory: NSURL = {
// The directory the application uses to store the Core Data store file. This code uses a directory named "com.dprados.CoreDataSpike" 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(self.storeName, 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 = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel)
var options = [NSObject: AnyObject]()
options[NSMigratePersistentStoresAutomaticallyOption] = NSNumber(bool: true)
options[NSInferMappingModelAutomaticallyOption] = NSNumber(bool: true)
do {
try NSFileManager.defaultManager().createDirectoryAtURL(self.storeDirectoryURL, withIntermediateDirectories: true, attributes: nil)
} catch {
NSLog("Unresolved error: local database storage position is unavailable.")
abort()
}
// Create the coordinator and store
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
managedObjectContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
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()
}
}
}
static func save() {
CoreDataStack.defaultStack.saveContext()
}
func enableEnsemble() {
CoreDataStack.defaultStack.cloudFileSystem = CDEICloudFileSystem(ubiquityContainerIdentifier: nil)
CoreDataStack.defaultStack.ensemble = CDEPersistentStoreEnsemble(ensembleIdentifier: self.storeName, persistentStoreURL: self.storeURL, managedObjectModelURL: self.modelURL, cloudFileSystem: CoreDataStack.defaultStack.cloudFileSystem)
CoreDataStack.defaultStack.ensemble!.delegate = CoreDataStack.defaultStack
}
func persistentStoreEnsemble(ensemble: CDEPersistentStoreEnsemble!, didSaveMergeChangesWithNotification notification: NSNotification!) {
CoreDataStack.defaultStack.managedObjectContext.performBlockAndWait({ () -> Void in
CoreDataStack.defaultStack.managedObjectContext.mergeChangesFromContextDidSaveNotification(notification)
})
if notification != nil {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(0.02 * Double(NSEC_PER_MSEC))), dispatch_get_main_queue(), {
NSLog("Database was updated from iCloud")
CoreDataStack.defaultStack.saveContext()
NSNotificationCenter.defaultCenter().postNotificationName("DB_UPDATED", object: nil)
})
}
}
func persistentStoreEnsemble(ensemble: CDEPersistentStoreEnsemble!, globalIdentifiersForManagedObjects objects: [AnyObject]!) -> [AnyObject]! {
NSLog("%#", (objects as NSArray).valueForKeyPath("uniqueIdentifier") as! [AnyObject])
return (objects as NSArray).valueForKeyPath("uniqueIdentifier") as! [AnyObject]
}
func syncWithCompletion(completion: (() -> Void)!) {
if CoreDataStack.defaultStack.ensemble!.leeched {
CoreDataStack.defaultStack.ensemble!.mergeWithCompletion({ (error:NSError?) -> Void in
if error != nil && error!.code != 103 {
NSLog("Error in merge: %#", error!)
} else if error != nil && error!.code == 103 {
self.performSelector("syncWithCompletion:", withObject: nil, afterDelay: 1.0)
} else {
if completion != nil {
completion()
}
}
})
} else {
CoreDataStack.defaultStack.ensemble!.leechPersistentStoreWithCompletion({ (error:NSError?) -> Void in
if error != nil && error!.code != 103 {
NSLog("Error in leech: %#", error!)
} else if error != nil && error!.code == 103 {
self.performSelector("syncWithCompletion:", withObject: nil, afterDelay: 1.0)
} else {
self.performSelector("syncWithCompletion:", withObject: nil, afterDelay: 1.0)
if completion != nil {
completion()
}
}
})
}
}
}
Step 3
Update App Delegate to sync and add notifications
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
let _ : CoreDataStack = CoreDataStack.defaultStack
// Value.ValueTypeInManagedObjectContext(CoreDataStack.defaultStack.managedObjectContext)
CoreDataStack.defaultStack.saveContext()
CoreDataStack.defaultStack.enableEnsemble()
// Listen for local saves, and trigger merges
NSNotificationCenter.defaultCenter().addObserver(self, selector: "localSaveOccured:", name: CDEMonitoredManagedObjectContextDidSaveNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "cloudDataDidDownload:", name:CDEICloudFileSystemDidDownloadFilesNotification, object:nil)
CoreDataStack.defaultStack.syncWithCompletion(nil);
return true
}
func applicationDidEnterBackground(application: UIApplication) {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
let identifier : UIBackgroundTaskIdentifier = UIApplication.sharedApplication().beginBackgroundTaskWithExpirationHandler(nil)
CoreDataStack.defaultStack.saveContext()
CoreDataStack.defaultStack.syncWithCompletion( { () -> Void in
UIApplication.sharedApplication().endBackgroundTask(identifier)
})
}
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.
// Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
}
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject]) {
NSLog("Received a remove notification")
}
func applicationWillEnterForeground(application: UIApplication) {
// Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
CoreDataStack.defaultStack.syncWithCompletion(nil)
}
func applicationDidBecomeActive(application: UIApplication) {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
CoreDataStack.defaultStack.syncWithCompletion(nil)
}
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.
CoreDataStack.defaultStack.saveContext()
}
func localSaveOccured(notif: NSNotification) {
NSLog("Local save occured")
CoreDataStack.defaultStack.syncWithCompletion(nil)
}
func cloudDataDidDownload(notif: NSNotification) {
NSLog("Cloud data did download")
CoreDataStack.defaultStack.syncWithCompletion(nil)
}
Step 4
Add Notifications to project to refresh UI
override func viewWillAppear(animated: Bool) {
NSNotificationCenter.defaultCenter().addObserver(self, selector: "cloudDataDidDownload:", name:"DB_UPDATED", object:nil)
//cloudDataDidDownload refetches the entities and reload the table
}
Step 5
Watch the magic happen.. Unfortunately there is no magic atm. The new CoreDataStack works fine, I can save and retrieve data from the persistent store.
I have two devices logged into the same iCloud account, and neither data is shared to the other device.
When deleting the app and reinstalling the data is not retrieved from the iCloud and saved to the persistent store.
I do get the following NSLog when 'sometimes' saving data or loading up the app.
2016-04-06 13:17:37.101 APPNAME[435:152241] Cloud data did download
This is the outcome for the following appDelegate notification function
func cloudDataDidDownload(notif: NSNotification) {
NSLog("Cloud data did download")
CoreDataStack.defaultStack.syncWithCompletion(nil)
}
The notification is sent from this function in the CoreDataStack for when changes are merged.
func persistentStoreEnsemble(ensemble: CDEPersistentStoreEnsemble!, didSaveMergeChangesWithNotification notification: NSNotification!) {
CoreDataStack.defaultStack.managedObjectContext.performBlockAndWait({ () -> Void in
CoreDataStack.defaultStack.managedObjectContext.mergeChangesFromContextDidSaveNotification(notification)
})
if notification != nil {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(0.02 * Double(NSEC_PER_MSEC))), dispatch_get_main_queue(), {
NSLog("Database was updated from iCloud")
CoreDataStack.defaultStack.saveContext()
NSNotificationCenter.defaultCenter().postNotificationName("DB_UPDATED", object: nil)
})
}
}
So everything seems like it should be working fine. I get no errors but the data is not syncing. I cannot tell if the problem is the data backing up to iCloud or retrieving from iCloud and merging with the persistent store. All I can tell is that data is not shared between devices using the same iCloud account and the app does not actually restore the data from the iCloud when reinstalling the app.

Swift 2.0 - "Argument passed to call that takes no arguments"

I have check all of the other threads related to this issue and nothing has seemed to work. In this code I get an error on the line "if moc.hasChanges..." :
func saveContext () {
var error: NSError? = nil
if let moc = self.managedObjectContext {
if moc.hasChanges && !moc.save(error) {
// 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.
NSLog("Unresolved error \(error), \(error!.userInfo)")
abort()
}
}
}
Here is the full .swift file:
import UIKit
import CoreData
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
UIApplication.sharedApplication().setStatusBarStyle(UIStatusBarStyle.LightContent, animated: true)
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.
// Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
}
func applicationDidEnterBackground(application: UIApplication) {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}
func applicationWillEnterForeground(application: UIApplication) {
// Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
}
func applicationDidBecomeActive(application: UIApplication) {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}
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()
}
// 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 "HardingSoftware.HRC" 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("HRC", withExtension: "momd")!
return NSManagedObjectModel(contentsOfURL: modelURL)!
}()
lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator? = {
// The persistent store coordinator for the application. This implementation creates and return 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
var coordinator: NSPersistentStoreCoordinator? = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel)
let url = self.applicationDocumentsDirectory.URLByAppendingPathComponent("HRC.sqlite")
var error: NSError? = nil
var failureReason = "There was an error creating or loading the application's saved data."
coordinator = nil
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
if coordinator == nil {
return nil
}
var managedObjectContext = NSManagedObjectContext()
managedObjectContext.persistentStoreCoordinator = coordinator
return managedObjectContext
}()
// MARK: - Core Data Saving support
func saveContext () {
var error: NSError? = nil
if let moc = self.managedObjectContext {
if moc.hasChanges && !moc.save(error) {
// 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.
NSLog("Unresolved error \(error), \(error!.userInfo)")
abort()
}
}
}
}
In Swift 2.0, to save context, you have to use do { try ...} and catch {...} your error.
do {
try context.save()
} catch let error {
print("Could not cache the response \(error)")
}
In your case:
func saveContext () {
var error: NSError? = nil
if let moc = self.managedObjectContext {
if moc.hasChanges {
do {
try moc.save()
} catch let error {
print("Could not cache the response \(error)")
}
}
}
}
The NSManagedObjectContext now has a save function that does not use the error argument any more. Instead you have to catch the error in a try-catch block.
do { try context.save() }
catch let error { print((error as NSError)) }

Resources