diff --git a/App/App_iOS.swift b/App/App_iOS.swift index c45e050d..778ee976 100644 --- a/App/App_iOS.swift +++ b/App/App_iOS.swift @@ -28,6 +28,7 @@ struct Kiwix: App { @StateObject private var navigation = NavigationViewModel() @UIApplicationDelegateAdaptor(AppDelegate.self) private var appDelegate private let fileMonitor: DirectoryMonitor +// @State var isOpeningDeeplink = true init() { fileMonitor = DirectoryMonitor(url: URL.documentDirectory) { LibraryOperations.scanDirectory($0) } @@ -60,14 +61,15 @@ struct Kiwix: App { } case .background: break -// reScheduleBackgroundDownloadTask() @unknown default: break } } .onOpenURL { url in if url.isFileURL { - NotificationCenter.openFiles([url], context: .file) + let deepLinkId = UUID() + DeepLinkService.shared.startFor(uuid: deepLinkId) + NotificationCenter.openFiles([url], context: .file(deepLinkId: deepLinkId)) } else if url.isZIMURL { NotificationCenter.openURL(url) } @@ -77,7 +79,9 @@ struct Kiwix: App { case .kiwix: fileMonitor.start() await LibraryOperations.reopen() - navigation.navigateToMostRecentTab() + if !DeepLinkService.shared.isRunning() { + navigation.navigateToMostRecentTab() + } LibraryOperations.scanDirectory(URL.documentDirectory) LibraryOperations.applyFileBackupSetting() DownloadService.shared.restartHeartbeatIfNeeded() diff --git a/App/SplitViewController.swift b/App/SplitViewController.swift index 8ba489ff..40ab29ae 100644 --- a/App/SplitViewController.swift +++ b/App/SplitViewController.swift @@ -89,7 +89,7 @@ final class SplitViewController: UISplitViewController { }) openURLObserver = NotificationCenter.default.addObserver( - forName: .openURL, object: nil, queue: nil + forName: .openURL, object: nil, queue: .main ) { [weak self] notification in guard let url = notification.userInfo?["url"] as? URL else { return } let inNewTab = notification.userInfo?["inNewTab"] as? Bool ?? false @@ -99,6 +99,10 @@ final class SplitViewController: UISplitViewController { } else if let tabID = self?.navigationViewModel.createTab() { BrowserViewModel.getCached(tabID: tabID).load(url: url) } + if let context = notification.userInfo?["context"] as? OpenURLContext, + case .deepLink(let deepLinkId) = context { + DeepLinkService.shared.stopFor(uuid: deepLinkId) + } } } observeGoBackAndForward() diff --git a/Model/DeepLinkService.swift b/Model/DeepLinkService.swift new file mode 100644 index 00000000..b8d01b0a --- /dev/null +++ b/Model/DeepLinkService.swift @@ -0,0 +1,42 @@ +// 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 + +/// Helper to figure out if a deeplink started ZIM file +/// handling is already running. +/// In that case we do not want to handle the default +/// navigation to the latest opened ZIM file +@MainActor +final class DeepLinkService { + + static let shared = DeepLinkService() + + private var ids = Set() + + private init() {} + + func startFor(uuid: UUID) { + ids.insert(uuid) + } + + func stopFor(uuid: UUID) { + ids.remove(uuid) + } + + func isRunning() -> Bool { + !ids.isEmpty + } +} diff --git a/SwiftUI/Model/Enum.swift b/SwiftUI/Model/Enum.swift index 14eeaf6d..cc727953 100644 --- a/SwiftUI/Model/Enum.swift +++ b/SwiftUI/Model/Enum.swift @@ -138,14 +138,14 @@ enum ExternalLinkLoadingPolicy: String, CaseIterable, Identifiable, Defaults.Ser } } -enum OpenURLContext: String { - case deepLink +enum OpenURLContext { + case deepLink(id: UUID) case file } -enum OpenFileContext: String { +enum OpenFileContext { case command - case file + case file(deepLinkId: UUID? = nil) case welcomeScreen case library } diff --git a/ViewModel/NavigationViewModel.swift b/ViewModel/NavigationViewModel.swift index 7ebd7276..7a6d45ae 100644 --- a/ViewModel/NavigationViewModel.swift +++ b/ViewModel/NavigationViewModel.swift @@ -16,6 +16,7 @@ import CoreData import WebKit import Combine +import os @MainActor final class NavigationViewModel: ObservableObject { @@ -67,6 +68,7 @@ final class NavigationViewModel: ObservableObject { let tab = (try? context.fetch(fetchRequest).first) ?? Self.makeTab(context: context) Task { await MainActor.run { + os_log("open navigate to most recent tab", log: Log.LibraryOperations, type: .error) currentItem = NavigationItem.tab(objectID: tab.objectID) } } diff --git a/Views/ViewModifiers/FileImport.swift b/Views/ViewModifiers/FileImport.swift index 4eed417e..a0539cc8 100644 --- a/Views/ViewModifiers/FileImport.swift +++ b/Views/ViewModifiers/FileImport.swift @@ -54,14 +54,14 @@ struct OpenFileHandler: ViewModifier { @State private var isAlertPresented = false @State private var activeAlert: ActiveAlert? - private let importFiles = NotificationCenter.default.publisher(for: .openFiles) + private let openFiles = NotificationCenter.default.publisher(for: .openFiles) enum ActiveAlert { case unableToOpen(filenames: [String]) } // swiftlint:disable:next cyclomatic_complexity func body(content: Content) -> some View { - content.onReceive(importFiles) { notification in + content.onReceive(openFiles) { notification in guard let urls = notification.userInfo?["urls"] as? [URL], let context = notification.userInfo?["context"] as? OpenFileContext else { return } @@ -90,7 +90,11 @@ struct OpenFileHandler: ViewModifier { NotificationCenter.openURL(url, inNewTab: true, context: .file) } #elseif os(iOS) - NotificationCenter.openURL(url, inNewTab: true) + if case .file(.some(let deepLinkID)) = context { + NotificationCenter.openURL(url, inNewTab: true, context: .deepLink(id: deepLinkID)) + } else { + NotificationCenter.openURL(url, inNewTab: true) + } #endif } case .welcomeScreen: