From f9969d3d7165b5978bade93faab0f40e5a403170 Mon Sep 17 00:00:00 2001 From: Marko Bencun Date: Fri, 27 Sep 2024 09:44:43 +0200 Subject: [PATCH] ios: fix exporting files (CSV exports, log export, notes export) Similar to Android, we set `ExportsDir()` to `""` and let the backend environment `getSaveFilename()` construct the full path. Mobile handling of the dirs/files is a big hack and works on implicit assumptions, e.g. that `getSaveFilename()` is always called with `filepath.Join(exportsDir, filename)`, which is only the filename on mobile as `ExportsDir()` returns `""`. This should be cleaned up, but for now iOS is made to behave like Android in this regard. We use a temp directory for these files, so the files will be cleaned up automatically after a while. --- .../BitBoxApp/BitBoxApp/BitBoxAppApp.swift | 52 ++++++++++++++++--- util/config/appdir.go | 4 +- 2 files changed, 46 insertions(+), 10 deletions(-) diff --git a/frontends/ios/BitBoxApp/BitBoxApp/BitBoxAppApp.swift b/frontends/ios/BitBoxApp/BitBoxApp/BitBoxAppApp.swift index 248fada3e2..7d979ec6c5 100644 --- a/frontends/ios/BitBoxApp/BitBoxApp/BitBoxAppApp.swift +++ b/frontends/ios/BitBoxApp/BitBoxApp/BitBoxAppApp.swift @@ -38,10 +38,12 @@ protocol SetMessageHandlersProtocol { func setMessageHandlers(handlers: MessageHandlersProtocol) } -class GoEnvironment: NSObject, MobileserverGoEnvironmentInterfaceProtocol { - func getSaveFilename(_ p0: String?) -> String { - // TODO - return "" +class GoEnvironment: NSObject, MobileserverGoEnvironmentInterfaceProtocol, UIDocumentInteractionControllerDelegate { + func getSaveFilename(_ fileName: String?) -> String { + let tempDirectory = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true) + // fileName cannot be nil this is called by Go and Go strings cannot be nil/null. + let fileURL = tempDirectory.appendingPathComponent(fileName!) + return fileURL.path } func auth() { @@ -75,10 +77,44 @@ class GoEnvironment: NSObject, MobileserverGoEnvironmentInterfaceProtocol { func setDarkTheme(_ p0: Bool) { } - func systemOpen(_ url: String?) throws { - guard let url = URL(string: url!) else { return } - if UIApplication.shared.canOpenURL(url) { - UIApplication.shared.open(url) + // Helper method to get the root view controller + private func getRootViewController() -> UIViewController? { + guard let scene = UIApplication.shared.connectedScenes + .filter({ $0.activationState == .foregroundActive }) + .first as? UIWindowScene else { + return nil + } + + return scene.windows.first(where: { $0.isKeyWindow })?.rootViewController + } + + func systemOpen(_ urlString: String?) throws { + guard let urlString = urlString else { return } + // Check if it's a local file path (not a URL) + var url: URL + if urlString.hasPrefix("/") { + // This is a local file path, construct a file URL + url = URL(fileURLWithPath: urlString) + } else if let potentialURL = URL(string: urlString), potentialURL.scheme != nil { + // This is already a valid URL with a scheme + url = potentialURL + } else { + // Invalid URL or path + return + } + // Ensure we run on the main thread + DispatchQueue.main.async { + if url.isFileURL { + // Local file path, use UIDocumentInteractionController + if let rootViewController = self.getRootViewController() { + let activityViewController = UIActivityViewController(activityItems: [url], applicationActivities: nil) + rootViewController.present(activityViewController, animated: true, completion: nil) + } + } else { + if UIApplication.shared.canOpenURL(url) { + UIApplication.shared.open(url) + } + } } } diff --git a/util/config/appdir.go b/util/config/appdir.go index 625f448b8f..65afb09353 100644 --- a/util/config/appdir.go +++ b/util/config/appdir.go @@ -83,8 +83,8 @@ func AppDir() string { // ExportsDir returns the absolute path to the folder which can be used to export files. func ExportsDir() (string, error) { - if runtime.GOOS == "android" { - // Android apps are sandboxed, we don't need to specify a folder. + if runtime.GOOS == "android" || runtime.GOOS == "ios" { + // Android/iOS apps are sandboxed, we don't need to specify a folder. return "", nil } homeFolder := os.Getenv("HOME")