From 566a4659b2feac7e413058373c4c39063b08365f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franc=CC=A7ois=20Benaiteau?= Date: Tue, 14 Jan 2025 22:35:36 +0100 Subject: [PATCH 1/4] add performBackupIntent --- wire-ios/Wire-iOS.xcodeproj/project.pbxproj | 4 ++ .../Sources/PerformBackupIntent.swift | 55 +++++++++++++++++++ 2 files changed, 59 insertions(+) create mode 100644 wire-ios/Wire-iOS/Sources/PerformBackupIntent.swift diff --git a/wire-ios/Wire-iOS.xcodeproj/project.pbxproj b/wire-ios/Wire-iOS.xcodeproj/project.pbxproj index 1fef863fc15..ad61b5a933b 100644 --- a/wire-ios/Wire-iOS.xcodeproj/project.pbxproj +++ b/wire-ios/Wire-iOS.xcodeproj/project.pbxproj @@ -36,6 +36,7 @@ 016DB6DF2C261A4900DEB81B /* WireDomain.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 016DB6DD2C261A4900DEB81B /* WireDomain.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 017ABBBB299542BA0004C243 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F1FDF2C521ADA3F000E037A1 /* Images.xcassets */; }; 01874DD72C18581C00208716 /* DeveloperToolsContextItemsProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01874DD62C18581C00208716 /* DeveloperToolsContextItemsProvider.swift */; }; + 01A162072D370D7F00A0F0D5 /* ScheduleActionIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01A162062D370D7F00A0F0D5 /* ScheduleActionIntent.swift */; }; 01BC68512CE3AF9E00445243 /* EmptyConversationSearchResultsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01BC68502CE3AF9E00445243 /* EmptyConversationSearchResultsView.swift */; }; 01BC68652CE496A500445243 /* EmptyPlaceholderContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01BC68642CE496A500445243 /* EmptyPlaceholderContainerView.swift */; }; 01C1A7C72A54C45A0058D578 /* SnapshotTesting in Frameworks */ = {isa = PBXBuildFile; productRef = 01C1A7C62A54C45A0058D578 /* SnapshotTesting */; }; @@ -1972,6 +1973,7 @@ 016BDDEF2AA0AE670054FB04 /* Developer-Flags.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Developer-Flags.xcconfig"; sourceTree = ""; }; 016DB6DD2C261A4900DEB81B /* WireDomain.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = WireDomain.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 01874DD62C18581C00208716 /* DeveloperToolsContextItemsProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeveloperToolsContextItemsProvider.swift; sourceTree = ""; }; + 01A162062D370D7F00A0F0D5 /* ScheduleActionIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScheduleActionIntent.swift; sourceTree = ""; }; 01A5E047297FDAB500624B65 /* updateStylekit */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = updateStylekit; sourceTree = ""; }; 01A5E048297FDAB500624B65 /* copyExtraAudioNotifications */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = copyExtraAudioNotifications; sourceTree = ""; }; 01BC68502CE3AF9E00445243 /* EmptyConversationSearchResultsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyConversationSearchResultsView.swift; sourceTree = ""; }; @@ -6206,6 +6208,7 @@ E95F1CA02C3D472A005E80BC /* AnalytitcsServiceConfigurationBuilder.swift */, F1141C961F5EB2DC005340B3 /* WireApplication.swift */, 5489E20D252C84BE0010461D /* AppStateCalculator.swift */, + 01A162062D370D7F00A0F0D5 /* ScheduleActionIntent.swift */, 543DF6AC25309641001F6EA6 /* AppRootRouter.swift */, 54B27985258113FD0043ED25 /* AuthenticatedRouter.swift */, 546EEA8C2536DDF500195097 /* URLActionRouter.swift */, @@ -9134,6 +9137,7 @@ 87A15FCE1E07EAB400AED79B /* AssetCollectionWrapper.swift in Sources */, 7A702FB7286322D800004580 /* ConversationEncryptionProtocolCell.swift in Sources */, 1648EEF32049A6F800CC6B37 /* DirectorySectionController.swift in Sources */, + 01A162072D370D7F00A0F0D5 /* ScheduleActionIntent.swift in Sources */, 060A36902CBCF1010066908C /* ConversationListViewController+EmptyState.swift in Sources */, A96A21EF23D5AE6D005B5579 /* UIApplication+Permissions.swift in Sources */, 2572EC572B148EE50075A7BE /* DeviceDetailsButtonsView.swift in Sources */, diff --git a/wire-ios/Wire-iOS/Sources/PerformBackupIntent.swift b/wire-ios/Wire-iOS/Sources/PerformBackupIntent.swift new file mode 100644 index 00000000000..8798e7b1d47 --- /dev/null +++ b/wire-ios/Wire-iOS/Sources/PerformBackupIntent.swift @@ -0,0 +1,55 @@ +// +// Wire +// Copyright (C) 2025 Wire Swiss GmbH +// +// This program 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 +// (at your option) any later version. +// +// This program 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 this program. If not, see http://www.gnu.org/licenses/. +// + + +import AppIntents +import Foundation + +enum BackupError: Error, LocalizedError { + case emptyPassword + + var errorDescription: String? { + switch self { + case .emptyPassword: + return "Password cannot be empty." + } + } +} + +struct PerformBackupIntent: AppIntent { + static var title: LocalizedStringResource = "Perform Backup" + + // Parameters for the backup + @Parameter(title: "Password") + var password: String + + static var description: IntentDescription { + IntentDescription("Perform a backup with the provided password.") + } + + func perform() async throws -> some IntentResult { + // Perform the backup operation + if password.isEmpty { + throw BackupError.emptyPassword + } + + // Replace with your app's backup logic + print("Performing backup with password: \(password)") + return .result(dialog: "Backup performed successfully!") + } +} From 445da52f8ddd8c2817ccf303483369aed155a67d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franc=CC=A7ois=20Benaiteau?= Date: Wed, 15 Jan 2025 10:33:59 +0100 Subject: [PATCH 2/4] wip --- wire-ios/Wire-iOS.xcodeproj/project.pbxproj | 12 +- wire-ios/Wire-iOS/Sources/AppDelegate.swift | 1 + .../Sources/PerformBackupIntent.swift | 111 ++++++++++++++++-- .../Wire-iOS/Sources/WireAppShorcuts.swift | 30 +++++ 4 files changed, 142 insertions(+), 12 deletions(-) create mode 100644 wire-ios/Wire-iOS/Sources/WireAppShorcuts.swift diff --git a/wire-ios/Wire-iOS.xcodeproj/project.pbxproj b/wire-ios/Wire-iOS.xcodeproj/project.pbxproj index ad61b5a933b..afbc69d611e 100644 --- a/wire-ios/Wire-iOS.xcodeproj/project.pbxproj +++ b/wire-ios/Wire-iOS.xcodeproj/project.pbxproj @@ -36,7 +36,8 @@ 016DB6DF2C261A4900DEB81B /* WireDomain.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 016DB6DD2C261A4900DEB81B /* WireDomain.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 017ABBBB299542BA0004C243 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F1FDF2C521ADA3F000E037A1 /* Images.xcassets */; }; 01874DD72C18581C00208716 /* DeveloperToolsContextItemsProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01874DD62C18581C00208716 /* DeveloperToolsContextItemsProvider.swift */; }; - 01A162072D370D7F00A0F0D5 /* ScheduleActionIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01A162062D370D7F00A0F0D5 /* ScheduleActionIntent.swift */; }; + 01A1624B2D3710AA00A0F0D5 /* PerformBackupIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01A1624A2D3710AA00A0F0D5 /* PerformBackupIntent.swift */; }; + 01A1624D2D37168400A0F0D5 /* WireAppShorcuts.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01A1624C2D37168400A0F0D5 /* WireAppShorcuts.swift */; }; 01BC68512CE3AF9E00445243 /* EmptyConversationSearchResultsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01BC68502CE3AF9E00445243 /* EmptyConversationSearchResultsView.swift */; }; 01BC68652CE496A500445243 /* EmptyPlaceholderContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01BC68642CE496A500445243 /* EmptyPlaceholderContainerView.swift */; }; 01C1A7C72A54C45A0058D578 /* SnapshotTesting in Frameworks */ = {isa = PBXBuildFile; productRef = 01C1A7C62A54C45A0058D578 /* SnapshotTesting */; }; @@ -1973,7 +1974,8 @@ 016BDDEF2AA0AE670054FB04 /* Developer-Flags.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Developer-Flags.xcconfig"; sourceTree = ""; }; 016DB6DD2C261A4900DEB81B /* WireDomain.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = WireDomain.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 01874DD62C18581C00208716 /* DeveloperToolsContextItemsProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeveloperToolsContextItemsProvider.swift; sourceTree = ""; }; - 01A162062D370D7F00A0F0D5 /* ScheduleActionIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScheduleActionIntent.swift; sourceTree = ""; }; + 01A1624A2D3710AA00A0F0D5 /* PerformBackupIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PerformBackupIntent.swift; sourceTree = ""; }; + 01A1624C2D37168400A0F0D5 /* WireAppShorcuts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WireAppShorcuts.swift; sourceTree = ""; }; 01A5E047297FDAB500624B65 /* updateStylekit */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = updateStylekit; sourceTree = ""; }; 01A5E048297FDAB500624B65 /* copyExtraAudioNotifications */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = copyExtraAudioNotifications; sourceTree = ""; }; 01BC68502CE3AF9E00445243 /* EmptyConversationSearchResultsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyConversationSearchResultsView.swift; sourceTree = ""; }; @@ -6208,8 +6210,9 @@ E95F1CA02C3D472A005E80BC /* AnalytitcsServiceConfigurationBuilder.swift */, F1141C961F5EB2DC005340B3 /* WireApplication.swift */, 5489E20D252C84BE0010461D /* AppStateCalculator.swift */, - 01A162062D370D7F00A0F0D5 /* ScheduleActionIntent.swift */, 543DF6AC25309641001F6EA6 /* AppRootRouter.swift */, + 01A1624C2D37168400A0F0D5 /* WireAppShorcuts.swift */, + 01A1624A2D3710AA00A0F0D5 /* PerformBackupIntent.swift */, 54B27985258113FD0043ED25 /* AuthenticatedRouter.swift */, 546EEA8C2536DDF500195097 /* URLActionRouter.swift */, 546EEA8E2536DFAB00195097 /* SwitchingAccountRouter.swift */, @@ -9137,7 +9140,6 @@ 87A15FCE1E07EAB400AED79B /* AssetCollectionWrapper.swift in Sources */, 7A702FB7286322D800004580 /* ConversationEncryptionProtocolCell.swift in Sources */, 1648EEF32049A6F800CC6B37 /* DirectorySectionController.swift in Sources */, - 01A162072D370D7F00A0F0D5 /* ScheduleActionIntent.swift in Sources */, 060A36902CBCF1010066908C /* ConversationListViewController+EmptyState.swift in Sources */, A96A21EF23D5AE6D005B5579 /* UIApplication+Permissions.swift in Sources */, 2572EC572B148EE50075A7BE /* DeviceDetailsButtonsView.swift in Sources */, @@ -9537,6 +9539,7 @@ 87BEB0401D3E44E600DE9575 /* AssetCell.swift in Sources */, E66258872B4D393400C23E79 /* DeveloperDebugActionsView.swift in Sources */, A92CB35923CDF97800F12797 /* ConversationViewController+ConversationContentViewControllerDelegate.swift in Sources */, + 01A1624B2D3710AA00A0F0D5 /* PerformBackupIntent.swift in Sources */, 0622D6A2265B38DE00D759DB /* UserBlockingReasonCell.swift in Sources */, BFCCA4FE20A0A03D000A4F33 /* CallInfoRootViewController.swift in Sources */, 7C52F723200CBE61009F85FB /* CameraKeyboardPermissionsCell.swift in Sources */, @@ -9599,6 +9602,7 @@ A90BECF124A23E6500E5C008 /* LinkViewDelegate.swift in Sources */, A993719123F7461600881564 /* UIView+ExtendedBlockAnimations.swift in Sources */, D550F57920445AD7009E09DD /* UIAlertController+ConversationGuestOptions.swift in Sources */, + 01A1624D2D37168400A0F0D5 /* WireAppShorcuts.swift in Sources */, E662588B2B4D3C9D00C23E79 /* DeveloperDebugActionsDisplayModel.swift in Sources */, 16D74BE82B573A1C00160298 /* AuthenticationE2EIdentityMissingErrorHandler.swift in Sources */, 871BC3601D34F94200DF0793 /* SettingsPropertyFactory.swift in Sources */, diff --git a/wire-ios/Wire-iOS/Sources/AppDelegate.swift b/wire-ios/Wire-iOS/Sources/AppDelegate.swift index 3f3a06a3391..125378ee57f 100644 --- a/wire-ios/Wire-iOS/Sources/AppDelegate.swift +++ b/wire-ios/Wire-iOS/Sources/AppDelegate.swift @@ -179,6 +179,7 @@ final class AppDelegate: UIResponder, UIApplicationDelegate { WireLogger.appDelegate .info("application:didFinishLaunchingWithOptions END \(String(describing: launchOptions))") WireLogger.appDelegate.info("Application was launched with arguments: \(ProcessInfo.processInfo.arguments)") + return true } diff --git a/wire-ios/Wire-iOS/Sources/PerformBackupIntent.swift b/wire-ios/Wire-iOS/Sources/PerformBackupIntent.swift index 8798e7b1d47..e0c0dcbc8ff 100644 --- a/wire-ios/Wire-iOS/Sources/PerformBackupIntent.swift +++ b/wire-ios/Wire-iOS/Sources/PerformBackupIntent.swift @@ -19,37 +19,132 @@ import AppIntents import Foundation +import WireSyncEngine +import Intents +import UniformTypeIdentifiers enum BackupError: Error, LocalizedError { case emptyPassword - + case noActiveAccount + case noAppGroup + var errorDescription: String? { switch self { case .emptyPassword: return "Password cannot be empty." + case .noActiveAccount: + return "No active account. Please open the app and login first" + case .noAppGroup: + return "Could not access the appGroup for sharing the resulting backup file" } } } +@available(iOS 18.0, *) struct PerformBackupIntent: AppIntent { static var title: LocalizedStringResource = "Perform Backup" - + // Parameters for the backup @Parameter(title: "Password") var password: String - + static var description: IntentDescription { IntentDescription("Perform a backup with the provided password.") } - - func perform() async throws -> some IntentResult { + + func perform() async throws -> some ProvidesDialog & ReturnsValue { // Perform the backup operation if password.isEmpty { throw BackupError.emptyPassword } - + // Replace with your app's backup logic - print("Performing backup with password: \(password)") - return .result(dialog: "Backup performed successfully!") + guard let account = SessionManager.shared?.currentAccount else { + throw BackupError.noActiveAccount + } + let fileURL = try await withCheckedThrowingContinuation { continuation in + DispatchQueue.main.async { + SessionManager.shared?.backupActiveAccount(password: password) { result in + switch result { + case .success(let url): + continuation.resume(returning: url) + case .failure(let error): + continuation.resume(throwing: error) + } + } + } + } + + guard let appGroupIdentifier = Bundle.main.appGroupIdentifier else { + throw BackupError.noAppGroup + } + + + + let sharedContainerURL = FileManager.sharedContainerDirectory(for: appGroupIdentifier) + let newFileURL = sharedContainerURL.appendingPathComponent(fileURL.lastPathComponent) + + try? FileManager.default.removeItem(at: newFileURL) + try FileManager.default.moveItem(at: fileURL, to: newFileURL) + // Return the file URL and a dialog message + if #available(iOS 18.0, *) { + return .result( + value: BackupFileEntity(id: try .file(url: newFileURL)), + dialog: "Backup was successful! The file is located at \(newFileURL.absoluteString)." + ) + } else { + // Fallback on earlier versions + throw BackupError.noAppGroup + } + } +} + +@available(iOS 18.0, *) +struct BackupFileEntity: FileEntity { + var displayRepresentation: DisplayRepresentation = .init(title: "Backup fileee") + + static var supportedContentTypes: [UTType] = [UTType("ios_wbu")!] + + static var defaultQuery = BackupFileQuery() + + typealias DefaultQuery = BackupFileQuery + +// static var supportedContentTypes = [UTType.init("ios_wbu")!] + + + var id: FileEntityIdentifier + + static var typeDisplayRepresentation: TypeDisplayRepresentation = "Wire Backup file" + + + +} + +@available(iOS 18.0, *) +struct BackupFileQuery: EntityQuery { + // Define the type of entity this query works with + typealias Entity = BackupFileEntity + + // Provide a user-friendly title for the query + static var suggestedSortDescriptors: [SortDescriptor] = [] + + // This function is called to retrieve matching entities + func entities(for identifiers: [BackupFileEntity.ID]) async throws -> [BackupFileEntity] { + // Fetch entities by identifiers (e.g., file URLs) + return identifiers.compactMap { id in + return BackupFileEntity(id: id) + } + } + + func suggestedEntities() async throws -> [BackupFileEntity] { + // Provide a list of suggested entities + let directory = FileManager.default.temporaryDirectory + let backupFiles = try FileManager.default.contentsOfDirectory(at: directory, includingPropertiesForKeys: nil) + .filter { $0.pathExtension == "ios_wbu" } + var result = [BackupFileEntity]() + for url in backupFiles { + result.append(BackupFileEntity(id: try FileEntityIdentifier.file(url: url))) + } + return result } } diff --git a/wire-ios/Wire-iOS/Sources/WireAppShorcuts.swift b/wire-ios/Wire-iOS/Sources/WireAppShorcuts.swift new file mode 100644 index 00000000000..1203b72ba68 --- /dev/null +++ b/wire-ios/Wire-iOS/Sources/WireAppShorcuts.swift @@ -0,0 +1,30 @@ +// +// Wire +// Copyright (C) 2025 Wire Swiss GmbH +// +// This program 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 +// (at your option) any later version. +// +// This program 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 this program. If not, see http://www.gnu.org/licenses/. +// + +import AppIntents + +struct WireAppShorcuts: AppShortcutsProvider { + @AppShortcutsBuilder static var appShortcuts: [AppShortcut] { + if #available(iOS 18.0, *) { + return [AppShortcut(intent: PerformBackupIntent(), phrases: ["Backup data with \(.applicationName)"])] + } else { + // Fallback on earlier versions + return [] + } + } +} From 67458d1bbe70ae4d99a6ba6f91eac3e53e281a79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franc=CC=A7ois=20Benaiteau?= Date: Wed, 15 Jan 2025 23:50:02 +0100 Subject: [PATCH 3/4] fix file --- .../Sources/PerformBackupIntent.swift | 67 ++----------------- 1 file changed, 7 insertions(+), 60 deletions(-) diff --git a/wire-ios/Wire-iOS/Sources/PerformBackupIntent.swift b/wire-ios/Wire-iOS/Sources/PerformBackupIntent.swift index e0c0dcbc8ff..83ad15aba29 100644 --- a/wire-ios/Wire-iOS/Sources/PerformBackupIntent.swift +++ b/wire-ios/Wire-iOS/Sources/PerformBackupIntent.swift @@ -52,7 +52,7 @@ struct PerformBackupIntent: AppIntent { IntentDescription("Perform a backup with the provided password.") } - func perform() async throws -> some ProvidesDialog & ReturnsValue { + func perform() async throws -> some ProvidesDialog & ReturnsValue { // Perform the backup operation if password.isEmpty { throw BackupError.emptyPassword @@ -87,64 +87,11 @@ struct PerformBackupIntent: AppIntent { try? FileManager.default.removeItem(at: newFileURL) try FileManager.default.moveItem(at: fileURL, to: newFileURL) // Return the file URL and a dialog message - if #available(iOS 18.0, *) { - return .result( - value: BackupFileEntity(id: try .file(url: newFileURL)), - dialog: "Backup was successful! The file is located at \(newFileURL.absoluteString)." - ) - } else { - // Fallback on earlier versions - throw BackupError.noAppGroup - } - } -} - -@available(iOS 18.0, *) -struct BackupFileEntity: FileEntity { - var displayRepresentation: DisplayRepresentation = .init(title: "Backup fileee") - - static var supportedContentTypes: [UTType] = [UTType("ios_wbu")!] - - static var defaultQuery = BackupFileQuery() - - typealias DefaultQuery = BackupFileQuery - -// static var supportedContentTypes = [UTType.init("ios_wbu")!] - - - var id: FileEntityIdentifier - - static var typeDisplayRepresentation: TypeDisplayRepresentation = "Wire Backup file" - - - -} - -@available(iOS 18.0, *) -struct BackupFileQuery: EntityQuery { - // Define the type of entity this query works with - typealias Entity = BackupFileEntity - - // Provide a user-friendly title for the query - static var suggestedSortDescriptors: [SortDescriptor] = [] - - // This function is called to retrieve matching entities - func entities(for identifiers: [BackupFileEntity.ID]) async throws -> [BackupFileEntity] { - // Fetch entities by identifiers (e.g., file URLs) - return identifiers.compactMap { id in - return BackupFileEntity(id: id) - } - } - - func suggestedEntities() async throws -> [BackupFileEntity] { - // Provide a list of suggested entities - let directory = FileManager.default.temporaryDirectory - let backupFiles = try FileManager.default.contentsOfDirectory(at: directory, includingPropertiesForKeys: nil) - .filter { $0.pathExtension == "ios_wbu" } - var result = [BackupFileEntity]() - for url in backupFiles { - result.append(BackupFileEntity(id: try FileEntityIdentifier.file(url: url))) - } - return result + return .result( + value: IntentFile(fileURL: newFileURL, + filename: newFileURL.lastPathComponent, + type: UTType("com.wire.backup-ios-underscore")!), + dialog: "Backup was successful! The file is located at \(newFileURL.absoluteString)." + ) } } From 9aa4c6a5d91f4a69b3233b8139fefd61d3a694b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franc=CC=A7ois=20Benaiteau?= Date: Thu, 16 Jan 2025 14:56:19 +0100 Subject: [PATCH 4/4] cleanup --- wire-ios-utilities/Source/DeveloperFlag.swift | 6 ++++ .../Sources/PerformBackupIntent.swift | 34 +++---------------- .../Wire-iOS/Sources/WireAppShorcuts.swift | 7 ++-- 3 files changed, 14 insertions(+), 33 deletions(-) diff --git a/wire-ios-utilities/Source/DeveloperFlag.swift b/wire-ios-utilities/Source/DeveloperFlag.swift index 22dd94bd0ad..8257888fb1e 100644 --- a/wire-ios-utilities/Source/DeveloperFlag.swift +++ b/wire-ios-utilities/Source/DeveloperFlag.swift @@ -29,6 +29,7 @@ public enum DeveloperFlag: String, CaseIterable { case debugDuplicateObjects case decryptAndStoreEventsSleep case forceCRLExpiryAfterOneMinute + case enablePerformBackupAction public var description: String { switch self { @@ -52,6 +53,9 @@ public enum DeveloperFlag: String, CaseIterable { case .forceCRLExpiryAfterOneMinute: "Turn on to force CRLs to expire after 1 minute" + + case .enablePerformBackupAction: + "Expose perform Backup Intent" } } @@ -90,6 +94,8 @@ public enum DeveloperFlag: String, CaseIterable { nil case .ignoreIncomingEvents: "IgnoreIncomingEventsEnabled" + case .enablePerformBackupAction: + "EnablePerformBackupAction" } } diff --git a/wire-ios/Wire-iOS/Sources/PerformBackupIntent.swift b/wire-ios/Wire-iOS/Sources/PerformBackupIntent.swift index 83ad15aba29..cba548da32b 100644 --- a/wire-ios/Wire-iOS/Sources/PerformBackupIntent.swift +++ b/wire-ios/Wire-iOS/Sources/PerformBackupIntent.swift @@ -25,26 +25,18 @@ import UniformTypeIdentifiers enum BackupError: Error, LocalizedError { case emptyPassword - case noActiveAccount - case noAppGroup var errorDescription: String? { switch self { case .emptyPassword: return "Password cannot be empty." - case .noActiveAccount: - return "No active account. Please open the app and login first" - case .noAppGroup: - return "Could not access the appGroup for sharing the resulting backup file" } } } -@available(iOS 18.0, *) struct PerformBackupIntent: AppIntent { static var title: LocalizedStringResource = "Perform Backup" - // Parameters for the backup @Parameter(title: "Password") var password: String @@ -52,16 +44,11 @@ struct PerformBackupIntent: AppIntent { IntentDescription("Perform a backup with the provided password.") } - func perform() async throws -> some ProvidesDialog & ReturnsValue { - // Perform the backup operation + func perform() async throws -> some ReturnsValue { if password.isEmpty { throw BackupError.emptyPassword } - // Replace with your app's backup logic - guard let account = SessionManager.shared?.currentAccount else { - throw BackupError.noActiveAccount - } let fileURL = try await withCheckedThrowingContinuation { continuation in DispatchQueue.main.async { SessionManager.shared?.backupActiveAccount(password: password) { result in @@ -75,23 +62,10 @@ struct PerformBackupIntent: AppIntent { } } - guard let appGroupIdentifier = Bundle.main.appGroupIdentifier else { - throw BackupError.noAppGroup - } - - - - let sharedContainerURL = FileManager.sharedContainerDirectory(for: appGroupIdentifier) - let newFileURL = sharedContainerURL.appendingPathComponent(fileURL.lastPathComponent) - - try? FileManager.default.removeItem(at: newFileURL) - try FileManager.default.moveItem(at: fileURL, to: newFileURL) - // Return the file URL and a dialog message return .result( - value: IntentFile(fileURL: newFileURL, - filename: newFileURL.lastPathComponent, - type: UTType("com.wire.backup-ios-underscore")!), - dialog: "Backup was successful! The file is located at \(newFileURL.absoluteString)." + value: IntentFile(fileURL: fileURL, + filename: fileURL.lastPathComponent, + type: UTType("com.wire.backup-ios-underscore")!) ) } } diff --git a/wire-ios/Wire-iOS/Sources/WireAppShorcuts.swift b/wire-ios/Wire-iOS/Sources/WireAppShorcuts.swift index 1203b72ba68..070e70660cf 100644 --- a/wire-ios/Wire-iOS/Sources/WireAppShorcuts.swift +++ b/wire-ios/Wire-iOS/Sources/WireAppShorcuts.swift @@ -17,14 +17,15 @@ // import AppIntents +import WireUtilities struct WireAppShorcuts: AppShortcutsProvider { - @AppShortcutsBuilder static var appShortcuts: [AppShortcut] { - if #available(iOS 18.0, *) { + static var appShortcuts: [AppShortcut] { + if DeveloperFlag.enablePerformBackupAction.isOn { return [AppShortcut(intent: PerformBackupIntent(), phrases: ["Backup data with \(.applicationName)"])] } else { - // Fallback on earlier versions return [] } + } }