passepartout-apple/PassepartoutLibrary/Sources/PassepartoutUtils/Reusable/Persistence.swift

115 lines
4.2 KiB
Swift

//
// Persistence.swift
// Passepartout
//
// Created by Davide De Rosa on 3/14/22.
// Copyright (c) 2022 Davide De Rosa. All rights reserved.
//
// https://github.com/passepartoutvpn
//
// This file is part of Passepartout.
//
// Passepartout is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Passepartout is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
//
import Foundation
import CoreData
import Combine
public class Persistence {
private let container: NSPersistentContainer
public var context: NSManagedObjectContext {
container.viewContext
}
public var coordinator: NSPersistentStoreCoordinator {
container.persistentStoreCoordinator
}
public convenience init(withLocalName containerName: String, model: NSManagedObjectModel, author: String?) {
let container = NSPersistentContainer(name: containerName, managedObjectModel: model)
self.init(withContainer: container, author: author)
}
public convenience init(withCloudKitName containerName: String, model: NSManagedObjectModel, author: String?) {
let container = NSPersistentCloudKitContainer(name: containerName, managedObjectModel: model)
self.init(withContainer: container, author: author)
}
private init(withContainer container: NSPersistentContainer, author: String?) {
self.container = container
guard let desc = container.persistentStoreDescriptions.first else {
fatalError("Could not read persistent store description")
}
pp_log.debug("Container description: \(desc)")
// set this even for local container, to avoid readonly mode in case
// container was formerly created with CloudKit option
desc.setOption(true as NSNumber, forKey: NSPersistentHistoryTrackingKey)
// report remote notifications (do this BEFORE loadPersistentStores)
//
// https://stackoverflow.com/a/69507329/784615
// desc.setOption(true as NSNumber, forKey: NSPersistentStoreRemoteChangeNotificationPostOptionKey)
container.loadPersistentStores {
if let error = $1 {
fatalError("Could not load persistent store: \(error)")
}
}
container.viewContext.mergePolicy = NSMergePolicy.mergeByPropertyObjectTrump
container.viewContext.automaticallyMergesChangesFromParent = true
if let author = author {
pp_log.debug("Setting transaction author: \(author)")
container.viewContext.transactionAuthor = author
}
}
public var containerURLs: [URL]? {
guard let url = container.persistentStoreDescriptions.first?.url else {
return nil
}
return [
url,
url.deletingPathExtension().appendingPathExtension("sqlite-shm"),
url.deletingPathExtension().appendingPathExtension("sqlite-wal")
]
}
// public func remoteChangesPublisher() -> AnyPublisher<Void, Never> {
// NotificationCenter.default.publisher(
// for: .NSPersistentStoreRemoteChange,
// object: coordinator
// ).flatMap { _ in
// Just(())
// }.eraseToAnyPublisher()
// }
public func truncate() {
let coordinator = container.persistentStoreCoordinator
container.persistentStoreDescriptions.forEach {
do {
try $0.url.map {
try coordinator.destroyPersistentStore(at: $0, ofType: NSSQLiteStoreType)
try coordinator.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: $0, options: nil)
}
} catch {
pp_log.warning("Could not truncate persistent store: \(error)")
}
}
}
}