mirror of
https://github.com/kiwix/kiwix-apple.git
synced 2025-09-07 02:58:10 -04:00

* webview controller * ZimFilesOpened title * ZimFilesOpened * GridBasics * ZimFilesNew * GridCommon * ZimFilesDownloads * downloads * refactor * DownloadTaskCell * ZimFilesDownloads * CellBackground * zim file downloads * opened iconName * FlavorTag * grid sizing * ZimFileGrid * app icon * itunes file sharing * photo permission * mac about * About * about * app integration * LibrarySettings * Capsule * library setting * zim file backup setting * backup * ZimFileContextMenu * background fetch * update library * BackgroundTasks * library refresh * last refresh * LanguageSelector * language * LanguageSelector table * LibrarySettings macos * fetchLanguages * iOS LanguageSelector * sorting mode * library setting * ZimFilesNew * SettingSection * about * navigationTitle * background task identifier * setting * Settings * SettingSection * about macos * library version * language filter * rename * empty view * grid * ZimFileDetail * ZimFileDetailPanel * window sizing * opened zim file bottom * refresh * refresh * page zoom command * library * page zoom * split reader files * command and focus * focus and commands * open file in reader * frame * refactoring * refactor * languages * remove env object * refactor library view model * delete download task * downloads database op * service workers * service worker warning * hides service worker files * SearchFilter * url / UI * macos reader title * LibraryViewModel.reopen * remove search field * search focus * search keyboard shortcut * open multiple files * animations * refactor * search * sheet * SheetView library * sheet view style * zimfile list * reorg * style * LanguageSelector * ios setting * LibrarySettings * background task * delete alert * move * open in place, open main page * library refresh refactor * open bookmark throws * zim file missing * locate file * ZimFileMissingIndicator * predicates * observed zim file * macos build * icons * library refactor * menu refactor * refactor * html parser
112 lines
4.7 KiB
Swift
112 lines
4.7 KiB
Swift
//
|
|
// Database.swift
|
|
// Kiwix
|
|
//
|
|
// Created by Chris Li on 12/23/21.
|
|
// Copyright © 2022 Chris Li. All rights reserved.
|
|
//
|
|
|
|
import CoreData
|
|
|
|
class Database {
|
|
static let shared = Database()
|
|
private var notificationToken: NSObjectProtocol?
|
|
private var token: NSPersistentHistoryToken?
|
|
private var tokenURL = NSPersistentContainer.defaultDirectoryURL().appendingPathComponent("token.data")
|
|
|
|
private init() {
|
|
notificationToken = NotificationCenter.default.addObserver(
|
|
forName: .NSPersistentStoreRemoteChange, object: nil, queue: nil) { notification in
|
|
try? self.mergeChanges()
|
|
}
|
|
token = {
|
|
guard let data = UserDefaults.standard.data(forKey: "PersistentHistoryToken") else { return nil }
|
|
return try? NSKeyedUnarchiver.unarchivedObject(ofClass: NSPersistentHistoryToken.self, from: data)
|
|
}()
|
|
}
|
|
|
|
deinit {
|
|
if let token = notificationToken {
|
|
NotificationCenter.default.removeObserver(token)
|
|
}
|
|
}
|
|
|
|
/// A persistent container to set up the Core Data stack.
|
|
lazy var container: NSPersistentContainer = {
|
|
/// - Tag: persistentContainer
|
|
let container = NSPersistentContainer(name: "DataModel")
|
|
|
|
guard let description = container.persistentStoreDescriptions.first else {
|
|
fatalError("Failed to retrieve a persistent store description.")
|
|
}
|
|
|
|
description.setOption(true as NSNumber, forKey: NSPersistentStoreRemoteChangeNotificationPostOptionKey)
|
|
description.setOption(true as NSNumber, forKey: NSPersistentHistoryTrackingKey)
|
|
|
|
container.loadPersistentStores { storeDescription, error in
|
|
if let error = error as NSError? {
|
|
fatalError("Unresolved error \(error), \(error.userInfo)")
|
|
}
|
|
}
|
|
|
|
// This sample refreshes UI by consuming store changes via persistent history tracking.
|
|
/// - Tag: viewContextMergeParentChanges
|
|
container.viewContext.automaticallyMergesChangesFromParent = false
|
|
container.viewContext.name = "viewContext"
|
|
/// - Tag: viewContextMergePolicy
|
|
container.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
|
|
container.viewContext.undoManager = nil
|
|
container.viewContext.shouldDeleteInaccessibleFaults = true
|
|
return container
|
|
}()
|
|
|
|
/// Save image data to zim files.
|
|
func saveImageData(url: URL, completion: @escaping (Data) -> Void) {
|
|
URLSession.shared.dataTask(with: url) { data, response, error in
|
|
guard let response = response as? HTTPURLResponse,
|
|
response.statusCode == 200,
|
|
let mimeType = response.mimeType,
|
|
mimeType.contains("image"),
|
|
let data = data else { return }
|
|
let context = self.container.newBackgroundContext()
|
|
context.perform {
|
|
let predicate = NSPredicate(format: "faviconURL == %@", url as CVarArg)
|
|
let request = ZimFile.fetchRequest(predicate: predicate)
|
|
guard let zimFile = try? context.fetch(request).first else { return }
|
|
zimFile.faviconData = data
|
|
try? context.save()
|
|
}
|
|
completion(data)
|
|
}.resume()
|
|
}
|
|
|
|
/// Merge changes performed on batch requests to view context.
|
|
private func mergeChanges() throws {
|
|
let context = container.newBackgroundContext()
|
|
context.mergePolicy = NSMergePolicy.mergeByPropertyObjectTrump
|
|
context.undoManager = nil
|
|
context.perform {
|
|
// fetch and merge changes
|
|
let fetchRequest = NSPersistentHistoryChangeRequest.fetchHistory(after: self.token)
|
|
guard let result = try? context.execute(fetchRequest) as? NSPersistentHistoryResult,
|
|
let transactions = result.result as? [NSPersistentHistoryTransaction] else { return }
|
|
self.container.viewContext.perform {
|
|
transactions.forEach { transaction in
|
|
self.container.viewContext.mergeChanges(fromContextDidSave: transaction.objectIDNotification())
|
|
self.token = transaction.token
|
|
}
|
|
}
|
|
|
|
// update token
|
|
guard let token = transactions.last?.token else { return }
|
|
let data = try? NSKeyedArchiver.archivedData(withRootObject: token, requiringSecureCoding: true)
|
|
UserDefaults.standard.set(data, forKey: "PersistentHistoryToken")
|
|
|
|
// purge history
|
|
let sevenDaysAgo = Date(timeIntervalSinceNow: -3600 * 24 * 7)
|
|
let purgeRequest = NSPersistentHistoryChangeRequest.deleteHistory(before: sevenDaysAgo)
|
|
_ = try? context.execute(purgeRequest)
|
|
}
|
|
}
|
|
}
|