Skip to content

Commit

Permalink
Migrate scheme from kiwix to zim
Browse files Browse the repository at this point in the history
  • Loading branch information
BPerlakiH committed Sep 1, 2024
1 parent f09e421 commit 94177fc
Show file tree
Hide file tree
Showing 11 changed files with 98 additions and 20 deletions.
9 changes: 8 additions & 1 deletion App/App_iOS.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,13 @@ struct Kiwix: App {
init() {
fileMonitor = DirectoryMonitor(url: URL.documentDirectory) { LibraryOperations.scanDirectory($0) }
UNUserNotificationCenter.current().delegate = appDelegate
// MARK: - migrations
if !ProcessInfo.processInfo.arguments.contains("testing") {
let migrations = MigrationService(migrations: [
Migrations.schemeToZIM(using: Database.shared.viewContext)
])
_ = migrations.migrateAll()
}
}

var body: some Scene {
Expand Down Expand Up @@ -59,7 +66,7 @@ struct Kiwix: App {
.onOpenURL { url in
if url.isFileURL {
NotificationCenter.openFiles([url], context: .file)
} else if url.isKiwixURL {
} else if url.isZIMURL {
NotificationCenter.openURL(url)
}
}
Expand Down
9 changes: 8 additions & 1 deletion App/App_macOS.swift
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ struct RootView: View {
.onOpenURL { url in
if url.isFileURL {
NotificationCenter.openFiles([url], context: .file)
} else if url.isKiwixURL {
} else if url.isZIMURL {
NotificationCenter.openURL(url)
}
}
Expand Down Expand Up @@ -227,6 +227,13 @@ struct RootView: View {
navigation.currentItem = .reading
}
}
// MARK: - migrations
if !ProcessInfo.processInfo.arguments.contains("testing") {
let migrations = MigrationService(migrations: [
Migrations.schemeToZIM(using: Database.shared.viewContext)
])
_ = migrations.migrateAll()
}
}
.withHostingWindow { [windowTracker] hostWindow in
windowTracker.current = hostWindow
Expand Down
2 changes: 1 addition & 1 deletion Model/Entities/SearchResult.m
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ - (instancetype)initWithZimFileID:(NSUUID *)zimFileID path:(NSString *)path titl
if (![path hasPrefix:@"/"]) { path = [@"/" stringByAppendingString:path]; }

NSURLComponents *components = [[NSURLComponents alloc] init];
components.scheme = @"kiwix";
components.scheme = @"zim";
components.host = [zimFileID UUIDString];
components.path = path;
self.url = [components URL];
Expand Down
39 changes: 39 additions & 0 deletions Model/Migration/MigrateSchemeToZIM.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// 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/.

import Foundation
import CoreData

enum Migrations {

/// Change the bookmarks articleURLs from "kiwix://..." to "zim://..."
/// - Parameter context: DataBase context
/// - Returns: Migration - general struct
static func schemeToZIM(using context: NSManagedObjectContext) -> Migration {
Migration(userDefaultsKey: "migrate_scheme_to_zim") {
// bookmarks:
let bookmarkPredicate = NSPredicate(format: "articleURL BEGINSWITH[cd] %@", "kiwix://")
let bookmarkRequest = Bookmark.fetchRequest(predicate: bookmarkPredicate)
let bookmarks: [Bookmark] = (try? context.fetch(bookmarkRequest)) ?? []
for bookmark in bookmarks {
bookmark.articleURL = bookmark.articleURL.updatedToZIMSheme()
}
if context.hasChanges {
try? context.save()
}
return true
}
}
}
18 changes: 17 additions & 1 deletion Model/Utilities/URL.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,16 @@
import Foundation

enum URLSchemeType {
case zim
case kiwix
case geo
case unsupported

init(scheme: String?) {
if scheme?.caseInsensitiveCompare("zim") == .orderedSame {
self = .zim
return
}
if scheme?.caseInsensitiveCompare("kiwix") == .orderedSame {
self = .kiwix
return
Expand All @@ -35,7 +40,7 @@ enum URLSchemeType {

extension URL {
init?(zimFileID: String, contentPath: String) {
let baseURLString = "kiwix://" + zimFileID
let baseURLString = "zim://" + zimFileID
guard let encoded = contentPath.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed) else {return nil}
self.init(string: encoded, relativeTo: URL(string: baseURLString))
}
Expand All @@ -45,6 +50,7 @@ extension URL {
}

var isUnsupported: Bool { schemeType == .unsupported }
var isZIMURL: Bool { schemeType == .zim }
var isKiwixURL: Bool { schemeType == .kiwix }
var isGeoURL: Bool { schemeType == .geo }

Expand Down Expand Up @@ -75,4 +81,14 @@ extension URL {
func toTemporaryFileURL() -> URL? {
URL(temporaryFileWithName: lastPathComponent)
}

/// If it's an old ``kiwix://`` url comming from a pre-migration tab
/// - Returns: the ``zim://`` url or the original one if it cannot be changed
func updatedToZIMSheme() -> URL {
guard isKiwixURL, var components = URLComponents(url: self, resolvingAgainstBaseURL: false) else {
return self
}
components.scheme = "zim"
return components.url ?? self
}
}
4 changes: 2 additions & 2 deletions Model/Utilities/WebKitHandler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ enum RangeRequestError: Error {
/// To mitigate, opting for the less "broken" behavior of ignoring Range header
/// until WebKit behavior is changed.
final class KiwixURLSchemeHandler: NSObject, WKURLSchemeHandler {
static let KiwixScheme = "kiwix"
static let ZIMScheme = "zim"
@MainActor private var startedTasks: [Int: Bool] = [:]

// MARK: Life cycle
Expand Down Expand Up @@ -75,7 +75,7 @@ final class KiwixURLSchemeHandler: NSObject, WKURLSchemeHandler {
@MainActor
private func handle(task urlSchemeTask: WKURLSchemeTask) async {
let request = urlSchemeTask.request
guard let url = request.url, url.isKiwixURL else {
guard let url = request.url?.updatedToZIMSheme(), url.isZIMURL else {
urlSchemeTask.didFailWithError(URLError(.unsupportedURL))
stopFor(urlSchemeTask.hash)
return
Expand Down
5 changes: 5 additions & 0 deletions Tests/BookmarkMigrationTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,9 @@ final class BookmarkMigrationTests: XCTestCase {
let expectedString = "\0\0\0\u{02}bplist00�\u{01}\u{02}\u{03}\u{04}\u{05}\u{06}^RenderTreeSize^IsAppInitiated^SessionHistory\u{10}\u{03}\t\u{07}\u{08}\t\n\u{1C}\u{1C}_\u{10}\u{15}SessionHistoryEntries_\u{10}\u{1A}SessionHistoryCurrentIndex_\u{10}\u{15}SessionHistoryVersion�\u{0B}\u{16}\u{0C}\r\u{0E}\u{0F}\u{10}\u{11}\u{12}\u{13}\u{14}\u{15}_\u{10}\u{17}SessionHistoryEntryData_\u{10}\u{18}SessionHistoryEntryTitle_\u{10}2SessionHistoryEntryShouldOpenExternalURLsPolicyKey_\u{10}\u{16}SessionHistoryEntryURL_\u{10}\u{1E}SessionHistoryEntryOriginalURLO\u{10}P\0\0\0\0\0\0\0\0\u{02}\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\u{07}�@�\u{13}\u{06}\0\0\0\0\0\0\0\0\0����\0\0\0\0\u{07}�@�\u{13}\u{06}\0����\0\0\0\0\u{03}\0\0\0\0�?\0\0\0\0����TDWDS\u{10}\u{01}_\u{10}2kiwix://A992BF76-CA94-6B60-A762-9B5BC89B5BBF/index_\u{10}2kiwix://A992BF76-CA94-6B60-A762-9B5BC89B5BBF/index�\u{0C}\r\u{0E}\u{0F}\u{10}\u{17}\u{18}\u{19}\u{1A}\u{1B}O\u{10}P\0\0\0\0\0\0\0\0\u{02}\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0V<�A�\u{13}\u{06}\0\0\0\0\0\0\0\0\0����\0\0\0\0U<�A�\u{13}\u{06}\0����\0\0\0\0\0\0\0\0\0\0�?\0\0\0\0����[hier und da\u{10}\u{02}_\u{10}<kiwix://A992BF76-CA94-6B60-A762-9B5BC89B5BBF/wb/hie_.und_.da_\u{10}<kiwix://A992BF76-CA94-6B60-A762-9B5BC89B5BBF/wb/hie_.und_.da\u{10}\u{01}\0\u{08}\0\u{0F}\0\u{1E}\0-\0<\0>\0?\0F\0^\0{\0\0\0\0\0\u{01}\u{0B}\u{01}$\u{01}E\u{01}\u{01}\u{01}\u{01}\u{02}\t\u{02}\u{14}\u{02}g\u{02}s\u{02}u\u{02}\u{02}\0\0\0\0\0\0\u{02}\u{01}\0\0\0\0\0\0\0\u{1D}\0\0\0\0\0\0\0\0\0\0\0\0\0\0\u{02}"
XCTAssertEqual(outString, expectedString)
}

func test_scheme_change() {
let url = URL(string: "kiwix://64C3EA1A-5161-2B94-1F50-606DA5EC0035/wb/Saftladen")!
XCTAssertEqual(url.updateScheme(to: "zim"), URL(string: "zim://64C3EA1A-5161-2B94-1F50-606DA5EC0035/wb/Saftladen")!)
}
}
8 changes: 4 additions & 4 deletions Tests/URLContentPathTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ import XCTest
final class URLContentPathTests: XCTestCase {

private let testURLs = [
URL(string: "kiwix://6E4F3D4A-2F8A-789A-3B88-212219F4FB27/irp.fas.org/doddir/milmed/index.html")!,
URL(string: "kiwix://861C031F-DAFB-9688-4DB4-8F1199FE2926/mesquartierschinois.wordpress.com/")!,
URL(string: "kiwix://861C031F-DAFB-9688-4DB4-8F1199FE2926/widgets.wp.com/likes/master.html%3Fver%3D20240530#ver=20240530&lang=fr&lang_ver=1713167421&origin=https://mesquartierschinois.wordpress.com")!
URL(string: "zim://6E4F3D4A-2F8A-789A-3B88-212219F4FB27/irp.fas.org/doddir/milmed/index.html")!,
URL(string: "zim://861C031F-DAFB-9688-4DB4-8F1199FE2926/mesquartierschinois.wordpress.com/")!,
URL(string: "zim://861C031F-DAFB-9688-4DB4-8F1199FE2926/widgets.wp.com/likes/master.html%3Fver%3D20240530#ver=20240530&lang=fr&lang_ver=1713167421&origin=https://mesquartierschinois.wordpress.com")!
]

func test_no_leading_slash() {
Expand All @@ -31,7 +31,7 @@ final class URLContentPathTests: XCTestCase {
}

func test_preserves_trailing_slash() {
let url = URL(string: "kiwix://861C031F-DAFB-9688-4DB4-8F1199FE2926/mesquartierschinois.wordpress.com/")!
let url = URL(string: "zim://861C031F-DAFB-9688-4DB4-8F1199FE2926/mesquartierschinois.wordpress.com/")!
XCTAssertEqual(url.contentPath.last, "/")
}

Expand Down
20 changes: 12 additions & 8 deletions ViewModel/BrowserViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -254,9 +254,13 @@ final class BrowserViewModel: NSObject, ObservableObject,
private func restoreBy(tabID: NSManagedObjectID) {
if let tab = try? Database.shared.viewContext.existingObject(with: tabID) as? Tab {
webView.interactionState = tab.interactionState
Task {
await MainActor.run {
url = webView.url
Task { [weak self] in
await MainActor.run { [weak self] in
// migrate the tab urls on demand to ZIM scheme
self?.url = self?.webView.url?.updatedToZIMSheme()
if let webURL = self?.webView.url, webURL.isKiwixURL {
self?.load(url: webURL.updatedToZIMSheme())
}
}
}
}
Expand Down Expand Up @@ -314,7 +318,7 @@ final class BrowserViewModel: NSObject, ObservableObject,
decidePolicyFor navigationAction: WKNavigationAction,
decisionHandler: @escaping (WKNavigationActionPolicy) -> Void
) {
guard let url = navigationAction.request.url else {
guard let url = navigationAction.request.url?.updatedToZIMSheme() else {
decisionHandler(.cancel)
return
}
Expand All @@ -329,12 +333,12 @@ final class BrowserViewModel: NSObject, ObservableObject,
}
#endif

if url.isKiwixURL, let redirectedURL = ZimFileService.shared.getRedirectedURL(url: url) {
if url.isZIMURL, let redirectedURL = ZimFileService.shared.getRedirectedURL(url: url) {
if webView.url != redirectedURL {
DispatchQueue.main.async { webView.load(URLRequest(url: redirectedURL)) }
}
decisionHandler(.cancel)
} else if url.isKiwixURL {
} else if url.isZIMURL {
guard ZimFileService.shared.getContentSize(url: url) != nil else {
os_log(
"Missing content at url: %@ => %@",
Expand Down Expand Up @@ -423,7 +427,7 @@ final class BrowserViewModel: NSObject, ObservableObject,
Task { @MainActor in
webView.stopLoading()
(webView.configuration
.urlSchemeHandler(forURLScheme: KiwixURLSchemeHandler.KiwixScheme) as? KiwixURLSchemeHandler)?
.urlSchemeHandler(forURLScheme: KiwixURLSchemeHandler.ZIMScheme) as? KiwixURLSchemeHandler)?
.didFailProvisionalNavigation()
}
guard error.code != NSURLErrorCancelled else { return }
Expand Down Expand Up @@ -497,7 +501,7 @@ final class BrowserViewModel: NSObject, ObservableObject,
contextMenuConfigurationForElement elementInfo: WKContextMenuElementInfo,
completionHandler: @escaping (UIContextMenuConfiguration?) -> Void
) {
guard let url = elementInfo.linkURL, url.isKiwixURL else { completionHandler(nil); return }
guard let url = elementInfo.linkURL, url.isZIMURL else { completionHandler(nil); return }
let configuration = UIContextMenuConfiguration(
previewProvider: {
let webView = WKWebView(frame: .zero, configuration: WebViewConfiguration())
Expand Down
2 changes: 1 addition & 1 deletion Views/BuildingBlocks/WebView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ extension WKWebView {
final class WebViewConfiguration: WKWebViewConfiguration {
override init() {
super.init()
setURLSchemeHandler(KiwixURLSchemeHandler(), forURLScheme: KiwixURLSchemeHandler.KiwixScheme)
setURLSchemeHandler(KiwixURLSchemeHandler(), forURLScheme: KiwixURLSchemeHandler.ZIMScheme)
#if os(macOS)
preferences.isElementFullscreenEnabled = true
#else
Expand Down
2 changes: 1 addition & 1 deletion Views/ViewModifiers/SaveContentHandler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ struct SaveContentHandler: ViewModifier {
func body(content: Content) -> some View {
content.onReceive(saveContentToFile) { notification in
guard let url = notification.userInfo?["url"] as? URL,
url.isKiwixURL else {
url.isZIMURL else {
return
}
#if os(macOS)
Expand Down

0 comments on commit 94177fc

Please sign in to comment.