Very rarely (<1% of users), a user will inform me that they open my app, and all their data is gone. The problem I'm having is that I am unable to replicate the issue because it happens so rarely. I don't even know how to begin fixing this problem. It seems like this does not happen while a user is using the app. They seem to just open it one day and the data is no longer there.
On one occasion, a user was able to recover the data by turning off and on the iCloud switch for my app (in the phone settings). However, this did not work for another user.
I was reading that iOS may delete the cache when the storage is full. However, to my understanding, the default location for the container is in Application Support.
Here is my Core Data implementation in App Delegate:
lazy var persistentContainer: NSPersistentContainer = {
let container = NSPersistentCloudKitContainer(name: "Bench")
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
print("Unresolved error \(error), \(error.userInfo)")
}
})
container.viewContext.automaticallyMergesChangesFromParent = true
return container
}()
func saveContext () {
let context = persistentContainer.viewContext
if context.hasChanges {
context.perform {
do {
try context.save()
} catch {
let nserror = error as NSError
print(nserror.localizedDescription)
}
}
}
}
I access it globally like this:
let ad = UIApplication.shared.delegate as! AppDelegate
let context = ad.persistentContainer.viewContext
Related
I am updating a pre-iOS13 Core Data app to use Core Data+CloudKit syncing to support single users on multiple devices. The syncing is supposed to occur automagically, and in an interim step in my development it did work. Now it's not working, with CloudKit telemetry not registering any activity, and I can't figure out why it's not working.
In my previous app versions I provided a small number of label strings in UserDefaults and allowed users to modify them, with the updated versions put back into UserDefaults. It was a shortcut to avoid having to manage a second Core Data entity for what would only ever be a small number of objects.
I have since realized this won't work in a multi-device implementation because the fact that the local Core Data database is empty no longer means it's the first use for that user. Instead, each new device needs to look to the cloud-based data source to find the user's in-use label strings, and only use app-provided default values if the user doesn't already have something else.
I followed Apple's instructions for Setting Up Core Data with CloudKit, and at first syncing worked fine. But then I realized the syncing behavior wasn't correct, and that instead of pre-populating from strings stored in UserDefaults I really needed to provide a pre-populated Core Data database (.sqlite files). I implemented that and the app now works fine locally after copying the bundled .sqlite files at first local launch.
But for some reason this change caused the CloudKit syncing to stop working. Now, not only do I not get any automagical updates on other devices, I get no results in the CloudKit dashboard telemetry so it appears that CloudKit synching never gets started. This is odd because locally I am getting notifications of 'remote' changes that just occurred locally (the function I list as a #selector for the notification is being called locally when I save new data locally).
I'm stumped as to what the problem/solution is. Here is my relevant code.
//In my CoreDataHelper class
lazy var context = persistentContainer.viewContext
lazy var persistentContainer: NSPersistentCloudKitContainer = {
let appName = Bundle.main.infoDictionary!["CFBundleName"] as! String
let container = NSPersistentCloudKitContainer(name: appName)
//Pre-load my default Core Data data (Category names) on first launch
let storeUrl = FileManager.default.urls(for: .applicationSupportDirectory, in:.userDomainMask).first!.appendingPathComponent(appName + ".sqlite")
let storeUrlFolder = FileManager.default.urls(for: .applicationSupportDirectory, in:.userDomainMask).first!
if !FileManager.default.fileExists(atPath: (storeUrl.path)) {
let seededDataUrl = Bundle.main.url(forResource: appName, withExtension: "sqlite")
let seededDataUrl2 = Bundle.main.url(forResource: appName, withExtension: "sqlite-shm")
let seededDataUrl3 = Bundle.main.url(forResource: appName, withExtension: "sqlite-wal")
try! FileManager.default.copyItem(at: seededDataUrl!, to: storeUrl)
try! FileManager.default.copyItem(at: seededDataUrl2!, to: storeUrlFolder.appendingPathComponent(appName + ".sqlite-shm"))
try! FileManager.default.copyItem(at: seededDataUrl3!, to: storeUrlFolder.appendingPathComponent(appName + ".sqlite-wal"))
}
let storeDescription = NSPersistentStoreDescription(url: storeUrl)
storeDescription.setOption(true as NSNumber, forKey: NSPersistentHistoryTrackingKey)
//In the view controllers, we'll listen for relevant remote changes
let remoteChangeKey = "NSPersistentStoreRemoteChangeNotificationOptionKey"
storeDescription.setOption(true as NSNumber, forKey: remoteChangeKey)
container.persistentStoreDescriptions = [storeDescription]
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
//This is returning nil but I don't think it should
print(storeDescription.cloudKitContainerOptions?.containerIdentifier)
return container
}()
//In my view controller
let context = CoreDataHelper.shared.context
override func viewDidLoad() {
super.viewDidLoad()
//Do other setup stuff, removed for clarity
//enable CloudKit syncing
context.automaticallyMergesChangesFromParent = true
}
override func viewWillAppear(_ animated: Bool) {
clearsSelectionOnViewWillAppear = splitViewController!.isCollapsed
super.viewWillAppear(animated)
NotificationCenter.default.addObserver(
self,
selector: #selector(reportCKchange),
name: NSNotification.Name(
rawValue: "NSPersistentStoreRemoteChangeNotification"),
object: CoreDataHelper.shared.persistentContainer.persistentStoreCoordinator
)
updateUI()
}
#objc
fileprivate func reportCKchange() {
print("Change reported from CK")
tableView.reloadData()
}
Note: I have updated the target to be iOS13+.
I think a newly created NSPersistentStoreDescription has no cloudKitContainerOptions by default.
To set them, try:
storeDescription.cloudKitContainerOptions = NSPersistentCloudKitContainerOptions(containerIdentifier: <<Your CloudKit ID>>)
I discovered as well that ALL attributes of your entities must be optional, or have a default value, otherwise it just won't work. Be nice if this was clearer in the documentation...
EDIT *********
7-09-2020 1:39 PM PST
I've got what I believe will suffice as a minimal working reproducible version of the app available at:
https://github.com/Rattletrap99/penny-game-test
EDIT *********
I'm building a game in which users create coins as rewards for various achievements. The coins are saved as managed objects in Core Data, with their various attributes. They are retrieved for various reasons, have their attributes modified, etc., during game play.
Everything saves and retrieves perfectly until I quit and relaunch, at which point no data is present in the persistent store. This is the case whether using a simulator or a device.
My usual means of saving to Core Data is:
func saveIt(){
guard let appDelegate =
UIApplication.shared.delegate as? AppDelegate else {
return
}
appDelegate.saveContext()
}
Which calls:
func saveContext () {
let context = persistentContainer.viewContext
if context.hasChanges {
do {
try context.save()
savedFlag += 1
let coinFetchRequest =
NSFetchRequest<NSManagedObject>(entityName: "Coin")
let savedCoins = try context.fetch(coinFetchRequest) as! [Coin]
print("In appDelegate, saveContext, after context.save, there are \(savedCoins.count) coins.")
print("Successfully saved in appDelegate \(String(describing: savedFlag)) times")
} 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)")
}
}
}
Every print() statement I put in the code confirms the save, but when I retrieve (upon relaunch), via code similar to:
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let issuerFetchRequest =
NSFetchRequest<NSManagedObject>(entityName: "Issuer")
let coinFetchRequest =
NSFetchRequest<NSManagedObject>(entityName: "Coin")
do {
let issuers = try context.fetch(issuerFetchRequest) as! [Issuer]
print(" ### Upon startup, there are \(issuers.count) Issuers in CD")
let coins = try context.fetch(coinFetchRequest) as! [Coin]
print(" #### Upon startup, there are \(coins.count) Coins in CD")
} catch let error as NSError {
print("Could not fetch. \(error), \(error.userInfo)")
}
I get:
### Upon startup, there are 0 Issuers in CD
#### Upon startup, there are 0 Coins in CD
Also, I try to save in applicationWillTerminate to be certain the data is saved before quitting:
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()
let issuerFetchRequest =
NSFetchRequest<NSManagedObject>(entityName: "Issuer")
let coinFetchRequest =
NSFetchRequest<NSManagedObject>(entityName: "Coin")
do {
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let issuers = try context.fetch(issuerFetchRequest) as! [Issuer]
let coins = try context.fetch(coinFetchRequest) as! [Coin]
print("\\\\ Upon quit, there are \(issuers.count) Issuers in CD")
print("\\\\ Upon quit, there are \(coins.count) Coins in CD")
} catch let error as NSError {
print("Could not fetch. \(error), \(error.userInfo)")
}
}
However, the print() statements are not printed, leading me to believe applicationWillTerminate is never fired.
I should mention that Issuer has a to many relationship with Coin, and I ensure that there are Issuers present before creating and saving a Coin.
Any help greatly appreciated!
After beating myself half to death for longer than I care to admit, I found the fault in these three lines in AppDelegate.swift:
description.shouldInferMappingModelAutomatically = true
description.shouldMigrateStoreAutomatically = true
container.persistentStoreDescriptions = [description]
Once these were removed, everything went back to normal. I'd like to say I understand why, but honestly, removing these lines were an act of desperation. And the irony is that I had added these lines in an attempt to rectify an earlier problem with fetching from Core Data.
Many thanks to all who contributed!
Remember to load up the persistentContainer in your AppDelegate. You can create a project in Xcode with CoreData built-in.
// 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: "CoreDataModelName")
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)")
}
}
}
I have a NSManagedObject called Event that is shared between the host app and today extension. (In Target Membership, both the main app and the widget are checked).
The host app and widget have the same App Group identifier and both share Data Model(In Target Membership, both the main app and the widget are checked).
When I launch(run) the widget in Xcode, it shows all of the app events (Event) that are already saved in the host app. However, when I add a new event, it appears in the host app but NOT in today-widget. If I relaunch the widget, all the events are shown including the last event that previously was not.
This is the method that fetches events. It is defined in TodayViewController of the widget.
private func fetchEvents(date: Date) {
let predicates = NSCompoundPredicate(andPredicateWithSubpredicates: [
NSPredicate(format: "date = %#",Date().startOfDay as CVarArg),
NSPredicate(format: "startTime >= %#", Date() as CVarArg)
])
if let ev = try? TPEvent.fetchAll(predicates: predicates, in: persistentManager.context) {
events = ev
}
}
This event is called in viewWillAppear and widgetPerformUpdate.
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
fetchEvents(date: Date())
self.tableView.reloadData()
}
func widgetPerformUpdate(completionHandler: (#escaping (NCUpdateResult) -> Void)) {
self.fetchEvents(date: Date() )
self.tableView.reloadData()
completionHandler(NCUpdateResult.newData)
}
persistentManaged.context is PersistentManager.shared.context (see code below).
By the way, both of the methods above are called when I view today-widget. I have a lot of time figuring out this issue but could not do so.
What could be the issue and how to fix it?
Please just comment should you need more info or have any question.
Update
I have a singleton PersistentManager. Use viewContext both in the host app and widget.
public final class PersistentManager {
init() {}
public static let shared = PersistentManager()
public lazy var persistentContainer: NSPersistentContainer = {
let container = NSPersistentCloudKitContainer(name: "Event")
guard let fileContainer = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.event.data") else {
fatalError("Shared file container could not be created.")
}
let storeURL = fileContainer.appendingPathComponent("Event.sqlite")
let storeDescription = NSPersistentStoreDescription(url: storeURL)
container.persistentStoreDescriptions = [storeDescription]
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
container.viewContext.automaticallyMergesChangesFromParent = true
do {
try container.viewContext.setQueryGenerationFrom(.current)
} catch {
fatalError("###\(#function): Failed to pin viewContext to the current generation:\(error)")
}
return container
}()
public lazy var context = persistentContainer.viewContext
// MARK: - Core Data Saving support
public func saveContext () {
let context = persistentContainer.viewContext
if context.hasChanges {
do {
try context.save()
} catch {
let nserror = error as NSError
fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
}
}
}
}
The issue is that the main app and the app extension work as two different processes on iOS.
CoreData works with NotificationCenter which sends notifications only within the main app process. Thus, you have to send interprocess notification here.
One hidden way to send interprocess notification on iOS is to use KVO on the UserDefaults object.
In NSUserDefaults.h header file Apple states that
/*!
NSUserDefaultsDidChangeNotification is posted whenever any user defaults changed within the current process, but is not posted when ubiquitous defaults change, or when an outside process changes defaults. Using key-value observing to register observers for the specific keys of interest will inform you of all updates, regardless of where they're from.
*/
Having this specified, one can assume that by using KVO on the particular key of UserDefaults, the value change will be propagated from the app to the extension, and vice versa.
So, the approach can be that on each change in the main app you save the current timestamp of the change into the UserDefaults:
/// When the change is made in the main app:
let defaults = UserDefaults(suiteName: "group.<your bundle id>")
defaults["LastChangeTimestamp"] = Date()
defaults.synchronize()
In the app extension:
let defaults = UserDefaults(suiteName: "group.<your bundle id>")
func subscribeForChangesObservation() {
defaults?.addObserver(self, forKeyPath: "LastChangeTimestamp", options: [.new, .initial], context: nil)
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
// Process your changes here.
}
deinit {
defaults?.removeObserver(self, forKeyPath: "LastChangeTimestamp")
}
Every time, when user launches application, app sends request to API and receive Data from it. I have to storage old data in a file on device and update them with every launch. When size of Data will be over than 3-5 mb, reading can be very long, isn't it?
My questions is: if i will store more than 5 mb of Data in file, can i quickly fetchs them, or only use database?
If yes, how can i create directory and put file to it?
And should i archive them?
I had a similar task and used CoreData with background fetch via DispatchQueue. I think this should help.
EDIT:
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else {
return
}
DispatchQueue.global(qos: .background).async {
let managedContext = appDelegate.persistentContainer.viewContext
let fetchRequest = NSFetchRequest<NSManagedObject>(entityName: "ENTITY")
do {
self."NSManagedObject ARRAY" = try managedContext.fetch(fetchRequest)
} catch let error as NSError {
print("Could not fetch. \(error), \(error.userInfo)")
}
DispatchQueue.main.async {
//MAKING CHANGES TO VIEW
}
}
P.S. I don't know how fast this fetch is, though it helped a lot in my task.
This question already has answers here:
NSPersistentContainer is only available in 10.0 or newer : error
(4 answers)
Closed 5 years ago.
I'm use core data in my application. I would like the app to open in iOS 9, but it doesn't. How can I fix it?
This section gives a warning. How do I get it to work on both iOS 9
and iOS 10?
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: "IndirimiKovala")
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)")
}
}
}
}
Method 1 : AppDelegate
// 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
}()
static func getEntity<T: NSManagedObject>() -> T {
if #available(iOS 10, *) {
let obj = T(context: CoreDataStack.context)
return obj
} else {
guard let entityDescription = NSEntityDescription.entity(forEntityName: NSStringFromClass(T.self), in: CoreDataStack.context) else {
fatalError("Core Data entity name doesn't match.")
}
let obj = T(entity: entityDescription, insertInto: CoreDataStack.context)
return obj
}
}
// 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()
}
}
}
}
}
}
Method 2 : Custom Class
Create a swift file named as CoreDataStack and copy paste below code into it.
import UIKit
import CoreData
class CoreDataStack {
static var applicationDocumentsDirectory: URL = {
let urls = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
return urls[urls.count-1]
}()
static 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(for: CoreDataStack.self).url(forResource: "DatabaseTest", withExtension: "momd")! // type your database name here..
return NSManagedObjectModel(contentsOf: modelURL)!
}()
static 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("DatabaseTest.sqlite") // type your database name here...
var failureReason = "There was an error creating or loading the application's saved data."
let options = [NSMigratePersistentStoresAutomaticallyOption: NSNumber(value: true as Bool), NSInferMappingModelAutomaticallyOption: NSNumber(value: true as Bool)]
do {
try coordinator.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: url, options: options)
} 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
}()
static 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 = persistentStoreCoordinator
var managedObjectContext = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
managedObjectContext.persistentStoreCoordinator = coordinator
return managedObjectContext
}()
static func getEntity<T: NSManagedObject>() -> T {
if #available(iOS 10, *) {
let obj = T(context: CoreDataStack.context)
return obj
} else {
guard let entityDescription = NSEntityDescription.entity(forEntityName: NSStringFromClass(T.self), in: CoreDataStack.context) else {
fatalError("Core Data entity name doesn't match.")
}
let obj = T(entity: entityDescription, insertInto: CoreDataStack.context)
return obj
}
}
// MARK: - Core Data Saving support
static 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()
}
}
}
}
And edit your AppDelegate as below
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.
CoreDataStack.saveContext()
}
Usage
Here Task is my entity name.
func dummyData() {
let task: Task = CoreDataStack.getEntity()
task.title = "Hello World!"
CoreDataStack.saveContext()
}