Skip to content

Commit b374979

Browse files
tripodsanshazron
authored andcommitted
CB-9544 Add file plugin for OSX
- defining osx filesystems roots - updating readme to include osx - updating test case for osx
1 parent c3b5778 commit b374979

File tree

4 files changed

+118
-105
lines changed

4 files changed

+118
-105
lines changed

README.md

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -59,11 +59,11 @@ Although in the global scope, it is not available until after the `deviceready`
5959
- BlackBerry 10
6060
- Firefox OS
6161
- iOS
62+
- OS X
6263
- Windows Phone 7 and 8*
6364
- Windows 8*
6465
- Windows*
6566
- Browser
66-
- OS X
6767

6868
\* _These platforms do not support `FileReader.readAsArrayBuffer` nor `FileWriter.write(blob)`._
6969

@@ -74,12 +74,12 @@ Each URL is in the form _file:///path/to/spot/_, and can be converted to a
7474
`DirectoryEntry` using `window.resolveLocalFileSystemURL()`.
7575

7676
* `cordova.file.applicationDirectory` - Read-only directory where the application
77-
is installed. (_iOS_, _Android_, _BlackBerry 10_)
77+
is installed. (_iOS_, _Android_, _BlackBerry 10_, _OSX_)
7878

7979
* `cordova.file.applicationStorageDirectory` - Root directory of the application's
8080
sandbox; on iOS this location is read-only (but specific subdirectories [like
8181
`/Documents`] are read-write). All data contained within is private to the app. (
82-
_iOS_, _Android_, _BlackBerry 10_)
82+
_iOS_, _Android_, _BlackBerry 10_, _OSX_)
8383

8484
* `cordova.file.dataDirectory` - Persistent and private data storage within the
8585
application's sandbox using internal memory (on Android, if you need to use
@@ -89,7 +89,7 @@ Each URL is in the form _file:///path/to/spot/_, and can be converted to a
8989
* `cordova.file.cacheDirectory` - Directory for cached data files or any files
9090
that your app can re-create easily. The OS may delete these files when the device
9191
runs low on storage, nevertheless, apps should not rely on the OS to delete files
92-
in here. (_iOS_, _Android_, _BlackBerry 10_)
92+
in here. (_iOS_, _Android_, _BlackBerry 10_, _OSX_)
9393

9494
* `cordova.file.externalApplicationStorageDirectory` - Application space on
9595
external storage. (_Android_)
@@ -104,13 +104,13 @@ Each URL is in the form _file:///path/to/spot/_, and can be converted to a
104104

105105
* `cordova.file.tempDirectory` - Temp directory that the OS can clear at will. Do not
106106
rely on the OS to clear this directory; your app should always remove files as
107-
applicable. (_iOS_)
107+
applicable. (_iOS_, _OSX_)
108108

109109
* `cordova.file.syncedDataDirectory` - Holds app-specific files that should be synced
110110
(e.g. to iCloud). (_iOS_)
111111

112112
* `cordova.file.documentsDirectory` - Files private to the app, but that are meaningful
113-
to other application (e.g. Office files). (_iOS_)
113+
to other application (e.g. Office files). Note that for _OSX_ this is the user's `~/Documents` directory. (_iOS_, _OSX_)
114114

115115
* `cordova.file.sharedDirectory` - Files globally available to all applications (_BlackBerry 10_)
116116

@@ -186,6 +186,28 @@ properties are `null`.
186186

187187
*Note*: When application is deployed to work perimeter, all paths are relative to /accounts/1000-enterprise.
188188

