$30 off During Our Annual Pro Sale. View Details »

Core Data: It's Not Dead Yet

Abizer Nasir
September 23, 2016

Core Data: It's Not Dead Yet

Core Data tends to be overlooked for persistence in favour of other options. In this talk I try and remind people that it is still around and it is even being updated and made easier to use.

The video is available on YouTube at https://youtu.be/iOdg7WOEWWE

Presented at FrenchKit conference in Paris, September 2016

Abizer Nasir

September 23, 2016
Tweet

More Decks by Abizer Nasir

Other Decks in Programming

Transcript

  1. In this talk I am going to remind you why

    Core Data is still relevant.
  2. What is Core Data → It is the Model layer

    for your application. → It is not just the persistence to disk, but also the the data objects. → Most of the time we concentrate on the objects, and think of saving to disk as secondary.
  3. Some Reasons People Don’t Use Core Data? → It’s a

    very big SDK which is hard to understand. → The team has different levels of Core Data experience. → Bugs and performance problems are hard to find and fix. → They’ve been burned with it before.
  4. Alternatives To Core Data → UserDefaults (Yes - I’ve seen

    this) ugh → NSCoding Not best for dynamic data → Magical Record Active Record on top of Core Data
  5. Alternatives - Realm → Open source → Cross Platform →

    Fast - Multithreaded → Easy to setup and use → Many plugins are available
  6. Core Data is large because it is powerful and flexible.

    You don’t have to use everything that it comes with.
  7. Core Data Is Part Of The System → Uses the

    latest available Swift / Objective-C syntax. → Works across all platforms. → Manages memory pressure → NSFetchedResultsController, for driving Table or Collection Views. → UIManagedDocument for document based
  8. Core Data Gives This For Free → Memory management with

    faulting. → Change Notifications. → Able to handle large datasets. → Undo / Redo. → Object Validation. → Management of the Object Graph.
  9. NSPersistentStore This is where the data is persisted. Usually the

    an SQLite store on disk, but can be memory.
  10. One Way To Set Up The Stack func initializeCoreDataStack() {

    guard let modelURL = NSBundle.mainBundle().URLForResource("PPRecipes", withExtension: "momd") else { fatalError("Failed to locate DataModel.momd in app bundle") } guard let mom = NSManagedObjectModel(contentsOfURL: modelURL) else { fatalError("Failed to initialize MOM") } let psc = NSPersistentStoreCoordinator(managedObjectModel: mom) let type = NSManagedObjectContextConcurrencyType.MainQueueConcurrencyType mainContext = NSManagedObjectContext(concurrencyType: type) mainContext?.persistentStoreCoordinator = psc let queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) dispatch_async(queue) { let fileManager = NSFileManager.defaultManager() guard let documentsURL = fileManager.URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask).first else { fatalError("Failed to resolve documents directory") } let storeURL = documentsURL.URLByAppendingPathComponent("PPRecipes.sqlite") do { try psc.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: storeURL, options: nil) } catch { fatalError("Failed to initialize PSC: \(error)") } self.populateTypeEntities() self.persistenceInitialized = true dispatch_sync(dispatch_get_main_queue()) { self.initializationComplete?() } } }
  11. !

  12. NSPersistentContainer → New, and simpler way of creating the Core

    Data Stack with sensible defaults. → Provides Convenience methods for dealing with multithreaded contexts. → Loads the stack asynchronously.
  13. New way to set up the stack: lazy var persistentContainer:

    NSPersistentContainer = { let container = NSPersistentContainer(name: "MyApp") container.loadPersistentStores) { (storeDescription, error) in if let error = error as NSError? { fatalError("Unresolved error \(error), \(error.userInfo)") } // Stack ready to use. } return container }()
  14. Managed Object Model → Model objects are subclasses on NSManagedObject.

    → Properties and relationships defined graphically.
  15. Managed Object Model → Core Data is about Entities and

    relationships, don’t think of keys and joins. → Be careful with inheritance, it doesn’t do what you think it does; subclasses all share the same table. → Make sure relationships are double ended
  16. Managed Objects → These are the actual data objects. →

    Managed by the Persistent Store Coordinator for putting them in a Managed Object Context or saving them to the Persistent Store. → Cannot be used across threads. → Use the objectID to recreate the object in other threads.
  17. New Convenience methods Much nicer initialiser: public convenience init(context moc:

    NSManagedObjectContext) Nicer way to get a fetch request public class func fetchRequest() -> NSFetchRequest<NSFetchRequestResult>
  18. Automatic Code Generation In the past, without generating a custom

    subclass, you needed to use Key-Value Coding: myObject.valueForKey("myProperty") Xcode 8 can now automatically generate the subclass and keep the files in Derived Data so it doesn’t appear in the source.
  19. Class Definition → Select Class Definition and Xcode will generate

    the class and the category for the properties. → You don’t have to create any files.
  20. Category Extension → You create the file which defines the

    class, with additional properties or methods. → Xcode generates the extension which has the properties and relationships defined in the editor.
  21. Multi-threading with MOCs Managed Objects can only be accessed from

    the same queue as their managed context. The initialiser is explicit init(concurrencyType ct: NSManagedObjectContextConcurrencyType)
  22. .mainQueueConcurrencyType → Runs on the main queue → Use objects

    from this type of managed object context in the UI. → NSPersistentContainer provides one with the viewContext parameter.
  23. .privateQueueConcurrencyType → Runs on a background queue useful for performing

    background tasks such as importing data. → NSPersistentContainer provides one with the newBackgroundContext() method. → Better to pass a closure to the performBackgroundTask method, which runs the block on the correct queue.
  24. Fetched Results → You get data into a managed object

    context with a fetch request. → Recently changed to have a generic type so no need to cast the results to the correct type.
  25. Create the request let request: NSFetchRequest<Note> = Note.fetchRequest() let sortDescriptor

    = NSSortDescriptor(key: #keyPath(Note.title), ascending: true) let predicate = NSPredicate(format: "%K beginswith %@", #keyPath(Note.title), "2016") request.sortDescriptors = [sortDescriptor] request.predicate = predicate
  26. Run the request var results: [Note] = [] do {

    results = try container.viewContext.fetch(request) } catch { print("\(error)") } // do something with results
  27. NSFetchedResultsController → Really useful table view and collection data sources.

    → Handles sections, section names, number of items, identifying objects with index paths. → Listens to changes in the context.
  28. Before The Changes Prepare the table view for changes in

    the datasource func controllerWillChangeContent( _ controller: NSFetchedResultsController<NSFetchRequestResult>) { tableview.beginUpdates() }
  29. Manage The Changes Make the changes to the tableview. func

    controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) { switch type { case .insert: // Insert rows case .delete: // Delete rows case .move: // Move rows case .update: // Update these rows } }
  30. After The Changes Finalize the changes. func controllerDidChangeContent( _ controller:

    NSFetchedResultsController<NSFetchRequestResult>) { tableview.endUpdates() }
  31. Faulting → When you fetch a managed object the properties

    and relationships may not be populated until they are actually used. This is called “firing a fault” → It’s like a promise of data that is available when you need it.
  32. “could not fulfill a fault” → Although it’s a major

    reason for Core Data’s performance, it sometimes leads to puzzling errors. → This error is generated when the faulted object is deleted by some other context. → More common than you would expect, unfortunately.
  33. New - Query Generations You can pin the context to

    a generation of data. If a fault gets deleted, it is still available to the pinned context.
  34. do { try container.viewContext.setQueryGenerationFrom(NSQueryGenerationToken.current) } catch { print("\(error)") } It’s

    now safe to fire a fault, the object and properties will still be available even if it has been deleted from the most current generation.
  35. → Any fetches will load the most recent version and

    pin it to the most current version. → As will save(), reset() or handling any merge notifications.
  36. → Not mentioned in the WWDC talk - but it

    is in the release notes. → The Core Data iCloud methods have all been marked as deprecated, but they will be available for some time.
  37. Summary → Core Data is getting easier to use. →

    Better support for Swift. → I’m really excited to try the new capabilities, and I hope you are too.