diff --git a/Tusker/CoreData/MastodonCachePersistentStore.swift b/Tusker/CoreData/MastodonCachePersistentStore.swift index 22150e96..a885d5bf 100644 --- a/Tusker/CoreData/MastodonCachePersistentStore.swift +++ b/Tusker/CoreData/MastodonCachePersistentStore.swift @@ -10,6 +10,9 @@ import Foundation import CoreData import Pachyderm import Combine +import OSLog + +fileprivate let logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "PersistentStore") class MastodonCachePersistentStore: NSPersistentContainer { @@ -49,7 +52,8 @@ class MastodonCachePersistentStore: NSPersistentContainer { loadPersistentStores { (description, error) in if let error = error { - fatalError("Unable to load persistent store: \(error)") + logger.error("Unable to load persistent store: \(String(describing: error), privacy: .public)") + fatalError("Unable to load persistent store") } } @@ -58,6 +62,18 @@ class MastodonCachePersistentStore: NSPersistentContainer { NotificationCenter.default.addObserver(self, selector: #selector(managedObjectsDidChange), name: .NSManagedObjectContextObjectsDidChange, object: viewContext) } + func save(context: NSManagedObjectContext) { + guard context.hasChanges else { + return + } + do { + try context.save() + } catch { + logger.error("Unable to save managed object context: \(String(describing: error), privacy: .public)") + fatalError("Unable to save managed object context") + } + } + func status(for id: String, in context: NSManagedObjectContext? = nil) -> StatusMO? { let context = context ?? viewContext let request: NSFetchRequest = StatusMO.fetchRequest() @@ -84,9 +100,7 @@ class MastodonCachePersistentStore: NSPersistentContainer { let context = context ?? backgroundContext context.perform { let statusMO = self.upsert(status: status, context: context) - if context.hasChanges { - try! context.save() - } + self.save(context: context) completion?(statusMO) self.statusSubject.send(status.id) } @@ -95,9 +109,7 @@ class MastodonCachePersistentStore: NSPersistentContainer { @MainActor func addOrUpdateOnViewContext(status: Status) -> StatusMO { let statusMO = self.upsert(status: status, context: viewContext) - if viewContext.hasChanges { - try! viewContext.save() - } + self.save(context: viewContext) statusSubject.send(status.id) return statusMO } @@ -105,9 +117,7 @@ class MastodonCachePersistentStore: NSPersistentContainer { func addAll(statuses: [Status], completion: (() -> Void)? = nil) { backgroundContext.perform { statuses.forEach { self.upsert(status: $0, context: self.backgroundContext) } - if self.backgroundContext.hasChanges { - try! self.backgroundContext.save() - } + self.save(context: self.backgroundContext) statuses.forEach { self.statusSubject.send($0.id) } completion?() } @@ -146,9 +156,7 @@ class MastodonCachePersistentStore: NSPersistentContainer { func addOrUpdate(account: Account, completion: ((AccountMO) -> Void)? = nil) { backgroundContext.perform { let accountMO = self.upsert(account: account) - if self.backgroundContext.hasChanges { - try! self.backgroundContext.save() - } + self.save(context: self.backgroundContext) completion?(accountMO) self.accountSubject.send(account.id) } @@ -180,9 +188,7 @@ class MastodonCachePersistentStore: NSPersistentContainer { func addOrUpdate(relationship: Relationship, completion: ((RelationshipMO) -> Void)? = nil) { backgroundContext.perform { let relationshipMO = self.upsert(relationship: relationship) - if self.backgroundContext.hasChanges { - try! self.backgroundContext.save() - } + self.save(context: self.backgroundContext) completion?(relationshipMO) self.relationshipSubject.send(relationship.id) } @@ -191,9 +197,7 @@ class MastodonCachePersistentStore: NSPersistentContainer { func addAll(accounts: [Account], completion: (() -> Void)? = nil) { backgroundContext.perform { accounts.forEach { self.upsert(account: $0) } - if self.backgroundContext.hasChanges { - try! self.backgroundContext.save() - } + self.save(context: self.backgroundContext) completion?() accounts.forEach { self.accountSubject.send($0.id) } } @@ -207,9 +211,7 @@ class MastodonCachePersistentStore: NSPersistentContainer { let accounts = notifications.filter { $0.kind != .mention }.map { $0.account } statuses.forEach { self.upsert(status: $0, context: self.backgroundContext) } accounts.forEach { self.upsert(account: $0) } - if self.backgroundContext.hasChanges { - try! self.backgroundContext.save() - } + self.save(context: self.backgroundContext) completion?() statuses.forEach { self.statusSubject.send($0.id) } accounts.forEach { self.accountSubject.send($0.id) } @@ -232,9 +234,7 @@ class MastodonCachePersistentStore: NSPersistentContainer { updatedAccounts.forEach(self.accountSubject.send) updatedStatuses.forEach(self.statusSubject.send) - if self.backgroundContext.hasChanges { - try! self.backgroundContext.save() - } + self.save(context: self.backgroundContext) completion?() } } diff --git a/Tusker/Screens/Timeline/HashtagTimelineViewController.swift b/Tusker/Screens/Timeline/HashtagTimelineViewController.swift index 823a1f80..d860c500 100644 --- a/Tusker/Screens/Timeline/HashtagTimelineViewController.swift +++ b/Tusker/Screens/Timeline/HashtagTimelineViewController.swift @@ -58,7 +58,7 @@ class HashtagTimelineViewController: TimelineTableViewController { } else { _ = SavedHashtag(hashtag: hashtag, context: context) } - try! context.save() + mastodonController.persistentContainer.save(context: context) } } diff --git a/Tusker/Screens/Timeline/InstanceTimelineViewController.swift b/Tusker/Screens/Timeline/InstanceTimelineViewController.swift index 3c475162..4c9dafbd 100644 --- a/Tusker/Screens/Timeline/InstanceTimelineViewController.swift +++ b/Tusker/Screens/Timeline/InstanceTimelineViewController.swift @@ -94,7 +94,7 @@ class InstanceTimelineViewController: TimelineTableViewController { _ = SavedInstance(url: instanceURL, context: context) delegate?.didSaveInstance(url: instanceURL) } - try? context.save() + mastodonController.persistentContainer.save(context: context) } } diff --git a/Tusker/Screens/Timeline/TimelineViewController.swift b/Tusker/Screens/Timeline/TimelineViewController.swift index 18912d00..58eb4c3a 100644 --- a/Tusker/Screens/Timeline/TimelineViewController.swift +++ b/Tusker/Screens/Timeline/TimelineViewController.swift @@ -144,6 +144,12 @@ class TimelineViewController: UIViewController, TimelineLikeCollectionViewContro } } + override func viewDidDisappear(_ animated: Bool) { + super.viewDidDisappear(animated) + + // TODO: prune offscreen rows + } + private func removeTimelineDescriptionCell() { var snapshot = dataSource.snapshot() snapshot.deleteSections([.header]) diff --git a/Tusker/Screens/Utilities/Previewing.swift b/Tusker/Screens/Utilities/Previewing.swift index 3dc73ff5..4ac58031 100644 --- a/Tusker/Screens/Utilities/Previewing.swift +++ b/Tusker/Screens/Utilities/Previewing.swift @@ -110,7 +110,7 @@ extension MenuActionProvider { } else { _ = SavedHashtag(hashtag: hashtag, context: context) } - try! context.save() + mastodonController.persistentContainer.save(context: context) }) ] } else { diff --git a/Tusker/Views/Poll/StatusPollView.swift b/Tusker/Views/Poll/StatusPollView.swift index fb24abd1..27c38817 100644 --- a/Tusker/Views/Poll/StatusPollView.swift +++ b/Tusker/Views/Poll/StatusPollView.swift @@ -170,9 +170,7 @@ class StatusPollView: UIView { return } status.poll = poll - if container.viewContext.hasChanges { - try! container.viewContext.save() - } + container.save(context: container.viewContext) container.statusSubject.send(status.id) } }