189+
### OS X File System Layout
190+
191+
| Device Path | `cordova.file.*` | `iosExtraFileSystems` | r/w? | OS clears | private |
192+
|:-------------------------------------------------|:----------------------------|:----------------------|:----:|:---------:|:-------:|
193+
| `/Applications/<appname>.app` | applicationDirectory | bundle | r | N/A | Yes |
194+
| `~/Library/Application Support/<bundle-id>/` | applicationStorageDirectory | - | r/w | No | Yes |
195+
| &nbsp;&nbsp;&nbsp;&nbsp;`files/` | dataDirectory | - | r/w | No | Yes |
196+
| `~/Documents/` | documentsDirectory | documents | r/w | No | No |
197+
| `~/Library/Caches/<bundle-id>/` | cacheDirectory | cache | r/w | No | Yes |
198+
| `/tmp/` | tempDirectory | - | r/w | Yes\* | Yes |
199+
| `/` | rootDirectory | root | r/w | No\*\* | No |
200+
201+
**Note**: This is the layout for non sandboxed applications. I you enable sandboxing, the `applicationStorageDirectory` will be below ` ~/Library/Containers/<bundle-id>/Data/Library/Application Support`.
202+
203+
\* Files persist across app restarts and upgrades, but this directory can
204+
be cleared whenever the OS desires. Your app should be able to recreate any
205+
content that might be deleted. You should clear this directory as
206+
appropriate for your application.
207+
208+
\*\* Allows access to the entire file system. This is only available for non sandboxed apps.
209+
210+
189211
## Android Quirks
190212

191213
### Android Persistent storage location

src/osx/CDVFile.h

Lines changed: 40 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,22 @@
2020
#import <Foundation/Foundation.h>
2121
#import <Cordova/CDVPlugin.h>
2222

23-
NSString* const kCDVAssetsLibraryPrefix;
2423
NSString* const kCDVFilesystemURLPrefix;
2524

