A taste of Core Data #1

5bfb729f6ac07e17c2326a7335a3f3be?s=47 yageek
December 19, 2017

A taste of Core Data #1

From the trenches - Code location: https://github.com/yageek/a-taste-of-core-data



December 19, 2017


  1. 2.

    What is CoreData? CoreData as a graph framework Add business

    semantics CoreData as a persistence framework Working with the data Plan
  2. 4.

    « Contrary to popular belief, Core Data is not an

    Object-Relational Mapper, but rather an object graph and persistence framework, capable of much more than the Active Record pattern alone is capable of. Using Core Data as an ORM necessarily limits the capabilities of Core Data and muddies its conceptual purity. But for many developers longing for the familiarity of an ORM, this trade-off is a deal at twice the price! » Mattt Thompson, @mattt, http://nshipster.com/core-data-libraries-and-utilities/ What is Core Data?
  3. 5.

    Why CoreData? •Creates a graph of object that you would

    eventually persist in. •The model part of your MVC/MVVM •To persist your data also •Help to synchronise with backend services •Simplify UI design.
  4. 6.

    Graph !!= ORM B#1 C#1 C#2 C#3 A#1 A#2 B#2

    B#3 ID Prop 1 Prop 2 Prop 3 #1 # # # #2 # # # #3 # # # #4 # # # A ID Prop 1 Prop 2 Prop 3 #1 # # # #2 # # # #3 # # # #4 # # # B ID Prop 1 Prop 2 Prop 3 #1 # # # #2 # # # #3 # # # #4 # # # C !!= Core Data ORM
  5. 7.

    •Using an ORM/Relational Database involves mostly writing SQL statements that

    returns Record (Pure Value elements) from a sets of tables. •Using a graph involves looking for object instances (node) from a specific graph instance. Graph !!= ORM
  6. 9.

    Graph semantics B#1 C#1 C#2 C#3 A#1 A#2 edge node

    graph instance B#1 C#1 C#2 C#3 A#1 A#2 B#2 B#3
  7. 10.

    Core Data vs Graph B#1 C#1 C#2 C#3 A#1 A#2

    edge = relationship node = NSManagedObject graph instance = NSManagedObjectContext B#1 C#1 C#2 C#3 A#1 A#2 B#2 B#3
  8. 13.

    Playground - Basic Initialisation Context is easy Let take a

    look at the documentation (concurrencyType will be explained in a next workshop): // Swift let context = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType) // Objective-C NSManagedObjectContext * ctx = [[NSManagedObjectContext alloc] initWithConcurrencyType: NSMainQueueConcurrencyType]; Using Code: Context initialisers
  9. 14.

    Playground - Basic Initialisation Object (node) ? How do we

    create object and create relationships? Documentation? ??? Object initialisers
  10. 17.

    Dealing with different nodes In reality we deals with more

    complex graph that have different kind of nodes (aka. nodes with different properties) and that have interactions. Example: Boss #1 name: Javier Employee #2 name: Damien speciality: iOS Boss #2 name: Mathieu Boss #3 name: Samy Employee #2 name: Alberto speciality: iOS Employee #2 name: Elaine speciality: test Employee #2 name: Khali speciality: test underBosses(*) employees (*) boss (1) bosses (*)
  11. 19.

    Playground - Multiple Descriptions Attributes and relationships usage // Attributes

    let nameAttr = NSAttributeDescription() nameAttr.name = "name" nameAttr.attributeType = .stringAttributeType nameAttr.defaultValue = "No Name" let specialityAttr = NSAttributeDescription() specialityAttr.name = "speciality" specialityAttr.attributeType = .stringAttributeType // Boss Description let bossDescription = NSEntityDescription() bossDescription.name = "Boss" // Employee Description let employeeDescription = NSEntityDescription() employeeDescription.name = "Employee" // Relationships let bossOneRel = NSRelationshipDescription() bossOneRel.name = "boss" bossOneRel.minCount = 0 bossOneRel.maxCount = 1 // To-One bossOneRel.destinationEntity = bossDescription let underBossesRel = NSRelationshipDescription() underBossesRel.name = "underBosses" underBossesRel.minCount = 0 underBossesRel.maxCount = 0 // To-Many underBossesRel.deleteRule = .nullifyDeleteRule underBossesRel.destinationEntity = bossDescription bossOneRel.inverseRelationship = underBossesRel underBossesRel.inverseRelationship = bossOneRel let bossesMulRel = NSRelationshipDescription() bossesMulRel.name = "bosses" bossesMulRel.minCount = 0 bossesMulRel.maxCount = 0 bossesMulRel.deleteRule = .nullifyDeleteRule bossesMulRel.destinationEntity = bossDescription let employeeMulRel = NSRelationshipDescription() employeeMulRel.name = "employees" employeeMulRel.minCount = 0 employeeMulRel.maxCount = 0 employeeMulRel.deleteRule = .nullifyDeleteRule employeeMulRel.destinationEntity = employeeDescription bossesMulRel.inverseRelationship = employeeMulRel employeeMulRel.inverseRelationship = bossesMulRel // Assign Relationships bossDescription.properties = [employeeMulRel, underBossesRel, bossOneRel, nameAttr]; employeeDescription.properties = [bossesMulRel, nameAttr, specialityAttr]; Alternatives to this boilerplate ? Keep track of evolution ?
  12. 20.

    Playground - Multiple Descriptions with Model Managed Model tool NSManagedObjectModel

    Using the model tool Compilation Model (.xcdatamodel) Compiled Model (.momd)
  13. 24.

    What have we created until now? NSManagedObject NSManagedObject NSManagedObject NSManagedObject

    Empty NSManagedObjects graph with default value How to modify the attributes and relation ships we declared?
  14. 25.

    /* Direct access to NSManagedObject private internal storage */ /*

    Does not triggers KVO updates */ - (void)setPrimitiveValue:(id)value forKey:(NSString *)key; - (id)primitiveValueForKey:(NSString *)key; /* Proxy methods to the upper ones */ /* Triggers KVO updates === KVC*/ - (id)valueForKey:(NSString *)key; - (void)setValue:(id)value forKey:(NSString *)key; - (NSMutableSet *)mutableSetValueForKey:(NSString *)key; Primitives to access elements Playground - Editing the node Every NSManagedObject is compatible with KVO/KVC
  15. 26.

    •Modifying the properties and relationships is really verbose? •I have

    no real « type » information, just plain NSManagedObjectContext? •Any alternatives? Looks like boilerplate again Remember the documentation?
  16. 27.

    •CoreData generates all properties and methods for you at runtime

    •KVC compliant (KVC programming guide -> Compliance Checklist) •@NSManaged looks like magic but it is not. Create subclasses! Playground - Editing the node - Subclasses @objc(Boss) public class Boss: NSManagedObject { @NSManaged var name: String? @NSManaged var bosses: Set<Boss>? @NSManaged var employees: Set<Employee> @NSManaged func addEmployeesObject(_ employee: Employee) } @objc(Employee) public class Employee: NSManagedObject { @NSManaged var name: String? @NSManaged var boss: Boss? }
  17. 28.

    We can create, add, remove methods to existing classes at

    runtime. Dynamic Objc properties or methods Playground - Editing the node - Recap
  18. 29.

    Operates at initialisation - initWithEntity:insertIntoManagedObjectContext: Discussion: NSManagedObject uses dynamic class

    generation to support the Objective-C 2 properties feature (see Declared Properties) by automatically creating a subclass of the class appropriate for entity. initWithEntity:insertIntoManagedObjectContext: therefore returns an instance of the appropriate class for entity. The dynamically-generated subclass will be based on the class specified by the entity, so specifying a custom class in your model will supersede the class passed to alloc. NSManagedObject documentation
  19. 30.

    You can use custom class as properties by specifying a

    transformable attribute and one NSValueTransformer class that will marshal/unmarshall your class into a CoreData primitive type. Custom properties If no transformer is specified, the value transformer specified NSKeyedUnarchiveFromDataTransformerName is used (See Core Data Release Notes). In this case y ou only need to make your custom type conformed to NSCoding
  20. 33.

    •Handling disk I/O •Validation of inputs •« Fine-tunable » regarding

    concurrency (Next session) •Performance (Next session) •Deal with migrations of data (Next session) What should persistence framework do?
  21. 34.

    An abstract class that handles persistence to disk that comes

    with two flavours: •NSAtomicStore: each write implies re-written all the data from all the graphs. •NSIncrementalStore: each write implies writing only the changes. NSPersistentStore Atomic Incremental iOS Binary In-Memory, SQLite macOS Binary, XML In-Memory, SQLite
  22. 35.

    You can implement you own store, but this topic is

    too complex for now. •NSAtomicStore: -> See Atomic Store Fundamentals •NSIncrementalStore: -> See Incremental Store Programming Guide and WWDC 201 1 Session 303 NSPersistentStore too deep for now
  23. 36.

    Who will take care about the constraints specified in our

    model and validate the inputs before persisting? How to deal/synchronise different graph instances (aka MOC)? How to deal with different data location (aka different NSPersistentStore)? Consistency of data
  24. 37.

    •MOC are bounded to it using the persistentStoreCoordinator •Validates changed

    and consistency according to the model provided at initialisation •Transmits changes from MOC to the stores once MOC called save() •Can Handle multiples NSPersistentStore instances NSPersistanceStoreCoordinator Playground - Store and Coordinators
  25. 38.

    CoreData view NSEntityDescription NSManagedObject NSPersistentStore NSPersistentStoreCoordinator Model #2 Context #3

    Store #1 Store #2 Store #3 Context #2 Context #1 Coordinator #1 Coordinator #2 Model #1
  26. 40.

    Identifying a set of data is done using an NSFetchRequest

    object. Request can be executed either on the context or on the store coordinator •entityName -> Which kind of object to return •predicate -> Only includes object matching the predicate •fetchLimit -> Limit the number of object to fetch •fetchOffset -> Skip object in the returned set •fetchBatchSize -> Lazy load the object in batch •sortDescriptors -> Sort the set according to this descriptors Identifying data Playground - Basic requests
  27. 42.

    If you have a graph with a huge number of

    elements and with a great depth, you probably do not want to load all the graph. By default, all children of a returned NSManagedObject are returned as Fault (lazy loaded object) CoreData will only load those children once you access to it. When you fetch in batch, the same principle is used. Faulting Playground - Faulting
  28. 44.

    More Information Core Data Programming Guide NSManagedObjectContext/Object/Model/Store documentation Core Data

    Release Notes Predicate Programming Guide NSExpression documentation WWDC 2010 128 - Mastering Core Data Core Data Second Edition - Marcus Zara Core Data - Florian Kugler/Daniel Eggert objc.io Issue 4 - Core Data https://www.objc.io/issues/4-core-data/