Skip to content

Commit

Permalink
[path_provider] add getApplicationSupportDirectory (flutter#1566)
Browse files Browse the repository at this point in the history
* Add support for getApplicationLibraryDirectory
  • Loading branch information
collinjackson committed May 8, 2019
1 parent 9298f1c commit a7c074c
Show file tree
Hide file tree
Showing 6 changed files with 111 additions and 21 deletions.
10 changes: 10 additions & 0 deletions packages/path_provider/CHANGELOG.md
@@ -1,3 +1,13 @@
## 1.1.0

* Added `getApplicationSupportDirectory`.
* Updated documentation for `getApplicationDocumentsDirectory` to suggest
using `getApplicationSupportDirectory` on iOS and
`getExternalStorageDirectory` on Android.
* Updated documentation for `getTemporaryDirectory` to suggest using it
for caches of files that do not need to be backed up.
* Updated integration tests and example to reflect the above changes.

## 1.0.0

* Added integration tests.
Expand Down
22 changes: 22 additions & 0 deletions packages/path_provider/example/lib/main.dart
Expand Up @@ -35,6 +35,7 @@ class MyHomePage extends StatefulWidget {

class _MyHomePageState extends State<MyHomePage> {
Future<Directory> _tempDirectory;
Future<Directory> _appSupportDirectory;
Future<Directory> _appDocumentsDirectory;
Future<Directory> _externalDocumentsDirectory;

Expand Down Expand Up @@ -65,6 +66,12 @@ class _MyHomePageState extends State<MyHomePage> {
});
}

void _requestAppSupportDirectory() {
setState(() {
_appSupportDirectory = getApplicationSupportDirectory();
});
}

void _requestExternalStorageDirectory() {
setState(() {
_externalDocumentsDirectory = getExternalStorageDirectory();
Expand Down Expand Up @@ -111,6 +118,21 @@ class _MyHomePageState extends State<MyHomePage> {
child: FutureBuilder<Directory>(
future: _appDocumentsDirectory, builder: _buildDirectory),
),
Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(16.0),
child: RaisedButton(
child: const Text('Get Application Support Directory'),
onPressed: _requestAppSupportDirectory,
),
),
],
),
Expanded(
child: FutureBuilder<Directory>(
future: _appSupportDirectory, builder: _buildDirectory),
),
Column(children: <Widget>[
Padding(
padding: const EdgeInsets.all(16.0),
Expand Down
15 changes: 15 additions & 0 deletions packages/path_provider/example/test_driver/path_provider.dart
Expand Up @@ -35,6 +35,21 @@ void main() {
file.deleteSync();
});

test('getApplicationSupportDirectory', () async {
if (Platform.isIOS) {
final Directory result = await getApplicationSupportDirectory();
final String uuid = Uuid().v1();
final File file = File('${result.path}/$uuid.txt');
file.writeAsStringSync('Hello world!');
expect(file.readAsStringSync(), 'Hello world!');
expect(result.listSync(), isNotEmpty);
file.deleteSync();
} else if (Platform.isAndroid) {
final Future<Directory> result = getApplicationSupportDirectory();
expect(result, throwsA(isInstanceOf<UnsupportedError>()));
}
});

test('getExternalStorageDirectory', () async {
if (Platform.isIOS) {
final Future<Directory> result = getExternalStorageDirectory();
Expand Down
26 changes: 26 additions & 0 deletions packages/path_provider/ios/Classes/PathProviderPlugin.m
Expand Up @@ -9,6 +9,13 @@
return paths.firstObject;
}

static FlutterError* getFlutterError(NSError* error) {
if (error == nil) return nil;
return [FlutterError errorWithCode:[NSString stringWithFormat:@"Error %ld", error.code]
message:error.domain
details:error.localizedDescription];
}

@implementation FLTPathProviderPlugin

+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
Expand All @@ -20,6 +27,21 @@ + (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
result([self getTemporaryDirectory]);
} 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);
}
} else {
result(FlutterMethodNotImplemented);
}
Expand All @@ -34,4 +56,8 @@ + (NSString*)getApplicationDocumentsDirectory {
return GetDirectoryOfType(NSDocumentDirectory);
}

+ (NSString*)getApplicationSupportDirectory {
return GetDirectoryOfType(NSApplicationSupportDirectory);
}

@end
57 changes: 37 additions & 20 deletions packages/path_provider/lib/path_provider.dart
Expand Up @@ -10,7 +10,8 @@ import 'package:flutter/services.dart';
const MethodChannel _channel =
MethodChannel('plugins.flutter.io/path_provider');

/// Path to the temporary directory on the device.
/// Path to the temporary directory on the device that is not backed up and is
/// suitable for storing caches of downloaded files.
///
/// Files in this directory may be cleared at any time. This does *not* return
/// a new temporary directory. Instead, the caller is responsible for creating
Expand All @@ -21,29 +22,47 @@ const MethodChannel _channel =
///
/// On Android, this uses the `getCacheDir` API on the context.
Future<Directory> getTemporaryDirectory() async {
// TODO(amirh): remove this on when the invokeMethod update makes it to stable Flutter.
// https://github.com/flutter/flutter/issues/26431
// ignore: strong_mode_implicit_dynamic_method
final String path = await _channel.invokeMethod('getTemporaryDirectory');
final String path =
await _channel.invokeMethod<String>('getTemporaryDirectory');
if (path == null) {
return null;
}
return Directory(path);
}

/// Path to a directory where the application may place application support
/// files.
///
/// Use this for files you don’t want exposed to the user. Your app should not
/// use this directory for user data files.
///
/// On iOS, this uses the `NSApplicationSupportDirectory` API.
/// If this directory does not exist, it is created automatically.
///
/// On Android, this function throws an [UnsupportedError].
Future<Directory> getApplicationSupportDirectory() async {
if (!Platform.isIOS)
throw UnsupportedError("getApplicationSupportDirectory requires iOS");
final String path =
await _channel.invokeMethod<String>('getApplicationSupportDirectory');
if (path == null) {
return null;
}
return Directory(path);
}

/// Path to a directory where the application may place files that are private
/// to the application and will only be cleared when the application itself
/// is deleted.
/// Path to a directory where the application may place data that is
/// user-generated, or that cannot otherwise be recreated by your application.
///
/// On iOS, this uses the `NSDocumentsDirectory` API.
/// On iOS, this uses the `NSDocumentDirectory` API. Consider using
/// [getApplicationSupportDirectory] instead if the data is not user-generated.
///
/// On Android, this returns the AppData directory.
/// On Android, this uses the `getDataDirectory` API on the context. Consider
/// using getExternalStorageDirectory instead if data is intended to be visible
/// to the user.
Future<Directory> getApplicationDocumentsDirectory() async {
final String path =
// TODO(amirh): remove this on when the invokeMethod update makes it to stable Flutter.
// https://github.com/flutter/flutter/issues/26431
// ignore: strong_mode_implicit_dynamic_method
await _channel.invokeMethod('getApplicationDocumentsDirectory');
await _channel.invokeMethod<String>('getApplicationDocumentsDirectory');
if (path == null) {
return null;
}
Expand All @@ -54,17 +73,15 @@ Future<Directory> getApplicationDocumentsDirectory() async {
/// The current operating system should be determined before issuing this
/// function call, as this functionality is only available on Android.
///
/// On iOS, this function throws an UnsupportedError as it is not possible
/// On iOS, this function throws an [UnsupportedError] as it is not possible
/// to access outside the app's sandbox.
///
/// On Android this returns getExternalStorageDirectory.
/// On Android this uses the `getExternalStorageDirectory` API.
Future<Directory> getExternalStorageDirectory() async {
if (Platform.isIOS)
throw UnsupportedError("Functionality not available on iOS");
// TODO(amirh): remove this on when the invokeMethod update makes it to stable Flutter.
// https://github.com/flutter/flutter/issues/26431
// ignore: strong_mode_implicit_dynamic_method
final String path = await _channel.invokeMethod('getStorageDirectory');
final String path =
await _channel.invokeMethod<String>('getStorageDirectory');
if (path == null) {
return null;
}
Expand Down
2 changes: 1 addition & 1 deletion packages/path_provider/pubspec.yaml
Expand Up @@ -3,7 +3,7 @@ description: Flutter plugin for getting commonly used locations on the Android &
iOS file systems, such as the temp and app data directories.
author: Flutter Team <flutter-dev@googlegroups.com>
homepage: https://github.com/flutter/plugins/tree/master/packages/path_provider
version: 1.0.0
version: 1.1.0

flutter:
plugin:
Expand Down

0 comments on commit a7c074c

Please sign in to comment.