25+
/**
26+
* The default filesystems if non are specified.
27+
*/
28+
#define CDV_FILESYSTEMS_DEFAULT @"documents,cache,bundle,root"
29+
30+
/**
31+
* Preference name of the extra filesystems to be "mounted". the following are supported:
32+
* 'bundle' - mounts the application directory
33+
* 'documents' - mounts the users Documents directory (~/Documents)
34+
* 'root' - mounts the root file system
35+
* 'cache' - mounts the caches directory (~/Library/Caches/<bundle-id/)
36+
*/
37+
#define CDV_PREF_EXTRA_FILESYSTEM @"osxextrafilesystems"
38+
2639
enum CDVFileError {
2740
NO_ERROR = 0,
2841
NOT_FOUND_ERR = 1,
@@ -141,17 +154,36 @@ typedef int CDVFileError;
141154
/* Internal methods for testing */
142155
- (void)_getLocalFilesystemPath:(CDVInvokedUrlCommand*)command;
143156

144-
@property (nonatomic, strong) NSString* rootDocsPath;
157+
/**
158+
* local path of the 'documents' file system (~/Documents)
159+
*/
145160
@property (nonatomic, strong) NSString* appDocsPath;
146-
@property (nonatomic, strong) NSString* appLibraryPath;
161+
162+
/**
163+
* local path of the 'applicationStorageDirectory' file system (~/Library/Application Support/<bundle-id>)
164+
*/
165+
@property (nonatomic, strong) NSString* appSupportPath;
166+
167+
/**
168+
* local path of the 'persistent' file system (~/Library/Application Support/<bundle-id>/files)
169+
*/
170+
@property (nonatomic, strong) NSString* appDataPath;
171+
172+
/**
173+
* local path of the 'documents' file system (~/Documents)
174+
*/
147175
@property (nonatomic, strong) NSString* appTempPath;
148-
@property (nonatomic, strong) NSString* persistentPath;
149-
@property (nonatomic, strong) NSString* temporaryPath;
176+
177+
/**
178+
* local path of the 'cache' file system (~/Library/Caches/<bundle-id>)
179+
*/
180+
@property (nonatomic, strong) NSString* appCachePath;
181+
182+
/**
183+
* registered file systems
184+
*/
150185
@property (nonatomic, strong) NSMutableArray* fileSystems;
151186

152187
@property BOOL userHasAllowed;
153188

154189
@end
155-
156-
#define kW3FileTemporary @"temporary"
157-
#define kW3FilePersistent @"persistent"

src/osx/CDVFile.m

Lines changed: 48 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ - (NSCachedURLResponse *)connection:(NSURLConnection *)connection
184184

185185
@implementation CDVFile
186186

187-
@synthesize rootDocsPath, appDocsPath, appLibraryPath, appTempPath, userHasAllowed, fileSystems=fileSystems_;
187+
@synthesize appDocsPath, appDataPath, appSupportPath, appTempPath, appCachePath, userHasAllowed, fileSystems=fileSystems_;
188188

189189
- (void)registerFilesystem:(NSObject<CDVFileSystem> *)fs {
190190
__weak CDVFile* weakSelf = self;
@@ -235,12 +235,12 @@ - (NSArray *)getExtraFileSystemsPreference:(NSWindowController *)vc
235235
{
236236
NSString *filesystemsStr = nil;
237237
if([self.viewController isKindOfClass:[CDVViewController class]]) {
238-
CDVViewController *vc = (CDVViewController *)self.viewController;
238+
CDVViewController *vc = self.viewController;
239239
NSDictionary *settings = [vc settings];
240-
filesystemsStr = [settings[@"osxextrafilesystems"] lowercaseString];
240+
filesystemsStr = [settings[CDV_PREF_EXTRA_FILESYSTEM] lowercaseString];
241241
}
242242
if (!filesystemsStr) {
243-
filesystemsStr = @"library,library-nosync,documents,documents-nosync,cache,bundle,root";
243+
filesystemsStr = CDV_FILESYSTEMS_DEFAULT;
244244
}
245245
return [filesystemsStr componentsSeparatedByString:@","];
246246
}
@@ -260,13 +260,6 @@ - (void)registerExtraFileSystems:(NSArray *)filesystems fromAvailableSet:(NSDict
260260
{
261261
NSMutableSet *installedFilesystems = [[NSMutableSet alloc] initWithCapacity:7];
262262

263-
/* Build non-syncable directories as necessary */
264-
for (NSString *nonSyncFS in @[@"library-nosync", @"documents-nosync"]) {
265-
if ([filesystems containsObject:nonSyncFS]) {
266-
[self makeNonSyncable:availableFileSystems[nonSyncFS]];
267-
}
268-
}
269-
270263
/* Register filesystems in order */
271264
for (NSString *fsName in filesystems) {
272265
if (![installedFilesystems containsObject:fsName]) {
@@ -283,88 +276,66 @@ - (void)registerExtraFileSystems:(NSArray *)filesystems fromAvailableSet:(NSDict
283276

284277
- (NSDictionary *)getAvailableFileSystems
285278
{
286-
NSString *libPath = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) objectAtIndex:0];
287-
NSString *docPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
288279
return @{
289-
@"library": libPath,
290-
@"library-nosync": [libPath stringByAppendingPathComponent:@"NoCloud"],
291-
@"documents": docPath,
292-
@"documents-nosync": [docPath stringByAppendingPathComponent:@"NoCloud"],
293-
@"cache": [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) objectAtIndex:0],
280+
@"documents": self.appDocsPath,
281+
@"cache": self.appCachePath,
294282
@"bundle": [[NSBundle mainBundle] bundlePath],
295283
@"root": @"/"
296284
};
297285
}
298286

299287
- (void)pluginInitialize
300-
{
301-
filePlugin = self;
288+
{ filePlugin = self;
302289
[NSURLProtocol registerClass:[CDVFilesystemURLProtocol class]];
303290

304291
fileSystems_ = [[NSMutableArray alloc] initWithCapacity:3];
305292

306-
// Get the Library directory path
307-
NSArray* paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);
308-
self.appLibraryPath = [[paths objectAtIndex:0] stringByAppendingPathComponent:@"files"];
309-
310293
// Get the Temporary directory path
311294
self.appTempPath = [NSTemporaryDirectory()stringByStandardizingPath]; // remove trailing slash from NSTemporaryDirectory()
295+
[self registerFilesystem:[[CDVLocalFilesystem alloc] initWithName:@"temporary" root:self.appTempPath]];
312296

313-
// Get the Documents directory path
314-
paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
315-
self.rootDocsPath = [paths objectAtIndex:0];
316-
self.appDocsPath = [self.rootDocsPath stringByAppendingPathComponent:@"files"];
297+
// ~/Library/Application Support/<bundle-id>
298+
self.appSupportPath = [self getSupportDirectoryFor:NSApplicationSupportDirectory pathComponents:nil];
317299

300+
// ~/Library/Application Support/<bundle-id>/files
301+
self.appDataPath = [self getSupportDirectoryFor:NSApplicationSupportDirectory pathComponents:@[@"files"]];
302+
[self registerFilesystem:[[CDVLocalFilesystem alloc] initWithName:@"persistent" root:self.appDataPath]];
318303

319-
NSString *location = nil;
320-
if([self.viewController isKindOfClass:[CDVViewController class]]) {
321-
CDVViewController *vc = (CDVViewController *)self.viewController;
322-
NSMutableDictionary *settings = vc.settings;
323-
location = [[settings objectForKey:@"iospersistentfilelocation"] lowercaseString];
324-
}
325-
if (location == nil) {
326-
// Compatibilty by default (if the config preference is not set, or
327-
// if we're not embedded in a CDVViewController somehow.)
328-
location = @"compatibility";
329-
}
304+
// ~/Documents/
305+
self.appDocsPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
330306

331-
NSError *error;
332-
if ([[NSFileManager defaultManager] createDirectoryAtPath:self.appTempPath
333-
withIntermediateDirectories:YES
334-
attributes:nil
335-
error:&error]) {
336-
[self registerFilesystem:[[CDVLocalFilesystem alloc] initWithName:@"temporary" root:self.appTempPath]];
337-
} else {
338-
NSLog(@"Unable to create temporary directory: %@", error);
339-
}
340-
if ([location isEqualToString:@"library"]) {
341-
if ([[NSFileManager defaultManager] createDirectoryAtPath:self.appLibraryPath
342-
withIntermediateDirectories:YES
343-
attributes:nil
344-
error:&error]) {
345-
[self registerFilesystem:[[CDVLocalFilesystem alloc] initWithName:@"persistent" root:self.appLibraryPath]];
346-
} else {
347-
NSLog(@"Unable to create library directory: %@", error);
348-
}
349-
} else if ([location isEqualToString:@"compatibility"]) {
350-
/*
351-
* Fall-back to compatibility mode -- this is the logic implemented in
352-
* earlier versions of this plugin, and should be maintained here so
353-
* that apps which were originally deployed with older versions of the
354-
* plugin can continue to provide access to files stored under those
355-
* versions.
356-
*/
357-
[self registerFilesystem:[[CDVLocalFilesystem alloc] initWithName:@"persistent" root:self.rootDocsPath]];
358-
} else {
359-
NSAssert(false,
360-
@"File plugin configuration error: Please set iosPersistentFileLocation in config.xml to one of \"library\" (for new applications) or \"compatibility\" (for compatibility with previous versions)");
361-
}
307+
// ~/Library/Caches/<bundle-id>/files
308+
self.appCachePath = [self getSupportDirectoryFor:NSCachesDirectory pathComponents:nil];
362309

363310
[self registerExtraFileSystems:[self getExtraFileSystemsPreference:self.viewController]
364311
fromAvailableSet:[self getAvailableFileSystems]];
312+
}
313+
314+
- (NSString*) getSupportDirectoryFor: (NSSearchPathDirectory) directory pathComponents: (NSArray*) components {
315+
NSError* error;
316+
NSFileManager* fm = [NSFileManager defaultManager];
317+
NSURL* supportDir = [fm URLForDirectory:directory inDomain:NSUserDomainMask appropriateForURL:nil create:YES error:&error];
318+
if (supportDir == nil) {
319+
NSLog(@"unable to get support directory: %@", error);
320+
return nil;
321+
}
365322

323+
NSString* bundleID = [[NSBundle mainBundle] bundleIdentifier];
324+
NSURL *dirPath = [supportDir URLByAppendingPathComponent:bundleID];
325+
for (NSString* pathComponent in components) {
326+
dirPath = [dirPath URLByAppendingPathComponent:pathComponent];
327+
}
328+
329+
if (![fm fileExistsAtPath:dirPath.path]) {
330+
if (![fm createDirectoryAtURL:dirPath withIntermediateDirectories:YES attributes:nil error:&error]) {
331+
NSLog(@"unable to create support directory: %@", error);
332+
return nil;
333+
}
334+
}
335+
return dirPath.path;
366336
}
367337

338+
368339
- (CDVFilesystemURL *)fileSystemURLforArg:(NSString *)urlArg
369340
{
370341
CDVFilesystemURL* ret = nil;
@@ -446,7 +417,7 @@ - (void)requestFileSystem:(CDVInvokedUrlCommand*)command
446417
} else {
447418
NSString* fullPath = @"/";
448419
// check for avail space for size request
449-
NSNumber* pNumAvail = [self checkFreeDiskSpace:self.rootDocsPath];
420+
NSNumber* pNumAvail = [self checkFreeDiskSpace:self.appSupportPath];
450421
// NSLog(@"Free space: %@", [NSString stringWithFormat:@"%qu", [ pNumAvail unsignedLongLongValue ]]);
451422
if (pNumAvail && ([pNumAvail unsignedLongLongValue] < size)) {
452423
result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:QUOTA_EXCEEDED_ERR];
@@ -480,28 +451,14 @@ - (void)requestAllFileSystems:(CDVInvokedUrlCommand*)command
480451

481452
- (void)requestAllPaths:(CDVInvokedUrlCommand*)command
482453
{
483-
NSString* libPath = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES)[0];
484-
NSString* libPathSync = [libPath stringByAppendingPathComponent:@"Cloud"];
485-
NSString* libPathNoSync = [libPath stringByAppendingPathComponent:@"NoCloud"];
486-
NSString* docPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
487-
NSString* storagePath = [libPath stringByDeletingLastPathComponent];
488-
NSString* cachePath = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)[0];
489-
490-
// Create the directories if necessary.
491-
[[NSFileManager defaultManager] createDirectoryAtPath:libPathSync withIntermediateDirectories:YES attributes:nil error:nil];
492-
[[NSFileManager defaultManager] createDirectoryAtPath:libPathNoSync withIntermediateDirectories:YES attributes:nil error:nil];
493-
// Mark NoSync as non-iCloud.
494-
[[NSURL fileURLWithPath:libPathNoSync] setResourceValue: [NSNumber numberWithBool: YES]
495-
forKey: NSURLIsExcludedFromBackupKey error:nil];
496-
497454
NSDictionary* ret = @{
498455
@"applicationDirectory": [[NSURL fileURLWithPath:[[NSBundle mainBundle] bundlePath]] absoluteString],
499-
@"applicationStorageDirectory": [[NSURL fileURLWithPath:storagePath] absoluteString],
500-
@"dataDirectory": [[NSURL fileURLWithPath:libPathNoSync] absoluteString],
501-
@"syncedDataDirectory": [[NSURL fileURLWithPath:libPathSync] absoluteString],
502-
@"documentsDirectory": [[NSURL fileURLWithPath:docPath] absoluteString],
503-
@"cacheDirectory": [[NSURL fileURLWithPath:cachePath] absoluteString],
504-
@"tempDirectory": [[NSURL fileURLWithPath:NSTemporaryDirectory()] absoluteString]
456+
@"applicationStorageDirectory": [[NSURL fileURLWithPath:self.appSupportPath] absoluteString],
457+
@"dataDirectory": [[NSURL fileURLWithPath:self.appDataPath] absoluteString],
458+
@"documentsDirectory": [[NSURL fileURLWithPath:self.appDocsPath] absoluteString],
459+
@"cacheDirectory": [[NSURL fileURLWithPath:self.appCachePath] absoluteString],
460+
@"tempDirectory":[[NSURL fileURLWithPath:self.appTempPath] absoluteString],
461+
@"rootDirectory": @"file:///",
505462
};
506463

507464
CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:ret];

tests/tests.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3302,6 +3302,8 @@ exports.defineAutoTests = function () {
33023302
expectedPaths.push('externalRootDirectory', 'sharedDirectory');
33033303
} else if (cordova.platformId == 'ios') {
33043304
expectedPaths.push('syncedDataDirectory', 'documentsDirectory', 'tempDirectory');
3305+
} else if (cordova.platformId == 'osx') {
3306+
expectedPaths.push('documentsDirectory', 'tempDirectory', 'rootDirectory');
33053307
} else {
33063308
console.log('Skipping test due on unsupported platform.');
33073309
return;

0 commit comments

Comments
 (0)