mirror of
https://github.com/kiwix/kiwix-apple.git
synced 2025-08-04 13:07:04 -04:00
115 lines
4.0 KiB
Swift
115 lines
4.0 KiB
Swift
// This file is part of Kiwix for iOS & macOS.
|
|
//
|
|
// Kiwix 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
|
|
// any later version.
|
|
//
|
|
// Kiwix 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 Kiwix; If not, see https://www.gnu.org/licenses/.
|
|
|
|
//
|
|
// ZimMigration.swift
|
|
// Kiwix
|
|
|
|
import Foundation
|
|
import CoreData
|
|
|
|
enum ZimMigration {
|
|
|
|
/// Holds the new zimfile host:
|
|
/// Set during migration,
|
|
/// and read back when updating URLS mapped from WebView interaction state,
|
|
/// witch is saved as Data for each opened Tab
|
|
@MainActor private static var newHost: String?
|
|
private static let sortDescriptors = [NSSortDescriptor(keyPath: \ZimFile.created, ascending: true)]
|
|
private static let requestLatestZimFile = ZimFile.fetchRequest(
|
|
predicate: ZimFile.Predicate.isDownloaded,
|
|
sortDescriptors: Self.sortDescriptors
|
|
)
|
|
|
|
static func forCustomApps() {
|
|
guard FeatureFlags.hasLibrary == false else { return }
|
|
Database.shared.performBackgroundTask { context in
|
|
guard var zimFiles = try? requestLatestZimFile.execute(),
|
|
zimFiles.count > 1,
|
|
let latest = zimFiles.popLast() else {
|
|
return
|
|
}
|
|
|
|
context.mergePolicy = NSMergePolicy.mergeByPropertyObjectTrump
|
|
for zimFile in zimFiles {
|
|
migrateFrom(zimFile: zimFile, toZimFile: latest, using: context)
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Migrates the bookmars from an old to new zim file,
|
|
/// also updates the bookmark urls accordingly (based on the new zim id as the host of those URLs)
|
|
/// deletes the old zim file in the DB
|
|
private static func migrateFrom(
|
|
zimFile fromZim: ZimFile,
|
|
toZimFile toZim: ZimFile,
|
|
using context: NSManagedObjectContext
|
|
) {
|
|
let newHost = toZim.fileID.uuidString
|
|
Task {
|
|
await MainActor.run {
|
|
Self.newHost = newHost
|
|
}
|
|
}
|
|
fromZim.bookmarks.forEach { (bookmark: Bookmark) in
|
|
bookmark.zimFile = toZim
|
|
if let newArticleURL = bookmark.articleURL.updateHost(to: newHost) {
|
|
bookmark.articleURL = newArticleURL
|
|
}
|
|
}
|
|
fromZim.tabs.forEach { (tab: Tab) in
|
|
tab.zimFile = toZim
|
|
tab.interactionState = tab.interactionState?.updateHost(to: newHost)
|
|
}
|
|
context.delete(fromZim)
|
|
if context.hasChanges { try? context.save() }
|
|
}
|
|
|
|
@MainActor
|
|
private static func latestZimFileHost() async -> String {
|
|
if let newHost = Self.newHost { return newHost }
|
|
// if it wasn't set before, set and return by the last ZimFile in DB:
|
|
guard let zimFile = try? Database.shared.viewContext.fetch(requestLatestZimFile).first else {
|
|
fatalError("we should have at least 1 zim file for a custom app")
|
|
}
|
|
let newHost = zimFile.fileID.uuidString
|
|
// save the new host for later
|
|
Self.newHost = newHost
|
|
return newHost
|
|
}
|
|
}
|
|
|
|
extension URL {
|
|
func updateHost(to newHost: String) -> URL? {
|
|
guard var components = URLComponents(url: self, resolvingAgainstBaseURL: false) else { return nil }
|
|
components.host = newHost
|
|
return components.url
|
|
}
|
|
}
|
|
|
|
extension Data {
|
|
func updateHost(to newHost: String) -> Data {
|
|
let string = String(decoding: self, as: UTF8.self)
|
|
if let replaced = try? string.replacingRegex(
|
|
matching: "kiwix:\\/\\/[A-Z0-9-]{0,36}\\/",
|
|
with: "kiwix://\(newHost)/"
|
|
) {
|
|
return Data(replaced.utf8)
|
|
} else {
|
|
return self
|
|
}
|
|
}
|
|
}
|