Slide 1

Slide 1 text

MULTITHREADING, CORE DATA AND REALM Poznań 28.02.2019 Łukasz Niedźwiedź Let It Swift #1

Slide 2

Slide 2 text

MULTITHREADING, CORE DATA AND REALM AGENDA ▸ Problem ▸ Core Data ▸ Realm ▸ Multithreading ▸ Common mistakes ▸ Solution

Slide 3

Slide 3 text

PROBLEM

Slide 4

Slide 4 text

MULTITHREADING, CORE DATA AND REALM SCENARIO ▸ We have been working all day long ▸ We are hungry ▸ It’s late ▸ We are ordering a delicious pizza right before it closes

Slide 5

Slide 5 text

MULTITHREADING, CORE DATA AND REALM RESULT ▸ We are waiting for pizza ▸ Pizzeria is closed ▸ We can’t contact with a delivery man or a restaurant ▸ We are starving ▸ We are unhappy ▸ We are not able to write a good quality code because we didn’t eat last night

Slide 6

Slide 6 text

MULTITHREADING, CORE DATA AND REALM WHAT HAPPENED? ▸ Application received push notification ▸ Application fetches latest orders from API with timestamp ▸ Core Data/Realm failed to save due to threading issue ▸ Delivery man doesn’t see our order ▸ It’s late so he is going home with our pizza

Slide 7

Slide 7 text

CORE DATA

Slide 8

Slide 8 text

MULTITHREADING, CORE DATA AND REALM CORE DATA ▸ ORM (object-relational mapping) ▸ Developed by Apple ▸ Objective-C based ▸ Uses SQLite

Slide 9

Slide 9 text

MULTITHREADING, CORE DATA AND REALM HOW CORE DATA WORKS ▸ Xcode editor tool that can generate model classes ▸ Core Data Stack ▸ Two concurrency types - Main and Private

Slide 10

Slide 10 text

MULTITHREADING, CORE DATA AND REALM MODEL CLASS EXAMPLE class Pizza: NSManagedObject { @NSManaged var name: String @NSManaged var price: Double @NSManaged var address: String @NSManaged var comment: String? } // Generated by XCode @nonobjc public class func fetchRequest() -> NSFetchRequest { return NSFetchRequest(entityName: "Pizza") } @NSManaged public var address: String? @NSManaged public var comment: String? @NSManaged public var name: String? @NSManaged public var price: Double

Slide 11

Slide 11 text

MULTITHREADING, CORE DATA AND REALM CORE DATA STACK MANAGED OBJECT MODEL MANAGED OBJECT CONTEXT PERSISTENT STORE COORDINATOR STORES DESCRIPTIONS

Slide 12

Slide 12 text

