Slide 1

Slide 1 text

USING CORE DATA IN SWIFT JESSE SQUIRES JESSESQUIRES.COM • @JESSE_SQUIRES • GITHUB/JESSESQUIRES

Slide 2

Slide 2 text

WHAT IS CORE DATA?

Slide 3

Slide 3 text

The Core Data framework provides generalized and automated solutions to common tasks associated with object life-cycle and object graph management, including persistence. — Core Data Programming Guide

Slide 4

Slide 4 text

Core Data is backed by a SQLite database. However, it is not a relational database or RDBMS.

Slide 5

Slide 5 text

BACKED BY SQLITE. NOT A DATABASE?

Slide 6

Slide 6 text

CORE DATA MANAGES: LIFE-CYCLE GRAPH PERSISTENCE SEARCHING OF OBJECTS

Slide 7

Slide 7 text

CORE DATA STACK: Managed objects NSMangedObject Managed object context NSManagedObjectContext Persistent Store Coordinator NSPersistentStoreCoordinator Persistent Store NSPersistentStore SQLite

Slide 8

Slide 8 text

WHY USE CORE DATA? ▸ Provides featues you need ▸ "Mature, unit tested, optimized" ▸ Part of iOS and OS X toolchain ▸ Apple continues to invest heavily in it ▸ Popular, tons of resources online

Slide 9

Slide 9 text

WHY USE SWIFT? ▸ Clarity ▸ Type-safety ▸ Swift-only features ▸ Functional paradigms

Slide 10

Slide 10 text

SWIFT + CORE DATA

Slide 11

Slide 11 text

WARNING: TOOLS ARE IMMATURE SourceKitService Terminated Editor functionality temporarily limited.

Slide 12

Slide 12 text

STANDING UP THE CORE DATA STACK Same boilerplate code as Objective-C Better in Swift

Slide 13

Slide 13 text

