From a1158a740f1c8fb597c735112fb43467e378ad88 Mon Sep 17 00:00:00 2001 From: Timilehin Date: Wed, 5 Nov 2025 17:30:33 +0100 Subject: [PATCH 1/2] feature: add getICloudContainerUrl method to iCloud storage plugin. --- ios/Classes/IcloudStorageSyncPlugin.swift | 16 ++++++++++++++++ lib/icloud_storage_sync.dart | 7 +++++++ lib/icloud_storage_sync_method_channel.dart | 7 +++++++ lib/icloud_storage_sync_platform_interface.dart | 5 +++++ macos/Classes/IcloudStorageSyncPlugin.swift | 16 +++++++++++++++- test/icloud_storage_sync_test.dart | 4 ++++ 6 files changed, 54 insertions(+), 1 deletion(-) diff --git a/ios/Classes/IcloudStorageSyncPlugin.swift b/ios/Classes/IcloudStorageSyncPlugin.swift index 8edbbfb..d974c63 100644 --- a/ios/Classes/IcloudStorageSyncPlugin.swift +++ b/ios/Classes/IcloudStorageSyncPlugin.swift @@ -27,6 +27,13 @@ public class IcloudStorageSyncPlugin: NSObject, FlutterPlugin { } else { result(FlutterError(code: "INVALID_ARGUMENT", message: "containerId not provided", details: nil)) } + case "getICloudContainerUrl": + if let args = call.arguments as? [String: Any], + let containerId = args["containerId"] as? String { + getICloudContainerUrl(containerId: containerId, result: result) + } else { + result(FlutterError(code: "INVALID_ARGUMENT", message: "containerId not provided", details: nil)) + } case "upload": upload(call, result) case "delete": @@ -157,6 +164,15 @@ public class IcloudStorageSyncPlugin: NSObject, FlutterPlugin { } } + private func getICloudContainerUrl(containerId: String, result: @escaping FlutterResult) { + guard let containerURL = FileManager.default.url(forUbiquityContainerIdentifier: containerId) else { + result(containerError) + return + } + DebugHelper.log("containerURL: \(containerURL.path)") + result(containerURL.path) + } + private func upload(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { guard let args = call.arguments as? Dictionary, diff --git a/lib/icloud_storage_sync.dart b/lib/icloud_storage_sync.dart index 43f4ed6..e9f25cb 100644 --- a/lib/icloud_storage_sync.dart +++ b/lib/icloud_storage_sync.dart @@ -13,6 +13,13 @@ class IcloudStorageSync { return IcloudStorageSyncPlatform.instance.getPlatformVersion(); } + /// Returns the absolute path of the iCloud container directory for [containerId]. + Future getContainerUrl({required String containerId}) { + return IcloudStorageSyncPlatform.instance.getContainerUrl( + containerId: containerId, + ); + } + /// Gathers metadata for all files in the specified iCloud container. /// /// [containerId] The ID of the iCloud container to query. diff --git a/lib/icloud_storage_sync_method_channel.dart b/lib/icloud_storage_sync_method_channel.dart index c34dc37..43b8179 100644 --- a/lib/icloud_storage_sync_method_channel.dart +++ b/lib/icloud_storage_sync_method_channel.dart @@ -19,6 +19,13 @@ class MethodChannelIcloudStorageSync extends IcloudStorageSyncPlatform { return version; } + @override + Future getContainerUrl({required String containerId}) async { + return await methodChannel.invokeMethod('getICloudContainerUrl', { + 'containerId': containerId, + }); + } + /// Gathers iCloud files and their metadata. /// /// [containerId] is the iCloud container identifier. diff --git a/lib/icloud_storage_sync_platform_interface.dart b/lib/icloud_storage_sync_platform_interface.dart index 03b526e..6851118 100644 --- a/lib/icloud_storage_sync_platform_interface.dart +++ b/lib/icloud_storage_sync_platform_interface.dart @@ -35,6 +35,11 @@ abstract class IcloudStorageSyncPlatform extends PlatformInterface { throw UnimplementedError('platformVersion() has not been implemented.'); } + /// Returns the absolute path of the iCloud container URL. + Future getContainerUrl({required String containerId}) async { + throw UnimplementedError('getContainerUrl() has not been implemented.'); + } + /// Gathers all the files' metadata from the iCloud container. /// /// [containerId] is the iCloud Container Id. diff --git a/macos/Classes/IcloudStorageSyncPlugin.swift b/macos/Classes/IcloudStorageSyncPlugin.swift index 6bb12f0..b6e0e8a 100644 --- a/macos/Classes/IcloudStorageSyncPlugin.swift +++ b/macos/Classes/IcloudStorageSyncPlugin.swift @@ -25,6 +25,13 @@ public class IcloudStorageSyncPlugin: NSObject, FlutterPlugin { } else { result(FlutterError(code: "INVALID_ARGUMENT", message: "containerId not provided", details: nil)) } + case "getICloudContainerUrl": + if let args = call.arguments as? [String: Any], + let containerId = args["containerId"] as? String { + getICloudContainerUrl(containerId: containerId, result: result) + } else { + result(FlutterError(code: "INVALID_ARGUMENT", message: "containerId not provided", details: nil)) + } case "upload": upload(call, result) case "download": @@ -205,8 +212,15 @@ public class IcloudStorageSyncPlugin: NSObject, FlutterPlugin { result(nil) } - + private func getICloudContainerUrl(containerId: String, result: @escaping FlutterResult) { + guard let containerURL = FileManager.default.url(forUbiquityContainerIdentifier: containerId) else { + result(containerError) + return + } + DebugHelper.log("containerURL: \(containerURL.path)") + result(containerURL.path) + } private func addUploadObservers(query: NSMetadataQuery, eventChannelName: String) { NotificationCenter.default.addObserver(forName: NSNotification.Name.NSMetadataQueryDidFinishGathering, object: query, queue: query.operationQueue) { [self] (notification) in diff --git a/test/icloud_storage_sync_test.dart b/test/icloud_storage_sync_test.dart index fc0e642..2ec9391 100644 --- a/test/icloud_storage_sync_test.dart +++ b/test/icloud_storage_sync_test.dart @@ -11,6 +11,10 @@ class MockIcloudStorageSyncPlatform @override Future getPlatformVersion() => Future.value('42'); + @override + Future getContainerUrl({required String containerId}) => + throw UnimplementedError(); + @override Future delete( {required String containerId, required String relativePath}) { From a3e669b0d5b6495210cc72b49927e807a2d9c389 Mon Sep 17 00:00:00 2001 From: Timilehin Date: Wed, 12 Nov 2025 11:43:21 +0100 Subject: [PATCH 2/2] feature: add isICloudAvailable method to iCloud storage plugin. --- ios/Classes/IcloudStorageSyncPlugin.swift | 7 +++++++ lib/icloud_storage_sync.dart | 9 +++++++++ lib/icloud_storage_sync_method_channel.dart | 8 ++++++++ lib/icloud_storage_sync_platform_interface.dart | 7 +++++++ macos/Classes/IcloudStorageSyncPlugin.swift | 7 +++++++ test/icloud_storage_sync_test.dart | 5 +++++ 6 files changed, 43 insertions(+) diff --git a/ios/Classes/IcloudStorageSyncPlugin.swift b/ios/Classes/IcloudStorageSyncPlugin.swift index d974c63..6e49076 100644 --- a/ios/Classes/IcloudStorageSyncPlugin.swift +++ b/ios/Classes/IcloudStorageSyncPlugin.swift @@ -34,6 +34,8 @@ public class IcloudStorageSyncPlugin: NSObject, FlutterPlugin { } else { result(FlutterError(code: "INVALID_ARGUMENT", message: "containerId not provided", details: nil)) } + case "isICloudAvailable": + isICloudAvailable(result) case "upload": upload(call, result) case "delete": @@ -173,6 +175,11 @@ public class IcloudStorageSyncPlugin: NSObject, FlutterPlugin { result(containerURL.path) } + private func isICloudAvailable(_ result: @escaping FlutterResult) { + let isAvailable = FileManager.default.ubiquityIdentityToken != nil + result(isAvailable) + } + private func upload(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { guard let args = call.arguments as? Dictionary, diff --git a/lib/icloud_storage_sync.dart b/lib/icloud_storage_sync.dart index e9f25cb..e236cce 100644 --- a/lib/icloud_storage_sync.dart +++ b/lib/icloud_storage_sync.dart @@ -20,6 +20,15 @@ class IcloudStorageSync { ); } + /// Indicates whether iCloud is available on this device for iCloud Drive Documents. + /// + /// This checks if the system exposes a ubiquity identity token, which is + /// present only when the user is signed into iCloud and the feature is + /// enabled. + Future isICloudAvailable() { + return IcloudStorageSyncPlatform.instance.isICloudAvailable(); + } + /// Gathers metadata for all files in the specified iCloud container. /// /// [containerId] The ID of the iCloud container to query. diff --git a/lib/icloud_storage_sync_method_channel.dart b/lib/icloud_storage_sync_method_channel.dart index 43b8179..b5b5085 100644 --- a/lib/icloud_storage_sync_method_channel.dart +++ b/lib/icloud_storage_sync_method_channel.dart @@ -26,6 +26,14 @@ class MethodChannelIcloudStorageSync extends IcloudStorageSyncPlatform { }); } + @override + Future isICloudAvailable() async { + final available = await methodChannel.invokeMethod( + 'isICloudAvailable', + ); + return available ?? false; + } + /// Gathers iCloud files and their metadata. /// /// [containerId] is the iCloud container identifier. diff --git a/lib/icloud_storage_sync_platform_interface.dart b/lib/icloud_storage_sync_platform_interface.dart index 6851118..c20cee6 100644 --- a/lib/icloud_storage_sync_platform_interface.dart +++ b/lib/icloud_storage_sync_platform_interface.dart @@ -40,6 +40,13 @@ abstract class IcloudStorageSyncPlatform extends PlatformInterface { throw UnimplementedError('getContainerUrl() has not been implemented.'); } + /// Determines whether iCloud is available for iCloud Drive Documents by checking the ubiquity identity token. + /// + /// Returns `true` when a token is present otherwise returns `false`. + Future isICloudAvailable() async { + throw UnimplementedError('isICloudAvailable() has not been implemented.'); + } + /// Gathers all the files' metadata from the iCloud container. /// /// [containerId] is the iCloud Container Id. diff --git a/macos/Classes/IcloudStorageSyncPlugin.swift b/macos/Classes/IcloudStorageSyncPlugin.swift index b6e0e8a..9c81601 100644 --- a/macos/Classes/IcloudStorageSyncPlugin.swift +++ b/macos/Classes/IcloudStorageSyncPlugin.swift @@ -32,6 +32,8 @@ public class IcloudStorageSyncPlugin: NSObject, FlutterPlugin { } else { result(FlutterError(code: "INVALID_ARGUMENT", message: "containerId not provided", details: nil)) } + case "isICloudAvailable": + isICloudAvailable(result) case "upload": upload(call, result) case "download": @@ -222,6 +224,11 @@ public class IcloudStorageSyncPlugin: NSObject, FlutterPlugin { result(containerURL.path) } + private func isICloudAvailable(_ result: @escaping FlutterResult) { + let isAvailable = FileManager.default.ubiquityIdentityToken != nil + result(isAvailable) + } + private func addUploadObservers(query: NSMetadataQuery, eventChannelName: String) { NotificationCenter.default.addObserver(forName: NSNotification.Name.NSMetadataQueryDidFinishGathering, object: query, queue: query.operationQueue) { [self] (notification) in onUploadQueryNotification(query: query, eventChannelName: eventChannelName) diff --git a/test/icloud_storage_sync_test.dart b/test/icloud_storage_sync_test.dart index 2ec9391..82fe549 100644 --- a/test/icloud_storage_sync_test.dart +++ b/test/icloud_storage_sync_test.dart @@ -58,6 +58,11 @@ class MockIcloudStorageSyncPlatform StreamHandler? onProgress}) { throw UnimplementedError(); } + + @override + Future isICloudAvailable() { + throw UnimplementedError(); + } } void main() {