MULTITHREADING, CORE DATA AND REALM CORE DATA STACK - PERSISTENT STORE CONTAINER init(completionClosure: @escaping () -> ()) { persistentContainer = NSPersistentContainer(name: "DataModel") persistentContainer.loadPersistentStores() { _, error in if let error = error { // Data migration/ No space } completionClosure() } } var privateContext: NSManagedObjectContext { return persistentContainer.newBackgroundContext() } var viewContext: NSManagedObjectContext { return persistentContainer.viewContext }

Slide 13

Slide 13 text

MULTITHREADING, CORE DATA AND REALM CORE DATA STACK - PERSISTENT STORE COORDINATOR // [1] let bundle = Bundle(for: CoreDataStack.self) let url = bundle.url(forResource: "Model", withExtension: "momd")! let model = NSManagedObjectModel(contentsOf: url)! // [2] storeCoordinator = NSPersistentStoreCoordinator(managedObjectModel: model) context = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType) context.persistentStoreCoordinator = storeCoordinator // [3] let storeUrl = URL(fileURLWithPath: "StoreUrl.sqlit") try! storeCoordinator .addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: storeUrl, options: nil)

Slide 14

Slide 14 text

MULTITHREADING, CORE DATA AND REALM CONCURRENCY TYPES ▸ Main - associates context with Main Queue ▸ Private - associates context with Private Queue

Slide 15

Slide 15 text

REALM

Slide 16

Slide 16 text

MULTITHREADING, CORE DATA AND REALM REALM ▸ Embedded Database ▸ Developed by Realm ▸ Objective-C based ▸ Cross-platform support

Slide 17

Slide 17 text

MULTITHREADING, CORE DATA AND REALM HOW REALM WORKS? ▸ Each model subclass from Object ▸ Auto syncing data ▸ Thread-confined ▸ Thread safe reference

Slide 18

Slide 18 text

MULTITHREADING, CORE DATA AND REALM MODEL CLASS EXAMPLE final class RLMPizza: Object { @objc dynamic var name: String = "" @objc dynamic var price: Double = 0 @objc dynamic var address: String = "" @objc dynamic var comment: String? = nil /* Required inits */ }

Slide 19

Slide 19 text

MULTITHREADING, CORE DATA AND REALM REALM CONFIGURATION let realm = try! Realm()

Slide 20

Slide 20 text

MULTITHREADING, CORE DATA AND REALM AUTO-SYNCING DATA ▸ Refresh instances in every run-loop ▸ Posting notification about data update ▸ Notification are delivered asynchronously ▸ Convient implementation for observing

Slide 21

Slide 21 text

MULTITHREADING, CORE DATA AND REALM THREAD-CONFINEND ▸ Each Realm belongs to the thread on which was created ▸ Each Object belongs to realm thread ▸ Unmanaged Object behaves like NSObject

Slide 22

Slide 22 text

MULTITHREADING, CORE DATA AND REALM THREAD SAFE REFERENCE ▸ Must be initialised with thread-confined object ▸ Should be short lived ▸ Resolved at most once

Slide 23

Slide 23 text

MULTITHREADING

Slide 24

Slide 24 text

MULTITHREADING, CORE DATA AND REALM THREADING IN CORE DATA JSON CORE DATA

Slide 25

Slide 25 text

MULTITHREADING, CORE DATA AND REALM THREADING IN CORE DATA JSON MANAGED OBJECT CORE DATA

Slide 26

Slide 26 text

MULTITHREADING, CORE DATA AND REALM THREADING IN CORE DATA JSON MANAGED OBJECT PRIVATE CONTEXT Saving Contexts

Slide 27

Slide 27 text

MULTITHREADING, CORE DATA AND REALM THREADING IN CORE DATA JSON MANAGED OBJECT Context Perform Block PRIVATE CONTEXT Saving Contexts

Slide 28

Slide 28 text

MULTITHREADING, CORE DATA AND REALM THREADING IN CORE DATA JSON MANAGED OBJECT Context Perform Block … PRIVATE CONTEXT Saving Contexts

Slide 29

Slide 29 text

MULTITHREADING, CORE DATA AND REALM THREADING IN CORE DATA JSON MANAGED OBJECT Context Perform Block … PRIVATE CONTEXT Saving Contexts Main context Perform And Wait Block MAIN CONTEXT

Slide 30

Slide 30 text

MULTITHREADING, CORE DATA AND REALM THREADING IN CORE DATA - PERSISTANCE CONTAINER JSON MANAGED OBJECT Persistsnce Store Perform Background Task PRIVATE CONTEXT Saving Contexts

Slide 31

Slide 31 text

MULTITHREADING, CORE DATA AND REALM THREADING IN CORE DATA - PASSING OBJECT JSON MANAGED OBJECT Persistsnce Store Perform Background Task PRIVATE CONTEXT Saving Contexts MANAGED OBJECT ID

Slide 32

Slide 32 text

MULTITHREADING, CORE DATA AND REALM THREADING IN REALM JSON REALM

Slide 33

Slide 33 text

MULTITHREADING, CORE DATA AND REALM THREADING IN REALM JSON OBJECT REALM

Slide 34

Slide 34 text

MULTITHREADING, CORE DATA AND REALM THREADING IN REALM JSON OBJECT REALM Realm write

Slide 35

Slide 35 text

MULTITHREADING, CORE DATA AND REALM THREADING IN REALM JSON OBJECT Realm Thread REALM Realm write

Slide 36

Slide 36 text

MULTITHREADING, CORE DATA AND REALM THREADING IN REALM - THREAD SAFE REFERENCE JSON OBJECT Realm Thread REALM Realm write THREAD SAFE REFERENCE

Slide 37

Slide 37 text

MISTAKES

Slide 38

Slide 38 text

MULTITHREADING, CORE DATA AND REALM COMMON MISTAKES ▸ Accessing realm object on wrong thread ▸ Accessing realm/managed object context on wrong thread ▸ Passing realm/managed object context among thread ▸ Wrongly configured Core Data Stack ▸ Race condition ▸ Third party frameworks

Slide 39

Slide 39 text

SOLUTION

Slide 40

Slide 40 text

MULTITHREADING, CORE DATA AND REALM DOMAIN - PLATFORM LAYERS CORE DATA / REALM OBJECT DOMAIN OBJECT

Slide 41

Slide 41 text

MULTITHREADING, CORE DATA AND REALM DOMAIN - PLATFORM LAYERS protocol DomainConvertible { associatedtype DomainType var asDomain: DomainType { get } } protocol RealmConvertible { associatedtype RealmType: Object var uid: String { get } var asRealm: RealmType { get } } protocol CoreDataConvertible { associatedtype CoreDataType: NSManagedObject var uid: String { get } func sync(in context: NSManagedObjectContext) }

Slide 42

Slide 42 text

MULTITHREADING, CORE DATA AND REALM PROTOCOL protocol DatabaseProtocol { func create(object: T) throws where T: PlatformConvertible func retrive(_ type: T.Type, predicate: NSPredicate?) -> [T] where T: PlatformConvertible, T.PlatformType: DomainConvertible, T.PlatformType.DomainType == T func update(objects: T) throws where T: PlatformConvertible func delete(object: T) throws where T: PlatformConvertible }

Slide 43

Slide 43 text

MULTITHREADING, CORE DATA AND REALM CORE DATA - SAMPLE IMPLEMENTATION func create(object: T) where T: CoreDataConvertible { context.perform { [unowned self] in let entity = T.CoreDataType.entity() let object = NSManagedObject(entity: entity, insertInto: self.context) as! T object.sync(in: self.context) self.context.saveWithParents() } } func retrive(_ type: T.Type, predicate: NSPredicate?) -> [T] where T: CoreDataConvertible, T == T.CoreDataType.DomainType, T.CoreDataType: DomainConvertible { let request = T.CoreDataType.fetchRequest() request.predicate = predicate var result: [T.CoreDataType]! context.performAndWait { [unowned self] in result = try! self.context.fetch(request) as! [T.CoreDataType] } return result.compactMap { $0.asDomain } }

Slide 44

Slide 44 text

MULTITHREADING, CORE DATA AND REALM REALM - SAMPLE IMPLEMENTATION func create(object: T) throws where T: RealmConvertible { try syncQueue.sync { do { try realm.write { realm.add(object.asRealm) } } } } func retrive(_ type: T.Type, predicate: NSPredicate) -> [T] where T: RealmConvertible, T == T.RealmType.DomainType, T.RealmType: DomainConvertible { return syncQueue.sync { do { return realm .objects(type.RealmType.self) .map { $0.asDomain } } } }

Slide 45

Slide 45 text

MULTITHREADING, CORE DATA AND REALM REPOSITORY ▸ Abstraction of a data layer ▸ Way of handling domain objects ▸ One repository for each Domain Object

Slide 46

Slide 46 text

MULTITHREADING, CORE DATA AND REALM REPOSITORY PLATFORM BUSINESS LOGIC REPOSITORY MAPPER CRUD Domain Object Domain Object Platform Object Platform Object

Slide 47

Slide 47 text

MULTITHREADING, CORE DATA AND REALM REPOSITORY - PROTOCOL protocol AbstractRepository { associatedtype T func query(with predicate: NSPredicate?, sortDescriptors: [NSSortDescriptor]?) -> [T] func save(entity: T) throws func delete(entity: T) throws }

Slide 48

Slide 48 text

MULTITHREADING, CORE DATA AND REALM REPOSITORY - IMPLEMENTATION func query(with predicate: NSPredicate? = nil, sortDescriptors: [NSSortDescriptor]? = nil) -> Observable<[T]> { let request = T.CoreDataType.fetchRequest() request.predicate = predicate request.sortDescriptors = sortDescriptors return context.rx.entities(fetchRequest: request) .mapToDomain() .subscribeOn(scheduler) } func save(entity: T) -> Observable { return entity.sync(in: context) .mapToVoid() .flatMapLatest(context.rx.save) .subscribeOn(scheduler) } func delete(entity: T) -> Observable { return entity.sync(in: context) .map({$0 as! NSManagedObject}) .flatMapLatest(context.rx.delete) }

Slide 49

Slide 49 text

MULTITHREADING, CORE DATA AND REALM SOURCES ▸ https://developer.apple.com/documentation/coredata - Apple Documentation ▸ https://realm.io/docs/swift/latest/ - Realm Documentation ▸ https://github.com/sergdort/CleanArchitectureRxSwift - Rx Swift Clean Architecture open source project ▸ https://github.com/objcio/core-data - Core Data by objc.io

Slide 50

Slide 50 text

THANKS FOR LISTENING

Slide 51

Slide 51 text

QUESTIONS ?