CORE DATA STACK struct CoreDataModel { let name: String let bundle: NSBundle init(name: String, bundle: NSBundle) // other properties & methods }

Slide 14

Slide 14 text

CORE DATA STACK class CoreDataStack { let model: CoreDataModel let managedObjectContext: NSManagedObjectContext let persistentStoreCoordinator: NSPersistentStoreCoordinator init(model: CoreDataModel, storeType: String, concurrencyType: NSManagedObjectContextConcurrencyType) // other properties and methods }

Slide 15

Slide 15 text

CORE DATA STACK let model = CoreDataModel(name: "MyModel", bundle: myBundle) let stack = CoreDataStack(model: model, storeType: NSSQLiteStoreType, concurrencyType: .MainQueueConcurrencyType) // Use context stack.managedObjectContext

Slide 16

Slide 16 text

CORE DATA STACK AppDelegate.m DO NOT

Slide 17

Slide 17 text

USE FRAMEWORKS Clear model namespace, Modular, Reusable, Unit Testing

Slide 18

Slide 18 text

CREATING MANAGED OBJECTS ▸ Xcode generated classes are terrible ▸ mogenerator Swift support still experimental (Last release Sept 2014)

Slide 19

Slide 19 text

CREATING MANAGED OBJECTS VISUAL MODEL EDITOR

Slide 20

Slide 20 text

CREATING MANAGED OBJECTS ATTRIBUTE VALIDATION

Slide 21

Slide 21 text

OBJECTIVE-C @interface Employee : NSManagedObject @property (nonatomic, retain) NSString * address; @property (nonatomic, retain) NSDate * dateOfBirth; @property (nonatomic, retain) NSString * email; @property (nonatomic, retain) NSString * name; @property (nonatomic, retain) NSDecimalNumber * salary; @property (nonatomic, retain) NSNumber * status; @end

Slide 22

Slide 22 text

SWIFT class Employee: NSManagedObject { @NSManaged var address: String? @NSManaged var dateOfBirth: NSDate @NSManaged var email: String? @NSManaged var name: String @NSManaged var salary: NSDecimalNumber @NSManaged var status: Int32 }

Slide 23

Slide 23 text

OPTIONALS Xcode will generate String instead of String? @property (nonatomic, retain) NSString * address; @NSManaged var address: String?

Slide 24

Slide 24 text

CREATING MANAGED OBJECTS PREFIXED SUBCLASSES NSManagedObject . Swift namespaces

Slide 25

Slide 25 text

Xcode does not prefix classes automatically Must add prefix manually after generating classes No prefix means runtime crash, obscure errors

Slide 26

Slide 26 text

INSTANTIATING MANAGED OBJECTS Reduce boilerplate, generalize NSEntityDescription

Slide 27

Slide 27 text

THE OBJECTIVE-C WAY // "Person" NSString *name = [Person entityName]; @implementation NSManagedObject (Helpers) + (NSString *)entityName { return NSStringFromClass([self class]); } @end

Slide 28

Slide 28 text

THE OBJECTIVE-C WAY // Create new person Person *person = [Person insertNewObjectInContext:context]; @implementation NSManagedObject (Helpers) + (instancetype)insertNewObjectInContext:(NSManagedObjectContext *)context { return [NSEntityDescription insertNewObjectForEntityForName:[self entityName] inManagedObjectContext:context]; } @end

Slide 29

Slide 29 text

THE SWIFT WAY? // "MyApp.Person" let fullName = NSStringFromClass(object_getClass(self)) extension NSManagedObject { class func entityName() -> String { let fullClassName = NSStringFromClass(object_getClass(self)) let nameComponents = split(fullClassName) { $0 == "." } return last(nameComponents)! } } // "Person" let entityName = Person.entityName()

Slide 30

Slide 30 text

THE SWIFT WAY? // Create new person let person = Person(context: context) extension NSManagedObject { convenience init(context: NSManagedObjectContext) { let name = self.dynamicType.entityName() let entity = NSEntityDescription.entityForName(name, inManagedObjectContext: context)! self.init(entity: entity, insertIntoManagedObjectContext: context) } }

Slide 31

Slide 31 text

THE SWIFT WAY? class Employee: NSManagedObject { init(context: NSManagedObjectContext) { let entity = NSEntityDescription.entityForName("Employee", inManagedObjectContext: context)! super.init(entity: entity, insertIntoManagedObjectContext: context) } }

Slide 32

Slide 32 text

NOT VERY SWIFT "OBJECTIVE-C WITH A NEW SYNTAX"

Slide 33

Slide 33 text

THE OBJECTIVE-C WAY IS NOT ALWAYS THE SWIFT WAY

Slide 34

Slide 34 text

EMBRACE THE SWIFTNESS

Slide 35

Slide 35 text

SWIFT DESIGNATED INITIALIZERS

Slide 36

Slide 36 text

1. Stored properties must be assigned initial value 2. Designated init fully initializes all properties 3. Convenience init are secondary 4. Convenience init must call designated init 5. Superclass initializers not inherited in subclasses by default

Slide 37

Slide 37 text

DESIGNATED INITIALIZERS? // designated init init(entity:insertIntoManagedObjectContext:) // our convenience init convenience init(context:)

Slide 38

Slide 38 text

CORE DATA BYPASSES INITIALIZATION RULES @NSManaged

Slide 39

Slide 39 text

class Employee: NSManagedObject { init(context: NSManagedObjectContext, name: String, dateOfBirth: NSDate, salary: NSDecimalNumber, employeeId: String = NSUUID().UUIDString, email: String? = nil, address: String? = nil) { // init } }

Slide 40

Slide 40 text

typealias

Slide 41

Slide 41 text

TYPEALIAS typealias EmployeeId = String class Employee: NSManagedObject { @NSManaged var employeeId: EmployeeId } // Example let id: EmployeeId = "12345"

Slide 42

Slide 42 text

RELATIONSHIPS NSSet Set

Slide 43

Slide 43 text

Set Nope

Slide 44

Slide 44 text

enum

Slide 45

Slide 45 text

ENUM enum Genre: String { case BlackMetal = "Black Metal" case DeathMetal = "Death Metal" case DoomMetal = "Doom Metal" case FolkMetal = "Folk Metal" case Grindcore = "Grindcore" case Hardcore = "Hardcore" case CrustPunk = "Crust Punk" case StreetPunk = "Street Punk" case Thrash = "Thrash" }

Slide 46

Slide 46 text

ENUM public class Band: NSManagedObject { @NSManaged private var genreValue: String public var genre: Genre { get { return Genre(rawValue: self.genreValue)! } set { self.genreValue = newValue.rawValue } } }

Slide 47

Slide 47 text

ENUM // old band.genre = "Black Metal" // new band.genre = .BlackMetal

Slide 48

Slide 48 text

ENUM Unfortunately, must use private property for fetch requests let fetch = NSFetchRequest(entityName: "Band") fetch.predicate = NSPredicate(format: "genreValue == %@", genre)

Slide 49

Slide 49 text

FUNCTIONAL PARADIGMS WITH MICRO-LIBRARIES

Slide 50

Slide 50 text

SAVING var error: NSError? let success: Bool = managedObjectContext.save(&error) // handle success or error

Slide 51

Slide 51 text

SAVING func saveContext(context:) -> (success: Bool, error: NSError?) // Example let result = saveContext(context) if !result.success { println("Error: \(result.error)") }

Slide 52

Slide 52 text

FETCH REQUESTS var error: NSError? var results = context.executeFetchRequest(request, error: &error) // [AnyObject]? if results == nil { println("Error = \(error)") }

Slide 53

Slide 53 text

FETCH REQUESTS // T is a phantom type class FetchRequest : NSFetchRequest { init(entity: NSEntityDescription) { super.init() self.entity = entity } }

Slide 54

Slide 54 text

FETCH REQUESTS typealias FetchResult = (success: Bool, objects: [T], error: NSError?) func fetch (request: FetchRequest, context: NSManagedObjectContext) -> FetchResult

Slide 55

Slide 55 text

FETCH REQUESTS typealias FetchResult = (success: Bool, objects: [T], error: NSError?) func fetch (request: FetchRequest, context: NSManagedObjectContext) -> FetchResult { var error: NSError? if let results = context.executeFetchRequest(request, error: &error) { return (true, results as! [T], error) } return (false, [], error) }

Slide 56

Slide 56 text

FETCH REQUESTS // Example let request = FetchRequest(entity: entityDescription) let results = fetch(request: request, inContext: context) if !results.success { println("Error = \(results.error)") } results.objects // [Band]

Slide 57

Slide 57 text

SLIGHTLY LESS TERRIBLE?

Slide 58

Slide 58 text

CLARITY OPTIONALS, ENUMS, TYPEALIAS, DESIGNATED INIT

Slide 59

Slide 59 text

SAFETY TYPES, GENERICS, DESIGNATED INIT

Slide 60

Slide 60 text

SWIFTNESS ENUMS, TYPEALIAS, OPTIONALS

Slide 61

Slide 61 text

FUNCTIONAL SAVING, FETCHING, MORE

Slide 62

Slide 62 text

github.com/jessesquires/JSQCoreDataKit

Slide 63

Slide 63 text

Thank you!

Slide 64

Slide 64 text

QUESTIONS? JESSESQUIRES.COM • @JESSE_SQUIRES • GITHUB/JESSESQUIRES