Upgrade to Pro — share decks privately, control downloads, hide ads and more …

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. Core Data: It’s Not Dead Yet
    Abizer Nasir | @abizern | abizern.org

    View Slide

  2. In this talk I am going to remind you why Core Data
    is still relevant.

    View Slide

  3. 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.

    View Slide

  4. 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.

    View Slide

  5. 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

    View Slide

  6. Alternatives - Realm
    → Open source
    → Cross Platform
    → Fast - Multithreaded
    → Easy to setup and use
    → Many plugins are available

    View Slide

  7. But Realm isn’t Core Data

    View Slide

  8. Core Data is large because it is powerful and flexible.
    You don’t have to use everything that it comes with.

    View Slide

  9. 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

    View Slide

  10. 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.

    View Slide

  11. The Core Data Stack

    View Slide

  12. Not As Scary
    As It Looks.

    View Slide

  13. NSPersistentStore
    This is where the data is persisted. Usually the an
    SQLite store on disk, but can be memory.

    View Slide

  14. NSManagedObjectModel
    Description of the Entities; their properties and their
    relationships.

    View Slide

  15. NSPersistentStoreCoordinator
    Mediates between the data

    View Slide

  16. NSManagedObjectContext
    This is what we mainly care about. Most operations
    on model objects will occur in a moc.

    View Slide

  17. 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?()
    }
    }
    }

    View Slide

  18. !

    View Slide

  19. 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.

    View Slide

  20. 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
    }()

    View Slide

  21. Managed
    Object Model
    → Model objects are subclasses
    on NSManagedObject.
    → Properties and relationships
    defined graphically.

    View Slide

  22. 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

    View Slide

  23. 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.

    View Slide

  24. New Convenience methods
    Much nicer initialiser:
    public convenience init(context moc: NSManagedObjectContext)
    Nicer way to get a fetch request
    public class func fetchRequest() -> NSFetchRequest

    View Slide

  25. 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.

    View Slide

  26. 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.

    View Slide

  27. 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.

    View Slide

  28. 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)

    View Slide

  29. .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.

    View Slide

  30. .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.

    View Slide

  31. 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.

    View Slide

  32. Create the request
    let request: NSFetchRequest = 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

    View Slide

  33. Run the request
    var results: [Note] = []
    do {
    results = try container.viewContext.fetch(request)
    } catch {
    print("\(error)")
    }
    // do something with results

    View Slide

  34. 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.

    View Slide

  35. Before The Changes
    Prepare the table view for changes in the datasource
    func controllerWillChangeContent(
    _ controller: NSFetchedResultsController) {
    tableview.beginUpdates()
    }

    View Slide

  36. Manage The Changes
    Make the changes to the tableview.
    func controller(_ controller: NSFetchedResultsController,
    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
    }
    }

    View Slide

  37. After The Changes
    Finalize the changes.
    func controllerDidChangeContent(
    _ controller: NSFetchedResultsController) {
    tableview.endUpdates()
    }

    View Slide

  38. 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.

    View Slide

  39. “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.

    View Slide

  40. 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.

    View Slide

  41. 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.

    View Slide

  42. → 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.

    View Slide

  43. iCloud

    View Slide

  44. ! Okay - Core Data on
    iCloud is Dead !

    View Slide

  45. → 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.

    View Slide

  46. References

    View Slide

  47. Core Data in
    Swift
    Marcus Zarra

    View Slide

  48. Objc.io Core
    Data
    Florian Kugler & Daniel Eggert

    View Slide

  49. 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.

    View Slide

  50. Thank You
    Abizer Nasir | @abizern | abizern.org

    View Slide