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

Multithreading, Core Data and Realm

Avatar for dziwiedz dziwiedz
February 28, 2019

Multithreading, Core Data and Realm

Everything becomes painful when we need to deal with multiple threads. How are Core Data and Realm dealing with them? What are the common mistakes? How not to lose your hair during the search of a bug, which can occur once in a milion runs? In my talk I will describe Core Data and Realm, how to deal with them, point out places where you can make a mistake and propose pattern which may be helpful in solving these problems.

Avatar for dziwiedz

dziwiedz

February 28, 2019
Tweet

Other Decks in Programming

Transcript

  1. MULTITHREADING, CORE DATA AND REALM AGENDA ▸ Problem ▸ Core

    Data ▸ Realm ▸ Multithreading ▸ Common mistakes ▸ Solution
  2. 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
  3. 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
  4. 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
  5. MULTITHREADING, CORE DATA AND REALM CORE DATA ▸ ORM (object-relational

    mapping) ▸ Developed by Apple ▸ Objective-C based ▸ Uses SQLite
  6. 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
  7. 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<Piza> { return NSFetchRequest<Pizza>(entityName: "Pizza") } @NSManaged public var address: String? @NSManaged public var comment: String? @NSManaged public var name: String? @NSManaged public var price: Double
  8. MULTITHREADING, CORE DATA AND REALM CORE DATA STACK MANAGED OBJECT

    MODEL MANAGED OBJECT CONTEXT PERSISTENT STORE COORDINATOR STORES DESCRIPTIONS
  9. 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 }
  10. 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)
  11. MULTITHREADING, CORE DATA AND REALM CONCURRENCY TYPES ▸ Main -

    associates context with Main Queue ▸ Private - associates context with Private Queue
  12. MULTITHREADING, CORE DATA AND REALM REALM ▸ Embedded Database ▸

    Developed by Realm ▸ Objective-C based ▸ Cross-platform support
  13. MULTITHREADING, CORE DATA AND REALM HOW REALM WORKS? ▸ Each

    model subclass from Object ▸ Auto syncing data ▸ Thread-confined ▸ Thread safe reference
  14. 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 */ }
  15. 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
  16. 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
  17. MULTITHREADING, CORE DATA AND REALM THREAD SAFE REFERENCE ▸ Must

    be initialised with thread-confined object ▸ Should be short lived ▸ Resolved at most once
  18. MULTITHREADING, CORE DATA AND REALM THREADING IN CORE DATA JSON

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

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

    MANAGED OBJECT Context Perform Block … PRIVATE CONTEXT Saving Contexts
  21. 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
  22. MULTITHREADING, CORE DATA AND REALM THREADING IN CORE DATA -

    PERSISTANCE CONTAINER JSON MANAGED OBJECT Persistsnce Store Perform Background Task PRIVATE CONTEXT Saving Contexts
  23. 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
  24. MULTITHREADING, CORE DATA AND REALM THREADING IN REALM - THREAD

    SAFE REFERENCE JSON OBJECT Realm Thread REALM Realm write THREAD SAFE REFERENCE
  25. 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
  26. 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) }
  27. MULTITHREADING, CORE DATA AND REALM PROTOCOL protocol DatabaseProtocol { func

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

    func create<T>(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<T>(_ 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 } }
  29. MULTITHREADING, CORE DATA AND REALM REALM - SAMPLE IMPLEMENTATION func

    create<T>(object: T) throws where T: RealmConvertible { try syncQueue.sync { do { try realm.write { realm.add(object.asRealm) } } } } func retrive<T>(_ 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 } } } }
  30. MULTITHREADING, CORE DATA AND REALM REPOSITORY ▸ Abstraction of a

    data layer ▸ Way of handling domain objects ▸ One repository for each Domain Object
  31. MULTITHREADING, CORE DATA AND REALM REPOSITORY PLATFORM BUSINESS LOGIC REPOSITORY

    MAPPER CRUD Domain Object Domain Object Platform Object Platform Object
  32. 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 }
  33. 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<Void> { return entity.sync(in: context) .mapToVoid() .flatMapLatest(context.rx.save) .subscribeOn(scheduler) } func delete(entity: T) -> Observable<Void> { return entity.sync(in: context) .map({$0 as! NSManagedObject}) .flatMapLatest(context.rx.delete) }
  34. 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