[path_provider] Switch iOS to an internal method channel (#4921)
diff --git a/packages/path_provider/path_provider_ios/CHANGELOG.md b/packages/path_provider/path_provider_ios/CHANGELOG.md
index eb155d1..543af77 100644
--- a/packages/path_provider/path_provider_ios/CHANGELOG.md
+++ b/packages/path_provider/path_provider_ios/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 2.0.8
+
+* Switches to a package-internal implementation of the platform interface.
+
## 2.0.7
* Fixes link in README.
diff --git a/packages/path_provider/path_provider_ios/ios/Classes/FLTPathProviderPlugin.m b/packages/path_provider/path_provider_ios/ios/Classes/FLTPathProviderPlugin.m
index 063e028..ac6a1be 100644
--- a/packages/path_provider/path_provider_ios/ios/Classes/FLTPathProviderPlugin.m
+++ b/packages/path_provider/path_provider_ios/ios/Classes/FLTPathProviderPlugin.m
@@ -9,18 +9,11 @@
return paths.firstObject;
}
-static FlutterError *getFlutterError(NSError *error) {
- if (error == nil) return nil;
- return [FlutterError errorWithCode:[NSString stringWithFormat:@"Error %ld", (long)error.code]
- message:error.domain
- details:error.localizedDescription];
-}
-
@implementation FLTPathProviderPlugin
+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar> *)registrar {
FlutterMethodChannel *channel =
- [FlutterMethodChannel methodChannelWithName:@"plugins.flutter.io/path_provider"
+ [FlutterMethodChannel methodChannelWithName:@"plugins.flutter.io/path_provider_ios"
binaryMessenger:registrar.messenger];
[channel setMethodCallHandler:^(FlutterMethodCall *call, FlutterResult result) {
if ([@"getTemporaryDirectory" isEqualToString:call.method]) {
@@ -28,20 +21,7 @@
} else if ([@"getApplicationDocumentsDirectory" isEqualToString:call.method]) {
result([self getApplicationDocumentsDirectory]);
} else if ([@"getApplicationSupportDirectory" isEqualToString:call.method]) {
- NSString *path = [self getApplicationSupportDirectory];
-
- // Create the path if it doesn't exist
- NSError *error;
- NSFileManager *fileManager = [NSFileManager defaultManager];
- BOOL success = [fileManager createDirectoryAtPath:path
- withIntermediateDirectories:YES
- attributes:nil
- error:&error];
- if (!success) {
- result(getFlutterError(error));
- } else {
- result(path);
- }
+ result([self getApplicationSupportDirectory]);
} else if ([@"getLibraryDirectory" isEqualToString:call.method]) {
result([self getLibraryDirectory]);
} else {
diff --git a/packages/path_provider/path_provider_ios/lib/path_provider_ios.dart b/packages/path_provider/path_provider_ios/lib/path_provider_ios.dart
new file mode 100644
index 0000000..88becf2
--- /dev/null
+++ b/packages/path_provider/path_provider_ios/lib/path_provider_ios.dart
@@ -0,0 +1,72 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'dart:io';
+
+import 'package:flutter/foundation.dart' show visibleForTesting;
+import 'package:flutter/services.dart';
+import 'package:path_provider_platform_interface/path_provider_platform_interface.dart';
+
+/// The iOS implementation of [PathProviderPlatform].
+class PathProviderIOS extends PathProviderPlatform {
+ /// The method channel used to interact with the native platform.
+ @visibleForTesting
+ MethodChannel methodChannel =
+ const MethodChannel('plugins.flutter.io/path_provider_ios');
+
+ /// Registers this class as the default instance of [PathProviderPlatform]
+ static void registerWith() {
+ PathProviderPlatform.instance = PathProviderIOS();
+ }
+
+ @override
+ Future<String?> getTemporaryPath() async {
+ return methodChannel.invokeMethod<String>('getTemporaryDirectory');
+ }
+
+ @override
+ Future<String?> getApplicationSupportPath() async {
+ final String? path = await methodChannel
+ .invokeMethod<String>('getApplicationSupportDirectory');
+ if (path != null) {
+ // Ensure the directory exists before returning it, for consistency with
+ // other platforms.
+ await Directory(path).create(recursive: true);
+ }
+ return path;
+ }
+
+ @override
+ Future<String?> getLibraryPath() async {
+ return methodChannel.invokeMethod<String>('getLibraryDirectory');
+ }
+
+ @override
+ Future<String?> getApplicationDocumentsPath() async {
+ return methodChannel
+ .invokeMethod<String>('getApplicationDocumentsDirectory');
+ }
+
+ @override
+ Future<String?> getExternalStoragePath() async {
+ throw UnsupportedError('getExternalStoragePath is not supported on iOS');
+ }
+
+ @override
+ Future<List<String>?> getExternalCachePaths() async {
+ throw UnsupportedError('getExternalCachePaths is not supported on iOS');
+ }
+
+ @override
+ Future<List<String>?> getExternalStoragePaths({
+ StorageDirectory? type,
+ }) async {
+ throw UnsupportedError('getExternalStoragePaths is not supported on iOS');
+ }
+
+ @override
+ Future<String?> getDownloadsPath() async {
+ throw UnsupportedError('getDownloadsPath is not supported on iOS');
+ }
+}
diff --git a/packages/path_provider/path_provider_ios/pubspec.yaml b/packages/path_provider/path_provider_ios/pubspec.yaml
index 0e05121..282f8e4 100644
--- a/packages/path_provider/path_provider_ios/pubspec.yaml
+++ b/packages/path_provider/path_provider_ios/pubspec.yaml
@@ -2,11 +2,11 @@
description: iOS implementation of the path_provider plugin.
repository: https://github.com/flutter/plugins/tree/main/packages/path_provider/path_provider_ios
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22
-version: 2.0.7
+version: 2.0.8
environment:
sdk: ">=2.14.0 <3.0.0"
- flutter: ">=2.5.0"
+ flutter: ">=2.8.0"
flutter:
plugin:
@@ -14,6 +14,7 @@
platforms:
ios:
pluginClass: FLTPathProviderPlugin
+ dartPluginClass: PathProviderIOS
dependencies:
flutter:
@@ -27,5 +28,6 @@
sdk: flutter
integration_test:
sdk: flutter
+ path: ^1.8.0
plugin_platform_interface: ^2.0.0
test: ^1.16.0
diff --git a/packages/path_provider/path_provider_ios/test/path_provider_ios_test.dart b/packages/path_provider/path_provider_ios/test/path_provider_ios_test.dart
new file mode 100644
index 0000000..40f81c5
--- /dev/null
+++ b/packages/path_provider/path_provider_ios/test/path_provider_ios_test.dart
@@ -0,0 +1,128 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'dart:io';
+
+import 'package:flutter/services.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:path/path.dart' as p;
+import 'package:path_provider_ios/path_provider_ios.dart';
+
+void main() {
+ TestWidgetsFlutterBinding.ensureInitialized();
+
+ group('PathProviderIOS', () {
+ late PathProviderIOS pathProvider;
+ late List<MethodCall> log;
+ // These unit tests use the actual filesystem, since an injectable
+ // filesystem would add a runtime dependency to the package, so everything
+ // is contained to a temporary directory.
+ late Directory testRoot;
+
+ late String temporaryPath;
+ late String applicationSupportPath;
+ late String libraryPath;
+ late String applicationDocumentsPath;
+
+ setUp(() async {
+ pathProvider = PathProviderIOS();
+
+ testRoot = Directory.systemTemp.createTempSync();
+ final String basePath = testRoot.path;
+ temporaryPath = p.join(basePath, 'temporary', 'path');
+ applicationSupportPath =
+ p.join(basePath, 'application', 'support', 'path');
+ libraryPath = p.join(basePath, 'library', 'path');
+ applicationDocumentsPath =
+ p.join(basePath, 'application', 'documents', 'path');
+
+ log = <MethodCall>[];
+ TestDefaultBinaryMessengerBinding.instance!.defaultBinaryMessenger
+ .setMockMethodCallHandler(pathProvider.methodChannel,
+ (MethodCall methodCall) async {
+ log.add(methodCall);
+ switch (methodCall.method) {
+ case 'getTemporaryDirectory':
+ return temporaryPath;
+ case 'getApplicationSupportDirectory':
+ return applicationSupportPath;
+ case 'getLibraryDirectory':
+ return libraryPath;
+ case 'getApplicationDocumentsDirectory':
+ return applicationDocumentsPath;
+ default:
+ return null;
+ }
+ });
+ });
+
+ tearDown(() {
+ testRoot.deleteSync(recursive: true);
+ });
+
+ test('getTemporaryPath', () async {
+ final String? path = await pathProvider.getTemporaryPath();
+ expect(
+ log,
+ <Matcher>[isMethodCall('getTemporaryDirectory', arguments: null)],
+ );
+ expect(path, temporaryPath);
+ });
+
+ test('getApplicationSupportPath', () async {
+ final String? path = await pathProvider.getApplicationSupportPath();
+ expect(
+ log,
+ <Matcher>[
+ isMethodCall('getApplicationSupportDirectory', arguments: null)
+ ],
+ );
+ expect(path, applicationSupportPath);
+ });
+
+ test('getApplicationSupportPath creates the directory if necessary',
+ () async {
+ final String? path = await pathProvider.getApplicationSupportPath();
+ expect(Directory(path!).existsSync(), isTrue);
+ });
+
+ test('getLibraryPath', () async {
+ final String? path = await pathProvider.getLibraryPath();
+ expect(
+ log,
+ <Matcher>[isMethodCall('getLibraryDirectory', arguments: null)],
+ );
+ expect(path, libraryPath);
+ });
+
+ test('getApplicationDocumentsPath', () async {
+ final String? path = await pathProvider.getApplicationDocumentsPath();
+ expect(
+ log,
+ <Matcher>[
+ isMethodCall('getApplicationDocumentsDirectory', arguments: null)
+ ],
+ );
+ expect(path, applicationDocumentsPath);
+ });
+
+ test('getDownloadsPath throws', () async {
+ expect(pathProvider.getDownloadsPath(), throwsA(isUnsupportedError));
+ });
+
+ test('getExternalCachePaths throws', () async {
+ expect(pathProvider.getExternalCachePaths(), throwsA(isUnsupportedError));
+ });
+
+ test('getExternalStoragePath throws', () async {
+ expect(
+ pathProvider.getExternalStoragePath(), throwsA(isUnsupportedError));
+ });
+
+ test('getExternalStoragePaths throws', () async {
+ expect(
+ pathProvider.getExternalStoragePaths(), throwsA(isUnsupportedError));
+ });
+ });
+}