// // MastodonCachePersistentStore.swift // Tusker // // Created by Shadowfacts on 4/11/20. // Copyright © 2020 Shadowfacts. All rights reserved. // import Foundation import CoreData import Pachyderm import Combine class MastodonCachePersistentStore: NSPersistentContainer { private(set) lazy var backgroundContext = newBackgroundContext() let statusSubject = PassthroughSubject() let accountSubject = PassthroughSubject() init(for controller: MastodonController) { let url = Bundle.main.url(forResource: "Tusker", withExtension: "momd")! let model = NSManagedObjectModel(contentsOf: url)! super.init(name: "\(controller.accountInfo!.id)_cache", managedObjectModel: model) loadPersistentStores { (description, error) in if let error = error { fatalError("Unable to load persistent store: \(error)") } } } func status(for id: String, in context: NSManagedObjectContext? = nil) -> StatusMO? { let context = context ?? viewContext let request: NSFetchRequest = StatusMO.fetchRequest() request.predicate = NSPredicate(format: "id = %@", id) request.fetchLimit = 1 if let result = try? context.fetch(request), let status = result.first { return status } else { return nil } } @discardableResult private func upsert(status: Status, incrementReferenceCount: Bool) -> StatusMO { if let statusMO = self.status(for: status.id, in: self.backgroundContext) { statusMO.updateFrom(apiStatus: status, container: self) if incrementReferenceCount { statusMO.incrementReferenceCount() } return statusMO } else { let statusMO = StatusMO(apiStatus: status, container: self, context: self.backgroundContext) if incrementReferenceCount { statusMO.incrementReferenceCount() } return statusMO } } func addOrUpdate(status: Status, incrementReferenceCount: Bool, completion: ((StatusMO) -> Void)? = nil) { backgroundContext.perform { let statusMO = self.upsert(status: status, incrementReferenceCount: incrementReferenceCount) if self.backgroundContext.hasChanges { try! self.backgroundContext.save() } completion?(statusMO) self.statusSubject.send(status.id) } } func addAll(statuses: [Status], completion: (() -> Void)? = nil) { backgroundContext.perform { statuses.forEach { self.upsert(status: $0, incrementReferenceCount: true) } if self.backgroundContext.hasChanges { try! self.backgroundContext.save() } completion?() statuses.forEach { self.statusSubject.send($0.id) } } } func account(for id: String, in context: NSManagedObjectContext? = nil) -> AccountMO? { let context = context ?? viewContext let request: NSFetchRequest = AccountMO.fetchRequest() request.predicate = NSPredicate(format: "id = %@", id) request.fetchLimit = 1 if let result = try? context.fetch(request), let account = result.first { return account } else { return nil } } @discardableResult private func upsert(account: Account) -> AccountMO { if let accountMO = self.account(for: account.id, in: self.backgroundContext) { accountMO.updateFrom(apiAccount: account, container: self) return accountMO } else { return AccountMO(apiAccount: account, container: self, context: self.backgroundContext) } } func addOrUpdate(account: Account, completion: ((AccountMO) -> Void)? = nil) { backgroundContext.perform { let accountMO = self.upsert(account: account) if self.backgroundContext.hasChanges { try! self.backgroundContext.save() } completion?(accountMO) self.accountSubject.send(account.id) } } func addAll(accounts: [Account], completion: (() -> Void)? = nil) { backgroundContext.perform { accounts.forEach { self.upsert(account: $0) } if self.backgroundContext.hasChanges { try! self.backgroundContext.save() } completion?() accounts.forEach { self.accountSubject.send($0.id) } } } func addAll(notifications: [Pachyderm.Notification], completion: (() -> Void)? = nil) { backgroundContext.perform { let statuses = notifications.compactMap { $0.status } let accounts = notifications.map { $0.account } statuses.forEach { self.upsert(status: $0, incrementReferenceCount: true) } accounts.forEach { self.upsert(account: $0) } if self.backgroundContext.hasChanges { try! self.backgroundContext.save() } completion?() statuses.forEach { self.statusSubject.send($0.id) } accounts.forEach { self.accountSubject.send($0.id) } } } }