From 4a3e9f9aa9e8042459aa254dee2ee9791174120e Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Thu, 24 Feb 2022 14:28:53 -0500 Subject: [PATCH 1/3] Add a package-internal method channel --- .../path_provider_ios/CHANGELOG.md | 4 + .../ios/Classes/FLTPathProviderPlugin.m | 2 +- .../lib/path_provider_ios.dart | 63 +++++++++ .../path_provider_ios/pubspec.yaml | 5 +- .../test/path_provider_ios_test.dart | 128 ++++++++++++++++++ 5 files changed, 199 insertions(+), 3 deletions(-) create mode 100644 packages/path_provider/path_provider_ios/lib/path_provider_ios.dart create mode 100644 packages/path_provider/path_provider_ios/test/path_provider_ios_test.dart diff --git a/packages/path_provider/path_provider_ios/CHANGELOG.md b/packages/path_provider/path_provider_ios/CHANGELOG.md index eb155d138218..543af778d2e2 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 063e028f4d3c..495b2cfb3f56 100644 --- a/packages/path_provider/path_provider_ios/ios/Classes/FLTPathProviderPlugin.m +++ b/packages/path_provider/path_provider_ios/ios/Classes/FLTPathProviderPlugin.m @@ -20,7 +20,7 @@ @implementation FLTPathProviderPlugin + (void)registerWithRegistrar:(NSObject *)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]) { 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 000000000000..4a970679d95d --- /dev/null +++ b/packages/path_provider/path_provider_ios/lib/path_provider_ios.dart @@ -0,0 +1,63 @@ +// 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 '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 getTemporaryPath() { + return methodChannel.invokeMethod('getTemporaryDirectory'); + } + + @override + Future getApplicationSupportPath() { + return methodChannel.invokeMethod('getApplicationSupportDirectory'); + } + + @override + Future getLibraryPath() { + return methodChannel.invokeMethod('getLibraryDirectory'); + } + + @override + Future getApplicationDocumentsPath() { + return methodChannel + .invokeMethod('getApplicationDocumentsDirectory'); + } + + @override + Future getExternalStoragePath() { + throw UnsupportedError('getExternalStoragePath is not supported on iOS'); + } + + @override + Future?> getExternalCachePaths() { + throw UnsupportedError('getExternalCachePaths is not supported on iOS'); + } + + @override + Future?> getExternalStoragePaths({ + StorageDirectory? type, + }) async { + throw UnsupportedError('getExternalStoragePaths is not supported on iOS'); + } + + @override + Future getDownloadsPath() { + 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 0e05121f3052..ce00faddcb42 100644 --- a/packages/path_provider/path_provider_ios/pubspec.yaml +++ b/packages/path_provider/path_provider_ios/pubspec.yaml @@ -2,11 +2,11 @@ name: path_provider_ios 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 @@ flutter: platforms: ios: pluginClass: FLTPathProviderPlugin + dartPluginClass: PathProviderIOS dependencies: flutter: 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 000000000000..40f81c5f445a --- /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 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 = []; + 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, + [isMethodCall('getTemporaryDirectory', arguments: null)], + ); + expect(path, temporaryPath); + }); + + test('getApplicationSupportPath', () async { + final String? path = await pathProvider.getApplicationSupportPath(); + expect( + log, + [ + 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, + [isMethodCall('getLibraryDirectory', arguments: null)], + ); + expect(path, libraryPath); + }); + + test('getApplicationDocumentsPath', () async { + final String? path = await pathProvider.getApplicationDocumentsPath(); + expect( + log, + [ + 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)); + }); + }); +} From 53778bbcabc23e73077b07d51ed50c76e89567e5 Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Thu, 24 Feb 2022 14:34:05 -0500 Subject: [PATCH 2/3] Move directory creation to Dart, as on macOS --- .../ios/Classes/FLTPathProviderPlugin.m | 22 +--------------- .../lib/path_provider_ios.dart | 25 +++++++++++++------ 2 files changed, 18 insertions(+), 29 deletions(-) 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 495b2cfb3f56..ac6a1be9414f 100644 --- a/packages/path_provider/path_provider_ios/ios/Classes/FLTPathProviderPlugin.m +++ b/packages/path_provider/path_provider_ios/ios/Classes/FLTPathProviderPlugin.m @@ -9,13 +9,6 @@ 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 *)registrar { @@ -28,20 +21,7 @@ + (void)registerWithRegistrar:(NSObject *)registrar { } 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 index 4a970679d95d..88becf20850e 100644 --- a/packages/path_provider/path_provider_ios/lib/path_provider_ios.dart +++ b/packages/path_provider/path_provider_ios/lib/path_provider_ios.dart @@ -2,6 +2,8 @@ // 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'; @@ -19,33 +21,40 @@ class PathProviderIOS extends PathProviderPlatform { } @override - Future getTemporaryPath() { + Future getTemporaryPath() async { return methodChannel.invokeMethod('getTemporaryDirectory'); } @override - Future getApplicationSupportPath() { - return methodChannel.invokeMethod('getApplicationSupportDirectory'); + Future getApplicationSupportPath() async { + final String? path = await methodChannel + .invokeMethod('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 getLibraryPath() { + Future getLibraryPath() async { return methodChannel.invokeMethod('getLibraryDirectory'); } @override - Future getApplicationDocumentsPath() { + Future getApplicationDocumentsPath() async { return methodChannel .invokeMethod('getApplicationDocumentsDirectory'); } @override - Future getExternalStoragePath() { + Future getExternalStoragePath() async { throw UnsupportedError('getExternalStoragePath is not supported on iOS'); } @override - Future?> getExternalCachePaths() { + Future?> getExternalCachePaths() async { throw UnsupportedError('getExternalCachePaths is not supported on iOS'); } @@ -57,7 +66,7 @@ class PathProviderIOS extends PathProviderPlatform { } @override - Future getDownloadsPath() { + Future getDownloadsPath() async { throw UnsupportedError('getDownloadsPath is not supported on iOS'); } } From 7366e3512328d1df1ee1d711564140013b9ea293 Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Thu, 24 Feb 2022 14:48:34 -0500 Subject: [PATCH 3/3] Add path --- packages/path_provider/path_provider_ios/pubspec.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/path_provider/path_provider_ios/pubspec.yaml b/packages/path_provider/path_provider_ios/pubspec.yaml index ce00faddcb42..282f8e4f0ebd 100644 --- a/packages/path_provider/path_provider_ios/pubspec.yaml +++ b/packages/path_provider/path_provider_ios/pubspec.yaml @@ -28,5 +28,6 @@ dev_dependencies: sdk: flutter integration_test: sdk: flutter + path: ^1.8.0 plugin_platform_interface: ^2.0.0 test: ^1.16.0