mirror of
https://github.com/kiwix/kiwix-apple.git
synced 2025-08-05 05:27:09 -04:00

* setup * RootView setup * open url * ReadingView * ReadingView * webview delegate * webview outlint * sidebar * buttons * bar buttons * reader view model * reader view model * ReadingView iOS * NavigationTitleSubtitle * map * introspect * content group * setting navigation * WebViewCoordinator * webview state * outline * root view iOS macos * ios buttons * OutlineMenu * OutlineMenu * outlint tree * webview gesture * bookmark button * merge notification * BookmarkMultiButton * ArticleCell * ArticleCell * data model * iOS bookmarks * bookmark loading * search * RandomArticle * MainArticleMenuButton * MoreActionMenu * swiftui4 * BarSetupModifier * RootView_SwiftUI4 * NavigationItem * purge & renaming * bookmarks * welcome no content * welcome no content * file importer * macos library hookup * library iOS setup * Library * Library setup * buttons * library setup * CategoryList * predicates * grid * settings view * RootView_SwiftUI4 * RootView * root view * RootView_iOS * ios reading view model * rootview * onchange * ios bars * reading view iOS 16 button * bookmark sort * mvoe * project setting * focus * NavigationItemButtons * patches * PageZoomButtons * FocusedSceneValue modifier * pagezoom observer * search view * SearchView * dismiss search * min detail view size * search result * SearchView * resrch * dismiss search on url change * search view * ios search setup * search * macos search * ios search * remove old search * use searchbar * searching * ios memory leak * WebViewConfiguration * macOS webview * refactor * SearchBar setup * search bar setup * SearchBar setup * searchbar * ReadingView * webview ref * outline * sheet * toolbar title outline * compact reading view * settings * recent search * reading view * reading view * SearchView * several small changes * shortcuts * commands buttons * macos command * navigation action * patch * revert * webview retention * navigation focused scene value * ipad keyboard shortcut * macos app min fheight * ios sheet action * macOS 13 build * purge * move * move * indent * settings * file import * rename * GridCommon * library views * purge * remove LibraryTopic * LibraryView_iOS * move * coredata context * add url binding * ZimFileSelection * pass url down * ZimFileDetail * load main page * revert * macos 12 compile * reading view empty title * view model * onboarding view * root view iOS 16 * ios search * root view * OutlineTree * search overlay * library view * library refresh views * view model * view model * LibraryViewModel * zim file detail * zim file detail styling * zim file detail * library detail & refresh * ZimFileContextMenu * directory monitor * LibraryOperations * LibraryOperations * reopen * migration * zim file migration * bookmark migration * comments * open zim file via bookmark * welcome view * hide bookmark section when empty * bookmark context menu setup * bookmark add / delete view model * skip notification * refactor view modifier * remove old code * url loading * refactor navigation delegate * safari sheet * safari view sheet * iOS font size * max width welcome view * onboarding view * library view * FileImportButton * FileImportButton * dismiss sheet when loading main page * settings misc * category auto refresh * ios app version
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.performAndWait {
|
|
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)
|
|
}
|
|
}
|
|
}
|