├── .flowconfig ├── .gitignore ├── .jshintrc ├── Downloader.h ├── Downloader.m ├── FS.common.js ├── IntegrationTests ├── AppDelegate.h ├── AppDelegate.m ├── Base.lproj │ └── LaunchScreen.xib ├── FSTest.js ├── Images.xcassets │ └── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── uie_icon@2x-1.png │ │ ├── uie_icon@2x-2.png │ │ ├── uie_icon@2x-3.png │ │ ├── uie_icon@2x-4.png │ │ ├── uie_icon@2x-5.png │ │ └── uie_icon@2x.png ├── Info.plist ├── IntegrationTestHarnessTest.js ├── IntegrationTests.xcodeproj │ ├── project.pbxproj │ └── xcshareddata │ │ └── xcschemes │ │ └── IntegrationTests.xcscheme ├── IntegrationTestsApp.js ├── IntegrationTestsTests │ ├── Info.plist │ ├── IntegrationTestsTests.m │ └── ReferenceImages │ │ └── IntegrationTests-IntegrationTestsApp │ │ └── testSimpleSnapshot_1@2x.png └── main.m ├── LICENSE ├── NSArray+Map.h ├── NSArray+Map.m ├── README.md ├── RNFS.podspec ├── RNFS.xcodeproj └── project.pbxproj ├── RNFSManager.h ├── RNFSManager.m ├── Uploader.h ├── Uploader.m ├── android ├── build.gradle └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ └── com │ └── rnfs │ ├── DownloadParams.java │ ├── DownloadResult.java │ ├── Downloader.java │ ├── IORejectionException.java │ ├── RNFSManager.java │ ├── RNFSPackage.java │ ├── UploadParams.java │ ├── UploadResult.java │ └── Uploader.java ├── index.d.ts ├── jsconfig.json ├── package.json ├── windows ├── .gitignore ├── .npmignore ├── RNFS.Tests │ ├── Assets │ │ ├── LockScreenLogo.scale-200.png │ │ ├── SplashScreen.scale-200.png │ │ ├── Square150x150Logo.scale-200.png │ │ ├── Square44x44Logo.scale-200.png │ │ ├── Square44x44Logo.targetsize-24_altform-unplated.png │ │ ├── StoreLogo.png │ │ └── Wide310x150Logo.scale-200.png │ ├── Package.appxmanifest │ ├── Properties │ │ ├── AssemblyInfo.cs │ │ └── UnitTestApp.rd.xml │ ├── RNFS.Tests.csproj │ ├── RNFS.Tests.nuget.props │ ├── RNFS.Tests.nuget.targets │ ├── RNFS.Tests_TemporaryKey.pfx │ ├── RNFSManagerTests.cs │ ├── UnitTestApp.xaml │ └── UnitTestApp.xaml.cs ├── RNFS.sln └── RNFS │ ├── Properties │ ├── AssemblyInfo.cs │ └── RNFS.rd.xml │ ├── RNFS.csproj │ ├── RNFSManager.cs │ └── RNFSPackage.cs └── yarn.lock /.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | .*/node_modules/react_native/.* 3 | 4 | [include] 5 | 6 | [libs] 7 | 8 | [options] 9 | 10 | [version] 11 | 0.28.0 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | workbench 3 | *.log 4 | # Xcode 5 | .DS_Store 6 | build/ 7 | *.pbxuser 8 | !default.pbxuser 9 | *.mode1v3 10 | !default.mode1v3 11 | *.mode2v3 12 | !default.mode2v3 13 | *.perspectivev3 14 | !default.perspectivev3 15 | *.xcworkspace 16 | !default.xcworkspace 17 | xcuserdata 18 | profile 19 | *.moved-aside 20 | DerivedData 21 | .idea/ 22 | # Pods - for those of you who use CocoaPods 23 | Pods 24 | update-test.sh 25 | .vscode/ 26 | android/.gradle/* 27 | android/gradle/* 28 | android/*.iml 29 | android/local.properties 30 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "-W093": true, 3 | "asi": false, 4 | "bitwise": true, 5 | "boss": false, 6 | "browser": false, 7 | "camelcase": true, 8 | "couch": false, 9 | "curly": true, 10 | "debug": false, 11 | "devel": true, 12 | "dojo": false, 13 | "eqeqeq": true, 14 | "eqnull": false, 15 | "esnext": true, 16 | "evil": false, 17 | "expr": true, 18 | "forin": false, 19 | "freeze": true, 20 | "funcscope": true, 21 | "gcl": false, 22 | "globalstrict": true, 23 | "immed": false, 24 | "indent": 2, 25 | "iterator": false, 26 | "jquery": false, 27 | "lastsemic": false, 28 | "latedef": false, 29 | "laxbreak": true, 30 | "laxcomma": false, 31 | "loopfunc": false, 32 | "maxcomplexity": false, 33 | "maxdepth": false, 34 | "maxerr": 50, 35 | "maxlen": 80, 36 | "maxparams": false, 37 | "maxstatements": false, 38 | "mootools": false, 39 | "moz": false, 40 | "multistr": false, 41 | "newcap": true, 42 | "noarg": true, 43 | "node": true, 44 | "noempty": true, 45 | "nonbsp": true, 46 | "nonew": true, 47 | "nonstandard": false, 48 | "notypeof": false, 49 | "noyield": false, 50 | "phantom": false, 51 | "plusplus": false, 52 | "predef": [ 53 | "jasmine", 54 | "describe", 55 | "beforeEach", 56 | "it", 57 | "jest", 58 | "pit", 59 | "expect", 60 | "rootRequire" 61 | ], 62 | "proto": false, 63 | "prototypejs": false, 64 | "quotmark": true, 65 | "rhino": false, 66 | "scripturl": false, 67 | "shadow": false, 68 | "smarttabs": false, 69 | "strict": true, 70 | "sub": false, 71 | "supernew": false, 72 | "trailing": true, 73 | "undef": true, 74 | "unused": true, 75 | "validthis": false, 76 | "worker": false, 77 | "wsh": false, 78 | "yui": false 79 | } 80 | -------------------------------------------------------------------------------- /Downloader.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | typedef void (^DownloadCompleteCallback)(NSNumber*, NSNumber*); 4 | typedef void (^ErrorCallback)(NSError*); 5 | typedef void (^BeginCallback)(NSNumber*, NSNumber*, NSDictionary*); 6 | typedef void (^ProgressCallback)(NSNumber*, NSNumber*); 7 | typedef void (^ResumableCallback)(); 8 | 9 | @interface RNFSDownloadParams : NSObject 10 | 11 | @property (copy) NSString* fromUrl; 12 | @property (copy) NSString* toFile; 13 | @property (copy) NSDictionary* headers; 14 | @property (copy) DownloadCompleteCallback completeCallback; // Download has finished (data written) 15 | @property (copy) ErrorCallback errorCallback; // Something went wrong 16 | @property (copy) BeginCallback beginCallback; // Download has started (headers received) 17 | @property (copy) ProgressCallback progressCallback; // Download is progressing 18 | @property (copy) ResumableCallback resumableCallback; // Download has stopped but is resumable 19 | @property bool background; // Whether to continue download when app is in background 20 | @property bool discretionary; // Whether the file may be downloaded at the OS's discretion (iOS only) 21 | @property bool cacheable; // Whether the file may be stored in the shared NSURLCache (iOS only) 22 | @property (copy) NSNumber* progressDivider; 23 | @property (copy) NSNumber* readTimeout; 24 | 25 | 26 | @end 27 | 28 | @interface RNFSDownloader : NSObject 29 | 30 | - (NSString *)downloadFile:(RNFSDownloadParams*)params; 31 | - (void)stopDownload; 32 | - (void)resumeDownload; 33 | - (BOOL)isResumable; 34 | 35 | @end 36 | -------------------------------------------------------------------------------- /Downloader.m: -------------------------------------------------------------------------------- 1 | #import "Downloader.h" 2 | 3 | @implementation RNFSDownloadParams 4 | 5 | @end 6 | 7 | @interface RNFSDownloader() 8 | 9 | @property (copy) RNFSDownloadParams* params; 10 | 11 | @property (retain) NSURLSession* session; 12 | @property (retain) NSURLSessionDownloadTask* task; 13 | @property (retain) NSNumber* statusCode; 14 | @property (retain) NSNumber* lastProgressValue; 15 | @property (retain) NSNumber* contentLength; 16 | @property (retain) NSNumber* bytesWritten; 17 | @property (retain) NSData* resumeData; 18 | 19 | @property (retain) NSFileHandle* fileHandle; 20 | 21 | @end 22 | 23 | @implementation RNFSDownloader 24 | 25 | - (NSString *)downloadFile:(RNFSDownloadParams*)params 26 | { 27 | NSString *uuid = nil; 28 | 29 | _params = params; 30 | 31 | _bytesWritten = 0; 32 | 33 | NSURL* url = [NSURL URLWithString:_params.fromUrl]; 34 | 35 | if ([[NSFileManager defaultManager] fileExistsAtPath:_params.toFile]) { 36 | _fileHandle = [NSFileHandle fileHandleForWritingAtPath:_params.toFile]; 37 | 38 | if (!_fileHandle) { 39 | NSError* error = [NSError errorWithDomain:@"Downloader" code:NSURLErrorFileDoesNotExist 40 | userInfo:@{NSLocalizedDescriptionKey: [NSString stringWithFormat: @"Failed to write target file at path: %@", _params.toFile]}]; 41 | 42 | _params.errorCallback(error); 43 | return nil; 44 | } else { 45 | [_fileHandle closeFile]; 46 | } 47 | } 48 | 49 | NSURLSessionConfiguration *config; 50 | if (_params.background) { 51 | uuid = [[NSUUID UUID] UUIDString]; 52 | config = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:uuid]; 53 | config.discretionary = _params.discretionary; 54 | } else { 55 | config = [NSURLSessionConfiguration defaultSessionConfiguration]; 56 | } 57 | 58 | if (!_params.cacheable) { 59 | config.URLCache = nil; 60 | } 61 | 62 | config.HTTPAdditionalHeaders = _params.headers; 63 | config.timeoutIntervalForRequest = [_params.readTimeout intValue] / 1000.0; 64 | 65 | _session = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:nil]; 66 | _task = [_session downloadTaskWithURL:url]; 67 | [_task resume]; 68 | 69 | return uuid; 70 | } 71 | 72 | - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite 73 | { 74 | NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)downloadTask.response; 75 | if (!_statusCode) { 76 | _statusCode = [NSNumber numberWithLong:httpResponse.statusCode]; 77 | _contentLength = [NSNumber numberWithLong:httpResponse.expectedContentLength]; 78 | return _params.beginCallback(_statusCode, _contentLength, httpResponse.allHeaderFields); 79 | } 80 | 81 | if ([_statusCode isEqualToNumber:[NSNumber numberWithInt:200]]) { 82 | _bytesWritten = @(totalBytesWritten); 83 | 84 | if (_params.progressDivider.integerValue <= 0) { 85 | return _params.progressCallback(_contentLength, _bytesWritten); 86 | } else { 87 | double doubleBytesWritten = (double)[_bytesWritten longValue]; 88 | double doubleContentLength = (double)[_contentLength longValue]; 89 | double doublePercents = doubleBytesWritten / doubleContentLength * 100; 90 | NSNumber* progress = [NSNumber numberWithUnsignedInt: floor(doublePercents)]; 91 | if ([progress unsignedIntValue] % [_params.progressDivider integerValue] == 0) { 92 | if (([progress unsignedIntValue] != [_lastProgressValue unsignedIntValue]) || ([_bytesWritten unsignedIntegerValue] == [_contentLength longValue])) { 93 | NSLog(@"---Progress callback EMIT--- %zu", [progress unsignedIntValue]); 94 | _lastProgressValue = [NSNumber numberWithUnsignedInt:[progress unsignedIntValue]]; 95 | return _params.progressCallback(_contentLength, _bytesWritten); 96 | } 97 | } 98 | } 99 | } 100 | } 101 | 102 | - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location 103 | { 104 | NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)downloadTask.response; 105 | if (!_statusCode) { 106 | _statusCode = [NSNumber numberWithLong:httpResponse.statusCode]; 107 | } 108 | NSURL *destURL = [NSURL fileURLWithPath:_params.toFile]; 109 | NSFileManager *fm = [NSFileManager defaultManager]; 110 | NSError *error = nil; 111 | if([_statusCode integerValue] >= 200 && [_statusCode integerValue] < 300) { 112 | [fm removeItemAtURL:destURL error:nil]; // Remove file at destination path, if it exists 113 | [fm moveItemAtURL:location toURL:destURL error:&error]; 114 | // There are no guarantees about how often URLSession:downloadTask:didWriteData: will fire, 115 | // so we read an authoritative number of bytes written here. 116 | _bytesWritten = @([fm attributesOfItemAtPath:_params.toFile error:nil].fileSize); 117 | } 118 | if (error) { 119 | NSLog(@"RNFS download: unable to move tempfile to destination. %@, %@", error, error.userInfo); 120 | } 121 | 122 | return _params.completeCallback(_statusCode, _bytesWritten); 123 | } 124 | 125 | - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error 126 | { 127 | if (error && error.code != NSURLErrorCancelled) { 128 | _resumeData = error.userInfo[NSURLSessionDownloadTaskResumeData]; 129 | if (_resumeData != nil) { 130 | _params.resumableCallback(); 131 | } else { 132 | _params.errorCallback(error); 133 | } 134 | } 135 | } 136 | 137 | - (void)stopDownload 138 | { 139 | if (_task.state == NSURLSessionTaskStateRunning) { 140 | [_task cancelByProducingResumeData:^(NSData * _Nullable resumeData) { 141 | if (resumeData != nil) { 142 | self.resumeData = resumeData; 143 | _params.resumableCallback(); 144 | } else { 145 | NSError *error = [NSError errorWithDomain:@"RNFS" 146 | code:@"Aborted" 147 | userInfo:@{ 148 | NSLocalizedDescriptionKey: @"Download has been aborted" 149 | }]; 150 | 151 | _params.errorCallback(error); 152 | } 153 | }]; 154 | 155 | } 156 | } 157 | 158 | - (void)resumeDownload 159 | { 160 | if (_resumeData != nil) { 161 | _task = [_session downloadTaskWithResumeData:_resumeData]; 162 | [_task resume]; 163 | _resumeData = nil; 164 | } 165 | } 166 | 167 | - (BOOL)isResumable 168 | { 169 | return _resumeData != nil; 170 | } 171 | 172 | @end 173 | -------------------------------------------------------------------------------- /FS.common.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Native FS 3 | * @flow 4 | */ 5 | 6 | 'use strict'; 7 | 8 | // This file supports both iOS and Android 9 | 10 | var RNFSManager = require('react-native').NativeModules.RNFSManager; 11 | 12 | var NativeAppEventEmitter = require('react-native').NativeAppEventEmitter; // iOS 13 | var DeviceEventEmitter = require('react-native').DeviceEventEmitter; // Android 14 | var base64 = require('base-64'); 15 | var utf8 = require('utf8'); 16 | var isIOS = require('react-native').Platform.OS === 'ios'; 17 | 18 | var RNFSFileTypeRegular = RNFSManager.RNFSFileTypeRegular; 19 | var RNFSFileTypeDirectory = RNFSManager.RNFSFileTypeDirectory; 20 | 21 | var jobId = 0; 22 | 23 | var getJobId = () => { 24 | jobId += 1; 25 | return jobId; 26 | }; 27 | 28 | var normalizeFilePath = (path: string) => (path.startsWith('file://') ? path.slice(7) : path); 29 | 30 | type MkdirOptions = { 31 | NSURLIsExcludedFromBackupKey?: boolean; // iOS only 32 | NSFileProtectionKey?: string; // IOS only 33 | }; 34 | 35 | type FileOptions = { 36 | NSFileProtectionKey?: string; // IOS only 37 | }; 38 | 39 | type ReadDirItem = { 40 | ctime: ?Date; // The creation date of the file (iOS only) 41 | mtime: ?Date; // The last modified date of the file 42 | name: string; // The name of the item 43 | path: string; // The absolute path to the item 44 | size: string; // Size in bytes 45 | isFile: () => boolean; // Is the file just a file? 46 | isDirectory: () => boolean; // Is the file a directory? 47 | }; 48 | 49 | type StatResult = { 50 | name: ?string; // The name of the item TODO: why is this not documented? 51 | path: string; // The absolute path to the item 52 | size: string; // Size in bytes 53 | mode: number; // UNIX file mode 54 | ctime: number; // Created date 55 | mtime: number; // Last modified date 56 | originalFilepath: string; // In case of content uri this is the pointed file path, otherwise is the same as path 57 | isFile: () => boolean; // Is the file just a file? 58 | isDirectory: () => boolean; // Is the file a directory? 59 | }; 60 | 61 | type Headers = { [name: string]: string }; 62 | type Fields = { [name: string]: string }; 63 | 64 | type DownloadFileOptions = { 65 | fromUrl: string; // URL to download file from 66 | toFile: string; // Local filesystem path to save the file to 67 | headers?: Headers; // An object of headers to be passed to the server 68 | background?: boolean; // Continue the download in the background after the app terminates (iOS only) 69 | discretionary?: boolean; // Allow the OS to control the timing and speed of the download to improve perceived performance (iOS only) 70 | cacheable?: boolean; // Whether the download can be stored in the shared NSURLCache (iOS only) 71 | progressDivider?: number; 72 | begin?: (res: DownloadBeginCallbackResult) => void; 73 | progress?: (res: DownloadProgressCallbackResult) => void; 74 | resumable?: () => void; // only supported on iOS yet 75 | connectionTimeout?: number; // only supported on Android yet 76 | readTimeout?: number; // supported on Android and iOS 77 | }; 78 | 79 | type DownloadBeginCallbackResult = { 80 | jobId: number; // The download job ID, required if one wishes to cancel the download. See `stopDownload`. 81 | statusCode: number; // The HTTP status code 82 | contentLength: number; // The total size in bytes of the download resource 83 | headers: Headers; // The HTTP response headers from the server 84 | }; 85 | 86 | type DownloadProgressCallbackResult = { 87 | jobId: number; // The download job ID, required if one wishes to cancel the download. See `stopDownload`. 88 | contentLength: number; // The total size in bytes of the download resource 89 | bytesWritten: number; // The number of bytes written to the file so far 90 | }; 91 | 92 | type DownloadResult = { 93 | jobId: number; // The download job ID, required if one wishes to cancel the download. See `stopDownload`. 94 | statusCode: number; // The HTTP status code 95 | bytesWritten: number; // The number of bytes written to the file 96 | }; 97 | 98 | type UploadFileOptions = { 99 | toUrl: string; // URL to upload file to 100 | files: UploadFileItem[]; // An array of objects with the file information to be uploaded. 101 | headers?: Headers; // An object of headers to be passed to the server 102 | fields?: Fields; // An object of fields to be passed to the server 103 | method?: string; // Default is 'POST', supports 'POST' and 'PUT' 104 | beginCallback?: (res: UploadBeginCallbackResult) => void; // deprecated 105 | progressCallback?: (res: UploadProgressCallbackResult) => void; // deprecated 106 | begin?: (res: UploadBeginCallbackResult) => void; 107 | progress?: (res: UploadProgressCallbackResult) => void; 108 | }; 109 | 110 | type UploadFileItem = { 111 | name: string; // Name of the file, if not defined then filename is used 112 | filename: string; // Name of file 113 | filepath: string; // Path to file 114 | filetype: string; // The mimetype of the file to be uploaded, if not defined it will get mimetype from `filepath` extension 115 | }; 116 | 117 | type UploadBeginCallbackResult = { 118 | jobId: number; // The upload job ID, required if one wishes to cancel the upload. See `stopUpload`. 119 | }; 120 | 121 | type UploadProgressCallbackResult = { 122 | jobId: number; // The upload job ID, required if one wishes to cancel the upload. See `stopUpload`. 123 | totalBytesExpectedToSend: number; // The total number of bytes that will be sent to the server 124 | totalBytesSent: number; // The number of bytes sent to the server 125 | }; 126 | 127 | type UploadResult = { 128 | jobId: number; // The upload job ID, required if one wishes to cancel the upload. See `stopUpload`. 129 | statusCode: number; // The HTTP status code 130 | headers: Headers; // The HTTP response headers from the server 131 | body: string; // The HTTP response body 132 | }; 133 | 134 | type FSInfoResult = { 135 | totalSpace: number; // The total amount of storage space on the device (in bytes). 136 | freeSpace: number; // The amount of available storage space on the device (in bytes). 137 | }; 138 | 139 | /** 140 | * Generic function used by readFile and readFileAssets 141 | */ 142 | function readFileGeneric(filepath: string, encodingOrOptions: ?string, command: Function) { 143 | var options = { 144 | encoding: 'utf8' 145 | }; 146 | 147 | if (encodingOrOptions) { 148 | if (typeof encodingOrOptions === 'string') { 149 | options.encoding = encodingOrOptions; 150 | } else if (typeof encodingOrOptions === 'object') { 151 | options = encodingOrOptions; 152 | } 153 | } 154 | 155 | return command(normalizeFilePath(filepath)).then((b64) => { 156 | var contents; 157 | 158 | if (options.encoding === 'utf8') { 159 | contents = utf8.decode(base64.decode(b64)); 160 | } else if (options.encoding === 'ascii') { 161 | contents = base64.decode(b64); 162 | } else if (options.encoding === 'base64') { 163 | contents = b64; 164 | } else { 165 | throw new Error('Invalid encoding type "' + String(options.encoding) + '"'); 166 | } 167 | 168 | return contents; 169 | }); 170 | } 171 | 172 | /** 173 | * Generic function used by readDir and readDirAssets 174 | */ 175 | function readDirGeneric(dirpath: string, command: Function) { 176 | return command(normalizeFilePath(dirpath)).then(files => { 177 | return files.map(file => ({ 178 | ctime: file.ctime && new Date(file.ctime * 1000) || null, 179 | mtime: file.mtime && new Date(file.mtime * 1000) || null, 180 | name: file.name, 181 | path: file.path, 182 | size: file.size, 183 | isFile: () => file.type === RNFSFileTypeRegular, 184 | isDirectory: () => file.type === RNFSFileTypeDirectory, 185 | })); 186 | }); 187 | } 188 | 189 | var RNFS = { 190 | 191 | mkdir(filepath: string, options: MkdirOptions = {}): Promise { 192 | return RNFSManager.mkdir(normalizeFilePath(filepath), options).then(() => void 0); 193 | }, 194 | 195 | moveFile(filepath: string, destPath: string, options: FileOptions = {}): Promise { 196 | return RNFSManager.moveFile(normalizeFilePath(filepath), normalizeFilePath(destPath), options).then(() => void 0); 197 | }, 198 | 199 | copyFile(filepath: string, destPath: string, options: FileOptions = {}): Promise { 200 | return RNFSManager.copyFile(normalizeFilePath(filepath), normalizeFilePath(destPath), options).then(() => void 0); 201 | }, 202 | 203 | pathForBundle(bundleNamed: string): Promise { 204 | return RNFSManager.pathForBundle(bundleNamed); 205 | }, 206 | 207 | pathForGroup(groupName: string): Promise { 208 | return RNFSManager.pathForGroup(groupName); 209 | }, 210 | 211 | getFSInfo(): Promise { 212 | return RNFSManager.getFSInfo(); 213 | }, 214 | 215 | getAllExternalFilesDirs(): Promise { 216 | return RNFSManager.getAllExternalFilesDirs(); 217 | }, 218 | 219 | unlink(filepath: string): Promise { 220 | return RNFSManager.unlink(normalizeFilePath(filepath)).then(() => void 0); 221 | }, 222 | 223 | exists(filepath: string): Promise { 224 | return RNFSManager.exists(normalizeFilePath(filepath)); 225 | }, 226 | 227 | stopDownload(jobId: number): void { 228 | RNFSManager.stopDownload(jobId); 229 | }, 230 | 231 | resumeDownload(jobId: number): void { 232 | RNFSManager.resumeDownload(jobId); 233 | }, 234 | 235 | isResumable(jobId: number): Promise { 236 | return RNFSManager.isResumable(jobId); 237 | }, 238 | 239 | stopUpload(jobId: number): void { 240 | RNFSManager.stopUpload(jobId); 241 | }, 242 | 243 | completeHandlerIOS(jobId: number): void { 244 | return RNFSManager.completeHandlerIOS(jobId); 245 | }, 246 | 247 | readDir(dirpath: string): Promise { 248 | return readDirGeneric(dirpath, RNFSManager.readDir); 249 | }, 250 | 251 | // Android-only 252 | readDirAssets(dirpath: string): Promise { 253 | if (!RNFSManager.readDirAssets) { 254 | throw new Error('readDirAssets is not available on this platform'); 255 | } 256 | return readDirGeneric(dirpath, RNFSManager.readDirAssets); 257 | }, 258 | 259 | // Android-only 260 | existsAssets(filepath: string) { 261 | if (!RNFSManager.existsAssets) { 262 | throw new Error('existsAssets is not available on this platform'); 263 | } 264 | return RNFSManager.existsAssets(filepath); 265 | }, 266 | 267 | // Android-only 268 | existsRes(filename: string) { 269 | if (!RNFSManager.existsRes) { 270 | throw new Error('existsRes is not available on this platform'); 271 | } 272 | return RNFSManager.existsRes(filename); 273 | }, 274 | 275 | // Node style version (lowercase d). Returns just the names 276 | readdir(dirpath: string): Promise { 277 | return RNFS.readDir(normalizeFilePath(dirpath)).then(files => { 278 | return files.map(file => file.name); 279 | }); 280 | }, 281 | 282 | // setReadable for Android 283 | setReadable(filepath: string, readable: boolean, ownerOnly: boolean): Promise { 284 | return RNFSManager.setReadable(filepath, readable, ownerOnly).then((result) => { 285 | return result; 286 | }) 287 | }, 288 | 289 | stat(filepath: string): Promise { 290 | return RNFSManager.stat(normalizeFilePath(filepath)).then((result) => { 291 | return { 292 | 'path': filepath, 293 | 'ctime': new Date(result.ctime * 1000), 294 | 'mtime': new Date(result.mtime * 1000), 295 | 'size': result.size, 296 | 'mode': result.mode, 297 | 'originalFilepath': result.originalFilepath, 298 | isFile: () => result.type === RNFSFileTypeRegular, 299 | isDirectory: () => result.type === RNFSFileTypeDirectory, 300 | }; 301 | }); 302 | }, 303 | 304 | readFile(filepath: string, encodingOrOptions?: any): Promise { 305 | return readFileGeneric(filepath, encodingOrOptions, RNFSManager.readFile); 306 | }, 307 | 308 | read(filepath: string, length: number = 0, position: number = 0, encodingOrOptions?: any): Promise { 309 | var options = { 310 | encoding: 'utf8' 311 | }; 312 | 313 | if (encodingOrOptions) { 314 | if (typeof encodingOrOptions === 'string') { 315 | options.encoding = encodingOrOptions; 316 | } else if (typeof encodingOrOptions === 'object') { 317 | options = encodingOrOptions; 318 | } 319 | } 320 | 321 | return RNFSManager.read(normalizeFilePath(filepath), length, position).then((b64) => { 322 | var contents; 323 | 324 | if (options.encoding === 'utf8') { 325 | contents = utf8.decode(base64.decode(b64)); 326 | } else if (options.encoding === 'ascii') { 327 | contents = base64.decode(b64); 328 | } else if (options.encoding === 'base64') { 329 | contents = b64; 330 | } else { 331 | throw new Error('Invalid encoding type "' + String(options.encoding) + '"'); 332 | } 333 | 334 | return contents; 335 | }); 336 | }, 337 | 338 | // Android only 339 | readFileAssets(filepath: string, encodingOrOptions?: any): Promise { 340 | if (!RNFSManager.readFileAssets) { 341 | throw new Error('readFileAssets is not available on this platform'); 342 | } 343 | return readFileGeneric(filepath, encodingOrOptions, RNFSManager.readFileAssets); 344 | }, 345 | 346 | // Android only 347 | readFileRes(filename: string, encodingOrOptions?: any): Promise { 348 | if (!RNFSManager.readFileRes) { 349 | throw new Error('readFileRes is not available on this platform'); 350 | } 351 | return readFileGeneric(filename, encodingOrOptions, RNFSManager.readFileRes); 352 | }, 353 | 354 | hash(filepath: string, algorithm: string): Promise { 355 | return RNFSManager.hash(normalizeFilePath(filepath), algorithm); 356 | }, 357 | 358 | // Android only 359 | copyFileAssets(filepath: string, destPath: string) { 360 | if (!RNFSManager.copyFileAssets) { 361 | throw new Error('copyFileAssets is not available on this platform'); 362 | } 363 | return RNFSManager.copyFileAssets(normalizeFilePath(filepath), normalizeFilePath(destPath)).then(() => void 0); 364 | }, 365 | 366 | // Android only 367 | copyFileRes(filename: string, destPath: string) { 368 | if (!RNFSManager.copyFileRes) { 369 | throw new Error('copyFileRes is not available on this platform'); 370 | } 371 | return RNFSManager.copyFileRes(filename, normalizeFilePath(destPath)).then(() => void 0); 372 | }, 373 | 374 | // iOS only 375 | // Copies fotos from asset-library (camera-roll) to a specific location 376 | // with a given width or height 377 | // @see: https://developer.apple.com/reference/photos/phimagemanager/1616964-requestimageforasset 378 | copyAssetsFileIOS(imageUri: string, destPath: string, width: number, height: number, 379 | scale: number = 1.0, compression: number = 1.0, resizeMode: string = 'contain'): Promise { 380 | return RNFSManager.copyAssetsFileIOS(imageUri, destPath, width, height, scale, compression, resizeMode); 381 | }, 382 | 383 | // iOS only 384 | // Copies fotos from asset-library (camera-roll) to a specific location 385 | // with a given width or height 386 | // @see: https://developer.apple.com/reference/photos/phimagemanager/1616964-requestimageforasset 387 | copyAssetsVideoIOS(imageUri: string, destPath: string): Promise { 388 | return RNFSManager.copyAssetsVideoIOS(imageUri, destPath); 389 | }, 390 | 391 | writeFile(filepath: string, contents: string, encodingOrOptions?: any): Promise { 392 | var b64; 393 | 394 | var options = { 395 | encoding: 'utf8' 396 | }; 397 | 398 | if (encodingOrOptions) { 399 | if (typeof encodingOrOptions === 'string') { 400 | options.encoding = encodingOrOptions; 401 | } else if (typeof encodingOrOptions === 'object') { 402 | options = { 403 | ...options, 404 | ...encodingOrOptions 405 | }; 406 | } 407 | } 408 | 409 | if (options.encoding === 'utf8') { 410 | b64 = base64.encode(utf8.encode(contents)); 411 | } else if (options.encoding === 'ascii') { 412 | b64 = base64.encode(contents); 413 | } else if (options.encoding === 'base64') { 414 | b64 = contents; 415 | } else { 416 | throw new Error('Invalid encoding type "' + options.encoding + '"'); 417 | } 418 | 419 | return RNFSManager.writeFile(normalizeFilePath(filepath), b64, options).then(() => void 0); 420 | }, 421 | 422 | appendFile(filepath: string, contents: string, encodingOrOptions?: any): Promise { 423 | var b64; 424 | 425 | var options = { 426 | encoding: 'utf8' 427 | }; 428 | 429 | if (encodingOrOptions) { 430 | if (typeof encodingOrOptions === 'string') { 431 | options.encoding = encodingOrOptions; 432 | } else if (typeof encodingOrOptions === 'object') { 433 | options = encodingOrOptions; 434 | } 435 | } 436 | 437 | if (options.encoding === 'utf8') { 438 | b64 = base64.encode(utf8.encode(contents)); 439 | } else if (options.encoding === 'ascii') { 440 | b64 = base64.encode(contents); 441 | } else if (options.encoding === 'base64') { 442 | b64 = contents; 443 | } else { 444 | throw new Error('Invalid encoding type "' + options.encoding + '"'); 445 | } 446 | 447 | return RNFSManager.appendFile(normalizeFilePath(filepath), b64); 448 | }, 449 | 450 | write(filepath: string, contents: string, position?: number, encodingOrOptions?: any): Promise { 451 | var b64; 452 | 453 | var options = { 454 | encoding: 'utf8' 455 | }; 456 | 457 | if (encodingOrOptions) { 458 | if (typeof encodingOrOptions === 'string') { 459 | options.encoding = encodingOrOptions; 460 | } else if (typeof encodingOrOptions === 'object') { 461 | options = encodingOrOptions; 462 | } 463 | } 464 | 465 | if (options.encoding === 'utf8') { 466 | b64 = base64.encode(utf8.encode(contents)); 467 | } else if (options.encoding === 'ascii') { 468 | b64 = base64.encode(contents); 469 | } else if (options.encoding === 'base64') { 470 | b64 = contents; 471 | } else { 472 | throw new Error('Invalid encoding type "' + options.encoding + '"'); 473 | } 474 | 475 | if (position === undefined) { 476 | position = -1; 477 | } 478 | 479 | return RNFSManager.write(normalizeFilePath(filepath), b64, position).then(() => void 0); 480 | }, 481 | 482 | downloadFile(options: DownloadFileOptions): { jobId: number, promise: Promise } { 483 | if (typeof options !== 'object') throw new Error('downloadFile: Invalid value for argument `options`'); 484 | if (typeof options.fromUrl !== 'string') throw new Error('downloadFile: Invalid value for property `fromUrl`'); 485 | if (typeof options.toFile !== 'string') throw new Error('downloadFile: Invalid value for property `toFile`'); 486 | if (options.headers && typeof options.headers !== 'object') throw new Error('downloadFile: Invalid value for property `headers`'); 487 | if (options.background && typeof options.background !== 'boolean') throw new Error('downloadFile: Invalid value for property `background`'); 488 | if (options.progressDivider && typeof options.progressDivider !== 'number') throw new Error('downloadFile: Invalid value for property `progressDivider`'); 489 | if (options.readTimeout && typeof options.readTimeout !== 'number') throw new Error('downloadFile: Invalid value for property `readTimeout`'); 490 | if (options.connectionTimeout && typeof options.connectionTimeout !== 'number') throw new Error('downloadFile: Invalid value for property `connectionTimeout`'); 491 | 492 | var jobId = getJobId(); 493 | var subscriptions = []; 494 | 495 | if (options.begin) { 496 | subscriptions.push(NativeAppEventEmitter.addListener('DownloadBegin-' + jobId, options.begin)); 497 | } 498 | 499 | if (options.progress) { 500 | subscriptions.push(NativeAppEventEmitter.addListener('DownloadProgress-' + jobId, options.progress)); 501 | } 502 | 503 | if (options.resumable) { 504 | subscriptions.push(NativeAppEventEmitter.addListener('DownloadResumable-' + jobId, options.resumable)); 505 | } 506 | 507 | var bridgeOptions = { 508 | jobId: jobId, 509 | fromUrl: options.fromUrl, 510 | toFile: normalizeFilePath(options.toFile), 511 | headers: options.headers || {}, 512 | background: !!options.background, 513 | progressDivider: options.progressDivider || 0, 514 | readTimeout: options.readTimeout || 15000, 515 | connectionTimeout: options.connectionTimeout || 5000 516 | }; 517 | 518 | return { 519 | jobId, 520 | promise: RNFSManager.downloadFile(bridgeOptions).then(res => { 521 | subscriptions.forEach(sub => sub.remove()); 522 | return res; 523 | }) 524 | .catch(e => { 525 | return Promise.reject(e); 526 | }) 527 | }; 528 | }, 529 | 530 | uploadFiles(options: UploadFileOptions): { jobId: number, promise: Promise } { 531 | if (!RNFSManager.uploadFiles) { 532 | return { 533 | jobId: -1, 534 | promise: Promise.reject(new Error('`uploadFiles` is unsupported on this platform')) 535 | }; 536 | } 537 | 538 | var jobId = getJobId(); 539 | var subscriptions = []; 540 | 541 | if (typeof options !== 'object') throw new Error('uploadFiles: Invalid value for argument `options`'); 542 | if (typeof options.toUrl !== 'string') throw new Error('uploadFiles: Invalid value for property `toUrl`'); 543 | if (!Array.isArray(options.files)) throw new Error('uploadFiles: Invalid value for property `files`'); 544 | if (options.headers && typeof options.headers !== 'object') throw new Error('uploadFiles: Invalid value for property `headers`'); 545 | if (options.fields && typeof options.fields !== 'object') throw new Error('uploadFiles: Invalid value for property `fields`'); 546 | if (options.method && typeof options.method !== 'string') throw new Error('uploadFiles: Invalid value for property `method`'); 547 | 548 | if (options.begin) { 549 | subscriptions.push(NativeAppEventEmitter.addListener('UploadBegin-' + jobId, options.begin)); 550 | } 551 | if (options.beginCallback && options.beginCallback instanceof Function) { 552 | // Deprecated 553 | subscriptions.push(NativeAppEventEmitter.addListener('UploadBegin-' + jobId, options.beginCallback)); 554 | } 555 | 556 | if (options.progress) { 557 | subscriptions.push(NativeAppEventEmitter.addListener('UploadProgress-' + jobId, options.progress)); 558 | } 559 | if (options.progressCallback && options.progressCallback instanceof Function) { 560 | // Deprecated 561 | subscriptions.push(NativeAppEventEmitter.addListener('UploadProgress-' + jobId, options.progressCallback)); 562 | } 563 | 564 | var bridgeOptions = { 565 | jobId: jobId, 566 | toUrl: options.toUrl, 567 | files: options.files, 568 | headers: options.headers || {}, 569 | fields: options.fields || {}, 570 | method: options.method || 'POST' 571 | }; 572 | 573 | return { 574 | jobId, 575 | promise: RNFSManager.uploadFiles(bridgeOptions).then(res => { 576 | subscriptions.forEach(sub => sub.remove()); 577 | return res; 578 | }) 579 | }; 580 | }, 581 | 582 | touch(filepath: string, mtime?: Date, ctime?: Date): Promise { 583 | if (ctime && !(ctime instanceof Date)) throw new Error('touch: Invalid value for argument `ctime`'); 584 | if (mtime && !(mtime instanceof Date)) throw new Error('touch: Invalid value for argument `mtime`'); 585 | var ctimeTime = 0; 586 | if (isIOS) { 587 | ctimeTime = ctime && ctime.getTime(); 588 | } 589 | return RNFSManager.touch( 590 | normalizeFilePath(filepath), 591 | mtime && mtime.getTime(), 592 | ctimeTime 593 | ); 594 | }, 595 | 596 | scanFile(path: string): Promise { 597 | return RNFSManager.scanFile(path); 598 | }, 599 | 600 | MainBundlePath: RNFSManager.RNFSMainBundlePath, 601 | CachesDirectoryPath: RNFSManager.RNFSCachesDirectoryPath, 602 | ExternalCachesDirectoryPath: RNFSManager.RNFSExternalCachesDirectoryPath, 603 | DocumentDirectoryPath: RNFSManager.RNFSDocumentDirectoryPath, 604 | ExternalDirectoryPath: RNFSManager.RNFSExternalDirectoryPath, 605 | ExternalStorageDirectoryPath: RNFSManager.RNFSExternalStorageDirectoryPath, 606 | TemporaryDirectoryPath: RNFSManager.RNFSTemporaryDirectoryPath, 607 | LibraryDirectoryPath: RNFSManager.RNFSLibraryDirectoryPath, 608 | PicturesDirectoryPath: RNFSManager.RNFSPicturesDirectoryPath, 609 | FileProtectionKeys: RNFSManager.RNFSFileProtectionKeys 610 | }; 611 | 612 | module.exports = RNFS; 613 | -------------------------------------------------------------------------------- /IntegrationTests/AppDelegate.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | */ 9 | 10 | #import 11 | 12 | @interface AppDelegate : UIResponder 13 | 14 | @property (nonatomic, strong) UIWindow *window; 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /IntegrationTests/AppDelegate.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | */ 9 | 10 | #import "AppDelegate.h" 11 | 12 | #if __has_include("RCTRootView.h") 13 | #import "RCTRootView.h" 14 | #else 15 | #import 16 | #endif 17 | 18 | @implementation AppDelegate 19 | 20 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 21 | { 22 | NSURL *jsCodeLocation; 23 | 24 | // Loading JavaScript code - uncomment the one you want. 25 | 26 | // OPTION 1 27 | // Load from development server. Start the server from the repository root: 28 | // 29 | // $ npm start 30 | // 31 | // To run on device, change `localhost` to the IP address of your computer, and make sure your computer and 32 | // iOS device are on the same Wi-Fi network. 33 | jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/IntegrationTests/IntegrationTestsApp.includeRequire.runModule.bundle?dev=true"]; 34 | 35 | // OPTION 2 36 | // Load from pre-bundled file on disk. To re-generate the static bundle, run 37 | // 38 | // $ curl http://localhost:8081/IntegrationTests/IntegrationTestsApp.includeRequire.runModule.bundle -o main.jsbundle 39 | // 40 | // and uncomment the next following line 41 | // jsCodeLocation = [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; 42 | 43 | RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation 44 | moduleName:@"IntegrationTestsApp" 45 | launchOptions:launchOptions]; 46 | 47 | self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; 48 | UIViewController *rootViewController = [[UIViewController alloc] init]; 49 | rootViewController.view = rootView; 50 | self.window.rootViewController = rootViewController; 51 | [self.window makeKeyAndVisible]; 52 | return YES; 53 | } 54 | 55 | @end 56 | -------------------------------------------------------------------------------- /IntegrationTests/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 21 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /IntegrationTests/FSTest.js: -------------------------------------------------------------------------------- 1 | 2 | 'use strict'; 3 | 4 | var RCTTestModule = require('NativeModules').TestModule; 5 | var React = require('react-native'); 6 | var createClass = require('create-react-class'); 7 | 8 | var { 9 | Text, 10 | View, 11 | } = React; 12 | var RNFS = require('react-native-fs'); 13 | var DEBUG = false; 14 | 15 | 16 | // setup in componentDidMount 17 | var done; 18 | var updateMessage; 19 | 20 | function runTestCase(description, fn) { 21 | updateMessage(description); 22 | fn(); 23 | } 24 | 25 | function expectTrue(condition, message) { 26 | if (!condition) { 27 | throw new Error(message); 28 | } 29 | } 30 | 31 | function expectEqual(lhs, rhs, testname) { 32 | expectTrue( 33 | lhs === rhs, 34 | 'Error in test ' + testname + ': expected ' + rhs + ', got ' + lhs 35 | ); 36 | } 37 | 38 | function expectFSNoError(err) { 39 | expectTrue(err === null, 'Unexpected FS error: ' + JSON.stringify(err)); 40 | } 41 | 42 | function testWriteAndReadFile() { 43 | var path = RNFS.DocumentDirectoryPath + '/test.txt'; 44 | 45 | var text = 'Lorem ipsum dolor sit amet'; 46 | var readText; 47 | 48 | RNFS.writeFile(path, text) 49 | .then((success) => { 50 | updateMessage('FILE WRITTEN!'); 51 | return RNFS.readFile(path); 52 | }) 53 | .then((contents) => { 54 | updateMessage('FILE READ! Contents:'); 55 | readText = contents; 56 | expectEqual(text, readText, 'testWriteAndReadFile'); 57 | updateMessage('readFile correctly returned' + readText); 58 | }) 59 | .finally(() => { 60 | runTestCase('testCreateAndDeleteFile', testCreateAndDeleteFile); 61 | }) 62 | .done();//promise done needed to throw exception so that in case test fails,error is propagated 63 | } 64 | 65 | 66 | 67 | function testCreateAndDeleteFile() { 68 | var path = RNFS.DocumentDirectoryPath + '/test.txt'; 69 | var text = 'Lorem ipsum dolor sit amet'; 70 | var readText; 71 | 72 | RNFS.writeFile(path, text) 73 | .then((success) => { 74 | updateMessage('FILE CREATED!'); 75 | return RNFS.unlink(path); 76 | }) 77 | .spread((success, path) => { 78 | updateMessage('FILE DELETED!' + success + ',' + path); 79 | return RNFS.stat(path); 80 | }) 81 | .then((statResult) => { 82 | updateMessage('*****' + statResult); 83 | if (statResult.isFile()) { 84 | updateMessage('FILE STILL EXISTS'); 85 | } 86 | }) 87 | .catch((err) => { 88 | updateMessage('catch' + err); 89 | expectTrue(true,'File is deleted'); 90 | }) 91 | .finally(() => { 92 | done(); //testrunners done 93 | }) 94 | .done(); //promise done needed to throw exception so that in case test fails,error is propagated 95 | } 96 | 97 | 98 | 99 | 100 | var FSTest = createClass({ 101 | getInitialState() { 102 | return { 103 | messages: 'Initializing...', 104 | done: false, 105 | }; 106 | }, 107 | 108 | componentDidMount() { 109 | done = () => this.setState({ 110 | done: true 111 | }, RCTTestModule.markTestCompleted); 112 | updateMessage = (msg) => { 113 | this.setState({ 114 | messages: this.state.messages.concat('\n' + msg) 115 | }); 116 | DEBUG && console.log(msg); 117 | }; 118 | testWriteAndReadFile(); 119 | }, 120 | 121 | render() { 122 | return ( 123 | 124 | 125 | {this.constructor.displayName + ': '} 126 | {this.state.done ? 'Done' : 'Testing...'} 127 | {'\n\n' + this.state.messages} 128 | 129 | 130 | ); 131 | } 132 | }); 133 | 134 | module.exports = FSTest; 135 | -------------------------------------------------------------------------------- /IntegrationTests/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "29x29", 5 | "idiom" : "iphone", 6 | "filename" : "uie_icon@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "29x29", 11 | "idiom" : "iphone", 12 | "filename" : "uie_icon@2x-1.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "40x40", 17 | "idiom" : "iphone", 18 | "filename" : "uie_icon@2x-2.png", 19 | "scale" : "2x" 20 | }, 21 | { 22 | "size" : "40x40", 23 | "idiom" : "iphone", 24 | "filename" : "uie_icon@2x-3.png", 25 | "scale" : "3x" 26 | }, 27 | { 28 | "size" : "60x60", 29 | "idiom" : "iphone", 30 | "filename" : "uie_icon@2x-5.png", 31 | "scale" : "2x" 32 | }, 33 | { 34 | "size" : "60x60", 35 | "idiom" : "iphone", 36 | "filename" : "uie_icon@2x-4.png", 37 | "scale" : "3x" 38 | } 39 | ], 40 | "info" : { 41 | "version" : 1, 42 | "author" : "xcode" 43 | } 44 | } -------------------------------------------------------------------------------- /IntegrationTests/Images.xcassets/AppIcon.appiconset/uie_icon@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naxel/react-native-fs/53ac0a75198c942564184f5675d5fcb9635c5751/IntegrationTests/Images.xcassets/AppIcon.appiconset/uie_icon@2x-1.png -------------------------------------------------------------------------------- /IntegrationTests/Images.xcassets/AppIcon.appiconset/uie_icon@2x-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naxel/react-native-fs/53ac0a75198c942564184f5675d5fcb9635c5751/IntegrationTests/Images.xcassets/AppIcon.appiconset/uie_icon@2x-2.png -------------------------------------------------------------------------------- /IntegrationTests/Images.xcassets/AppIcon.appiconset/uie_icon@2x-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naxel/react-native-fs/53ac0a75198c942564184f5675d5fcb9635c5751/IntegrationTests/Images.xcassets/AppIcon.appiconset/uie_icon@2x-3.png -------------------------------------------------------------------------------- /IntegrationTests/Images.xcassets/AppIcon.appiconset/uie_icon@2x-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naxel/react-native-fs/53ac0a75198c942564184f5675d5fcb9635c5751/IntegrationTests/Images.xcassets/AppIcon.appiconset/uie_icon@2x-4.png -------------------------------------------------------------------------------- /IntegrationTests/Images.xcassets/AppIcon.appiconset/uie_icon@2x-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naxel/react-native-fs/53ac0a75198c942564184f5675d5fcb9635c5751/IntegrationTests/Images.xcassets/AppIcon.appiconset/uie_icon@2x-5.png -------------------------------------------------------------------------------- /IntegrationTests/Images.xcassets/AppIcon.appiconset/uie_icon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naxel/react-native-fs/53ac0a75198c942564184f5675d5fcb9635c5751/IntegrationTests/Images.xcassets/AppIcon.appiconset/uie_icon@2x.png -------------------------------------------------------------------------------- /IntegrationTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | com.facebook.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | NSLocationWhenInUseUsageDescription 38 | You need to add NSLocationWhenInUseUsageDescription key in Info.plist to enable geolocation, otherwise it is going to *fail silently*! 39 | UIViewControllerBasedStatusBarAppearance 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /IntegrationTests/IntegrationTestHarnessTest.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | */ 9 | 'use strict'; 10 | import PropTypes from 'prop-types'; 11 | 12 | var createClass = require('create-react-class'); 13 | var RCTTestModule = require('NativeModules').TestModule; 14 | var React = require('react-native'); 15 | var { 16 | Text, 17 | View, 18 | } = React; 19 | 20 | var IntegrationTestHarnessTest = createClass({ 21 | propTypes: { 22 | shouldThrow: PropTypes.bool, 23 | waitOneFrame: PropTypes.bool, 24 | }, 25 | 26 | getInitialState() { 27 | return { 28 | done: false, 29 | }; 30 | }, 31 | 32 | componentDidMount() { 33 | if (this.props.waitOneFrame) { 34 | requestAnimationFrame(this.runTest); 35 | } else { 36 | this.runTest(); 37 | } 38 | }, 39 | 40 | runTest() { 41 | if (this.props.shouldThrow) { 42 | throw new Error('Throwing error because shouldThrow'); 43 | } 44 | if (!RCTTestModule) { 45 | throw new Error('RCTTestModule is not registered.'); 46 | } else if (!RCTTestModule.markTestCompleted) { 47 | throw new Error('RCTTestModule.markTestCompleted not defined.'); 48 | } 49 | this.setState({done: true}, RCTTestModule.markTestCompleted); 50 | }, 51 | 52 | render() { 53 | return ( 54 | 55 | 56 | {this.constructor.displayName + ': '} 57 | {this.state.done ? 'Done' : 'Testing...'} 58 | 59 | 60 | ); 61 | } 62 | }); 63 | 64 | module.exports = IntegrationTestHarnessTest; 65 | -------------------------------------------------------------------------------- /IntegrationTests/IntegrationTests.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 004D28A31AAF61C70097A701 /* IntegrationTestsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 004D28A21AAF61C70097A701 /* IntegrationTestsTests.m */; }; 11 | 13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; }; 12 | 13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB11A68108700A75B9A /* LaunchScreen.xib */; }; 13 | 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; 14 | 13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; 15 | F24706C11B1F865C00001A84 /* libRCTTest.a in Frameworks */ = {isa = PBXBuildFile; fileRef = F24706C01B1F863700001A84 /* libRCTTest.a */; }; 16 | F24706C21B1F865C00001A84 /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = F24706BA1B1F85BD00001A84 /* libReact.a */; }; 17 | F24706CF1B1F898A00001A84 /* libRCTText.a in Frameworks */ = {isa = PBXBuildFile; fileRef = F24706CE1B1F896000001A84 /* libRCTText.a */; }; 18 | F24706D01B1F898A00001A84 /* libRCTWebSocket.a in Frameworks */ = {isa = PBXBuildFile; fileRef = F24706C81B1F87A800001A84 /* libRCTWebSocket.a */; }; 19 | F24706F81B1FC0DB00001A84 /* libRCTNetwork.a in Frameworks */ = {isa = PBXBuildFile; fileRef = F24706F71B1FC0C300001A84 /* libRCTNetwork.a */; }; 20 | F24707431B1FC8E000001A84 /* libRNFS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = F24707421B1FC8D200001A84 /* libRNFS.a */; }; 21 | /* End PBXBuildFile section */ 22 | 23 | /* Begin PBXContainerItemProxy section */ 24 | 58005BCB1ABA44F10062E044 /* PBXContainerItemProxy */ = { 25 | isa = PBXContainerItemProxy; 26 | containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */; 27 | proxyType = 1; 28 | remoteGlobalIDString = 13B07F861A680F5B00A75B9A; 29 | remoteInfo = IntegrationTests; 30 | }; 31 | F24706B91B1F85BD00001A84 /* PBXContainerItemProxy */ = { 32 | isa = PBXContainerItemProxy; 33 | containerPortal = F24706B51B1F85BD00001A84 /* React.xcodeproj */; 34 | proxyType = 2; 35 | remoteGlobalIDString = 83CBBA2E1A601D0E00E9B192; 36 | remoteInfo = React; 37 | }; 38 | F24706BF1B1F863700001A84 /* PBXContainerItemProxy */ = { 39 | isa = PBXContainerItemProxy; 40 | containerPortal = F24706BB1B1F863700001A84 /* RCTTest.xcodeproj */; 41 | proxyType = 2; 42 | remoteGlobalIDString = 580C376F1AB104AF0015E709; 43 | remoteInfo = RCTTest; 44 | }; 45 | F24706C71B1F87A800001A84 /* PBXContainerItemProxy */ = { 46 | isa = PBXContainerItemProxy; 47 | containerPortal = F24706C31B1F87A800001A84 /* RCTWebSocket.xcodeproj */; 48 | proxyType = 2; 49 | remoteGlobalIDString = 3C86DF461ADF2C930047B81A; 50 | remoteInfo = RCTWebSocket; 51 | }; 52 | F24706CD1B1F896000001A84 /* PBXContainerItemProxy */ = { 53 | isa = PBXContainerItemProxy; 54 | containerPortal = F24706C91B1F895F00001A84 /* RCTText.xcodeproj */; 55 | proxyType = 2; 56 | remoteGlobalIDString = 58B5119B1A9E6C1200147676; 57 | remoteInfo = RCTText; 58 | }; 59 | F24706F61B1FC0C300001A84 /* PBXContainerItemProxy */ = { 60 | isa = PBXContainerItemProxy; 61 | containerPortal = F24706F21B1FC0C300001A84 /* RCTNetwork.xcodeproj */; 62 | proxyType = 2; 63 | remoteGlobalIDString = 58B511DB1A9E6C8500147676; 64 | remoteInfo = RCTNetwork; 65 | }; 66 | F24707411B1FC8D200001A84 /* PBXContainerItemProxy */ = { 67 | isa = PBXContainerItemProxy; 68 | containerPortal = F24707381B1FC8D200001A84 /* RNFS.xcodeproj */; 69 | proxyType = 2; 70 | remoteGlobalIDString = F12AFB9B1ADAF8F800E0535D; 71 | remoteInfo = RNFS; 72 | }; 73 | /* End PBXContainerItemProxy section */ 74 | 75 | /* Begin PBXFileReference section */ 76 | 004D289E1AAF61C70097A701 /* IntegrationTestsTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = IntegrationTestsTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 77 | 004D28A11AAF61C70097A701 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 78 | 004D28A21AAF61C70097A701 /* IntegrationTestsTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = IntegrationTestsTests.m; sourceTree = ""; }; 79 | 13B07F961A680F5B00A75B9A /* IntegrationTests.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = IntegrationTests.app; sourceTree = BUILT_PRODUCTS_DIR; }; 80 | 13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 81 | 13B07FB01A68108700A75B9A /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 82 | 13B07FB21A68108700A75B9A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; 83 | 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 84 | 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 85 | 13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 86 | F24706B51B1F85BD00001A84 /* React.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = React.xcodeproj; path = "../node_modules/react-native/React/React.xcodeproj"; sourceTree = ""; }; 87 | F24706BB1B1F863700001A84 /* RCTTest.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTTest.xcodeproj; path = "../node_modules/react-native/Libraries/RCTTest/RCTTest.xcodeproj"; sourceTree = ""; }; 88 | F24706C31B1F87A800001A84 /* RCTWebSocket.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTWebSocket.xcodeproj; path = "../node_modules/react-native/Libraries/WebSocket/RCTWebSocket.xcodeproj"; sourceTree = ""; }; 89 | F24706C91B1F895F00001A84 /* RCTText.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTText.xcodeproj; path = "../node_modules/react-native/Libraries/Text/RCTText.xcodeproj"; sourceTree = ""; }; 90 | F24706F21B1FC0C300001A84 /* RCTNetwork.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTNetwork.xcodeproj; path = "../node_modules/react-native/Libraries/Network/RCTNetwork.xcodeproj"; sourceTree = ""; }; 91 | F24707381B1FC8D200001A84 /* RNFS.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RNFS.xcodeproj; path = ../RNFS.xcodeproj; sourceTree = ""; }; 92 | /* End PBXFileReference section */ 93 | 94 | /* Begin PBXFrameworksBuildPhase section */ 95 | 004D289B1AAF61C70097A701 /* Frameworks */ = { 96 | isa = PBXFrameworksBuildPhase; 97 | buildActionMask = 2147483647; 98 | files = ( 99 | ); 100 | runOnlyForDeploymentPostprocessing = 0; 101 | }; 102 | 13B07F8C1A680F5B00A75B9A /* Frameworks */ = { 103 | isa = PBXFrameworksBuildPhase; 104 | buildActionMask = 2147483647; 105 | files = ( 106 | F24707431B1FC8E000001A84 /* libRNFS.a in Frameworks */, 107 | F24706F81B1FC0DB00001A84 /* libRCTNetwork.a in Frameworks */, 108 | F24706CF1B1F898A00001A84 /* libRCTText.a in Frameworks */, 109 | F24706D01B1F898A00001A84 /* libRCTWebSocket.a in Frameworks */, 110 | F24706C11B1F865C00001A84 /* libRCTTest.a in Frameworks */, 111 | F24706C21B1F865C00001A84 /* libReact.a in Frameworks */, 112 | ); 113 | runOnlyForDeploymentPostprocessing = 0; 114 | }; 115 | /* End PBXFrameworksBuildPhase section */ 116 | 117 | /* Begin PBXGroup section */ 118 | 004D289F1AAF61C70097A701 /* IntegrationTestsTests */ = { 119 | isa = PBXGroup; 120 | children = ( 121 | 004D28A21AAF61C70097A701 /* IntegrationTestsTests.m */, 122 | 004D28A01AAF61C70097A701 /* Supporting Files */, 123 | ); 124 | path = IntegrationTestsTests; 125 | sourceTree = ""; 126 | }; 127 | 004D28A01AAF61C70097A701 /* Supporting Files */ = { 128 | isa = PBXGroup; 129 | children = ( 130 | 004D28A11AAF61C70097A701 /* Info.plist */, 131 | ); 132 | name = "Supporting Files"; 133 | sourceTree = ""; 134 | }; 135 | 1316A21D1AA397F400C0188E /* Libraries */ = { 136 | isa = PBXGroup; 137 | children = ( 138 | F24707381B1FC8D200001A84 /* RNFS.xcodeproj */, 139 | F24706F21B1FC0C300001A84 /* RCTNetwork.xcodeproj */, 140 | F24706C91B1F895F00001A84 /* RCTText.xcodeproj */, 141 | F24706C31B1F87A800001A84 /* RCTWebSocket.xcodeproj */, 142 | F24706BB1B1F863700001A84 /* RCTTest.xcodeproj */, 143 | F24706B51B1F85BD00001A84 /* React.xcodeproj */, 144 | ); 145 | name = Libraries; 146 | sourceTree = ""; 147 | }; 148 | 13B07FAE1A68108700A75B9A /* IntegrationTests */ = { 149 | isa = PBXGroup; 150 | children = ( 151 | 13B07FAF1A68108700A75B9A /* AppDelegate.h */, 152 | 13B07FB01A68108700A75B9A /* AppDelegate.m */, 153 | 13B07FB51A68108700A75B9A /* Images.xcassets */, 154 | 13B07FB61A68108700A75B9A /* Info.plist */, 155 | 13B07FB11A68108700A75B9A /* LaunchScreen.xib */, 156 | 13B07FB71A68108700A75B9A /* main.m */, 157 | ); 158 | name = IntegrationTests; 159 | sourceTree = ""; 160 | }; 161 | 83CBB9F61A601CBA00E9B192 = { 162 | isa = PBXGroup; 163 | children = ( 164 | 13B07FAE1A68108700A75B9A /* IntegrationTests */, 165 | 1316A21D1AA397F400C0188E /* Libraries */, 166 | 004D289F1AAF61C70097A701 /* IntegrationTestsTests */, 167 | 83CBBA001A601CBA00E9B192 /* Products */, 168 | ); 169 | indentWidth = 2; 170 | sourceTree = ""; 171 | tabWidth = 2; 172 | }; 173 | 83CBBA001A601CBA00E9B192 /* Products */ = { 174 | isa = PBXGroup; 175 | children = ( 176 | 13B07F961A680F5B00A75B9A /* IntegrationTests.app */, 177 | 004D289E1AAF61C70097A701 /* IntegrationTestsTests.xctest */, 178 | ); 179 | name = Products; 180 | sourceTree = ""; 181 | }; 182 | F24706B61B1F85BD00001A84 /* Products */ = { 183 | isa = PBXGroup; 184 | children = ( 185 | F24706BA1B1F85BD00001A84 /* libReact.a */, 186 | ); 187 | name = Products; 188 | sourceTree = ""; 189 | }; 190 | F24706BC1B1F863700001A84 /* Products */ = { 191 | isa = PBXGroup; 192 | children = ( 193 | F24706C01B1F863700001A84 /* libRCTTest.a */, 194 | ); 195 | name = Products; 196 | sourceTree = ""; 197 | }; 198 | F24706C41B1F87A800001A84 /* Products */ = { 199 | isa = PBXGroup; 200 | children = ( 201 | F24706C81B1F87A800001A84 /* libRCTWebSocket.a */, 202 | ); 203 | name = Products; 204 | sourceTree = ""; 205 | }; 206 | F24706CA1B1F895F00001A84 /* Products */ = { 207 | isa = PBXGroup; 208 | children = ( 209 | F24706CE1B1F896000001A84 /* libRCTText.a */, 210 | ); 211 | name = Products; 212 | sourceTree = ""; 213 | }; 214 | F24706F31B1FC0C300001A84 /* Products */ = { 215 | isa = PBXGroup; 216 | children = ( 217 | F24706F71B1FC0C300001A84 /* libRCTNetwork.a */, 218 | ); 219 | name = Products; 220 | sourceTree = ""; 221 | }; 222 | F24707391B1FC8D200001A84 /* Products */ = { 223 | isa = PBXGroup; 224 | children = ( 225 | F24707421B1FC8D200001A84 /* libRNFS.a */, 226 | ); 227 | name = Products; 228 | sourceTree = ""; 229 | }; 230 | /* End PBXGroup section */ 231 | 232 | /* Begin PBXNativeTarget section */ 233 | 004D289D1AAF61C70097A701 /* IntegrationTestsTests */ = { 234 | isa = PBXNativeTarget; 235 | buildConfigurationList = 004D28AD1AAF61C70097A701 /* Build configuration list for PBXNativeTarget "IntegrationTestsTests" */; 236 | buildPhases = ( 237 | 004D289A1AAF61C70097A701 /* Sources */, 238 | 004D289B1AAF61C70097A701 /* Frameworks */, 239 | 004D289C1AAF61C70097A701 /* Resources */, 240 | ); 241 | buildRules = ( 242 | ); 243 | dependencies = ( 244 | 58005BCC1ABA44F10062E044 /* PBXTargetDependency */, 245 | ); 246 | name = IntegrationTestsTests; 247 | productName = IntegrationTestsTests; 248 | productReference = 004D289E1AAF61C70097A701 /* IntegrationTestsTests.xctest */; 249 | productType = "com.apple.product-type.bundle.unit-test"; 250 | }; 251 | 13B07F861A680F5B00A75B9A /* IntegrationTests */ = { 252 | isa = PBXNativeTarget; 253 | buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "IntegrationTests" */; 254 | buildPhases = ( 255 | 13B07F871A680F5B00A75B9A /* Sources */, 256 | 13B07F8C1A680F5B00A75B9A /* Frameworks */, 257 | 13B07F8E1A680F5B00A75B9A /* Resources */, 258 | ); 259 | buildRules = ( 260 | ); 261 | dependencies = ( 262 | ); 263 | name = IntegrationTests; 264 | productName = "Hello World"; 265 | productReference = 13B07F961A680F5B00A75B9A /* IntegrationTests.app */; 266 | productType = "com.apple.product-type.application"; 267 | }; 268 | /* End PBXNativeTarget section */ 269 | 270 | /* Begin PBXProject section */ 271 | 83CBB9F71A601CBA00E9B192 /* Project object */ = { 272 | isa = PBXProject; 273 | attributes = { 274 | LastUpgradeCheck = 0610; 275 | ORGANIZATIONNAME = Facebook; 276 | TargetAttributes = { 277 | 004D289D1AAF61C70097A701 = { 278 | CreatedOnToolsVersion = 6.1.1; 279 | TestTargetID = 13B07F861A680F5B00A75B9A; 280 | }; 281 | }; 282 | }; 283 | buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "IntegrationTests" */; 284 | compatibilityVersion = "Xcode 3.2"; 285 | developmentRegion = English; 286 | hasScannedForEncodings = 0; 287 | knownRegions = ( 288 | en, 289 | Base, 290 | ); 291 | mainGroup = 83CBB9F61A601CBA00E9B192; 292 | productRefGroup = 83CBBA001A601CBA00E9B192 /* Products */; 293 | projectDirPath = ""; 294 | projectReferences = ( 295 | { 296 | ProductGroup = F24706F31B1FC0C300001A84 /* Products */; 297 | ProjectRef = F24706F21B1FC0C300001A84 /* RCTNetwork.xcodeproj */; 298 | }, 299 | { 300 | ProductGroup = F24706BC1B1F863700001A84 /* Products */; 301 | ProjectRef = F24706BB1B1F863700001A84 /* RCTTest.xcodeproj */; 302 | }, 303 | { 304 | ProductGroup = F24706CA1B1F895F00001A84 /* Products */; 305 | ProjectRef = F24706C91B1F895F00001A84 /* RCTText.xcodeproj */; 306 | }, 307 | { 308 | ProductGroup = F24706C41B1F87A800001A84 /* Products */; 309 | ProjectRef = F24706C31B1F87A800001A84 /* RCTWebSocket.xcodeproj */; 310 | }, 311 | { 312 | ProductGroup = F24706B61B1F85BD00001A84 /* Products */; 313 | ProjectRef = F24706B51B1F85BD00001A84 /* React.xcodeproj */; 314 | }, 315 | { 316 | ProductGroup = F24707391B1FC8D200001A84 /* Products */; 317 | ProjectRef = F24707381B1FC8D200001A84 /* RNFS.xcodeproj */; 318 | }, 319 | ); 320 | projectRoot = ""; 321 | targets = ( 322 | 13B07F861A680F5B00A75B9A /* IntegrationTests */, 323 | 004D289D1AAF61C70097A701 /* IntegrationTestsTests */, 324 | ); 325 | }; 326 | /* End PBXProject section */ 327 | 328 | /* Begin PBXReferenceProxy section */ 329 | F24706BA1B1F85BD00001A84 /* libReact.a */ = { 330 | isa = PBXReferenceProxy; 331 | fileType = archive.ar; 332 | path = libReact.a; 333 | remoteRef = F24706B91B1F85BD00001A84 /* PBXContainerItemProxy */; 334 | sourceTree = BUILT_PRODUCTS_DIR; 335 | }; 336 | F24706C01B1F863700001A84 /* libRCTTest.a */ = { 337 | isa = PBXReferenceProxy; 338 | fileType = archive.ar; 339 | path = libRCTTest.a; 340 | remoteRef = F24706BF1B1F863700001A84 /* PBXContainerItemProxy */; 341 | sourceTree = BUILT_PRODUCTS_DIR; 342 | }; 343 | F24706C81B1F87A800001A84 /* libRCTWebSocket.a */ = { 344 | isa = PBXReferenceProxy; 345 | fileType = archive.ar; 346 | path = libRCTWebSocket.a; 347 | remoteRef = F24706C71B1F87A800001A84 /* PBXContainerItemProxy */; 348 | sourceTree = BUILT_PRODUCTS_DIR; 349 | }; 350 | F24706CE1B1F896000001A84 /* libRCTText.a */ = { 351 | isa = PBXReferenceProxy; 352 | fileType = archive.ar; 353 | path = libRCTText.a; 354 | remoteRef = F24706CD1B1F896000001A84 /* PBXContainerItemProxy */; 355 | sourceTree = BUILT_PRODUCTS_DIR; 356 | }; 357 | F24706F71B1FC0C300001A84 /* libRCTNetwork.a */ = { 358 | isa = PBXReferenceProxy; 359 | fileType = archive.ar; 360 | path = libRCTNetwork.a; 361 | remoteRef = F24706F61B1FC0C300001A84 /* PBXContainerItemProxy */; 362 | sourceTree = BUILT_PRODUCTS_DIR; 363 | }; 364 | F24707421B1FC8D200001A84 /* libRNFS.a */ = { 365 | isa = PBXReferenceProxy; 366 | fileType = archive.ar; 367 | path = libRNFS.a; 368 | remoteRef = F24707411B1FC8D200001A84 /* PBXContainerItemProxy */; 369 | sourceTree = BUILT_PRODUCTS_DIR; 370 | }; 371 | /* End PBXReferenceProxy section */ 372 | 373 | /* Begin PBXResourcesBuildPhase section */ 374 | 004D289C1AAF61C70097A701 /* Resources */ = { 375 | isa = PBXResourcesBuildPhase; 376 | buildActionMask = 2147483647; 377 | files = ( 378 | ); 379 | runOnlyForDeploymentPostprocessing = 0; 380 | }; 381 | 13B07F8E1A680F5B00A75B9A /* Resources */ = { 382 | isa = PBXResourcesBuildPhase; 383 | buildActionMask = 2147483647; 384 | files = ( 385 | 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */, 386 | 13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */, 387 | ); 388 | runOnlyForDeploymentPostprocessing = 0; 389 | }; 390 | /* End PBXResourcesBuildPhase section */ 391 | 392 | /* Begin PBXSourcesBuildPhase section */ 393 | 004D289A1AAF61C70097A701 /* Sources */ = { 394 | isa = PBXSourcesBuildPhase; 395 | buildActionMask = 2147483647; 396 | files = ( 397 | 004D28A31AAF61C70097A701 /* IntegrationTestsTests.m in Sources */, 398 | ); 399 | runOnlyForDeploymentPostprocessing = 0; 400 | }; 401 | 13B07F871A680F5B00A75B9A /* Sources */ = { 402 | isa = PBXSourcesBuildPhase; 403 | buildActionMask = 2147483647; 404 | files = ( 405 | 13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */, 406 | 13B07FC11A68108700A75B9A /* main.m in Sources */, 407 | ); 408 | runOnlyForDeploymentPostprocessing = 0; 409 | }; 410 | /* End PBXSourcesBuildPhase section */ 411 | 412 | /* Begin PBXTargetDependency section */ 413 | 58005BCC1ABA44F10062E044 /* PBXTargetDependency */ = { 414 | isa = PBXTargetDependency; 415 | target = 13B07F861A680F5B00A75B9A /* IntegrationTests */; 416 | targetProxy = 58005BCB1ABA44F10062E044 /* PBXContainerItemProxy */; 417 | }; 418 | /* End PBXTargetDependency section */ 419 | 420 | /* Begin PBXVariantGroup section */ 421 | 13B07FB11A68108700A75B9A /* LaunchScreen.xib */ = { 422 | isa = PBXVariantGroup; 423 | children = ( 424 | 13B07FB21A68108700A75B9A /* Base */, 425 | ); 426 | name = LaunchScreen.xib; 427 | sourceTree = ""; 428 | }; 429 | /* End PBXVariantGroup section */ 430 | 431 | /* Begin XCBuildConfiguration section */ 432 | 004D28A61AAF61C70097A701 /* Debug */ = { 433 | isa = XCBuildConfiguration; 434 | buildSettings = { 435 | BUNDLE_LOADER = "$(TEST_HOST)"; 436 | FRAMEWORK_SEARCH_PATHS = ( 437 | "$(inherited)", 438 | "$(SDKROOT)/Developer/Library/Frameworks", 439 | "$(DEVELOPER_FRAMEWORKS_DIR)", 440 | ); 441 | GCC_PREPROCESSOR_DEFINITIONS = ( 442 | "DEBUG=1", 443 | "FB_REFERENCE_IMAGE_DIR=\"\\\"$(SOURCE_ROOT)/$(PROJECT_NAME)Tests/ReferenceImages\\\"\"", 444 | "$(inherited)", 445 | ); 446 | HEADER_SEARCH_PATHS = ( 447 | "$(inherited)", 448 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, 449 | "$(SRCROOT)/../node_modules/react-native/React/**", 450 | ); 451 | INFOPLIST_FILE = IntegrationTestsTests/Info.plist; 452 | IPHONEOS_DEPLOYMENT_TARGET = 8.1; 453 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 454 | PRODUCT_NAME = "$(TARGET_NAME)"; 455 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/IntegrationTests.app/IntegrationTests"; 456 | }; 457 | name = Debug; 458 | }; 459 | 004D28A71AAF61C70097A701 /* Release */ = { 460 | isa = XCBuildConfiguration; 461 | buildSettings = { 462 | BUNDLE_LOADER = "$(TEST_HOST)"; 463 | FRAMEWORK_SEARCH_PATHS = ( 464 | "$(inherited)", 465 | "$(SDKROOT)/Developer/Library/Frameworks", 466 | "$(DEVELOPER_FRAMEWORKS_DIR)", 467 | ); 468 | HEADER_SEARCH_PATHS = ( 469 | "$(inherited)", 470 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, 471 | "$(SRCROOT)/../node_modules/react-native/React/**", 472 | ); 473 | INFOPLIST_FILE = IntegrationTestsTests/Info.plist; 474 | IPHONEOS_DEPLOYMENT_TARGET = 8.1; 475 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 476 | PRODUCT_NAME = "$(TARGET_NAME)"; 477 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/IntegrationTests.app/IntegrationTests"; 478 | }; 479 | name = Release; 480 | }; 481 | 13B07F941A680F5B00A75B9A /* Debug */ = { 482 | isa = XCBuildConfiguration; 483 | buildSettings = { 484 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 485 | HEADER_SEARCH_PATHS = ( 486 | "$(inherited)", 487 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, 488 | "$(SRCROOT)/../node_modules/react-native/React/**", 489 | "$(SRCROOT)/../node_modules/react-native/Libraries", 490 | ); 491 | INFOPLIST_FILE = "$(SRCROOT)/Info.plist"; 492 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 493 | LIBRARY_SEARCH_PATHS = "$(inherited)"; 494 | OTHER_LDFLAGS = "-ObjC"; 495 | PRODUCT_NAME = IntegrationTests; 496 | }; 497 | name = Debug; 498 | }; 499 | 13B07F951A680F5B00A75B9A /* Release */ = { 500 | isa = XCBuildConfiguration; 501 | buildSettings = { 502 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 503 | HEADER_SEARCH_PATHS = ( 504 | "$(inherited)", 505 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, 506 | "$(SRCROOT)/../node_modules/react-native/React/**", 507 | "$(SRCROOT)/../node_modules/react-native/Libraries", 508 | ); 509 | INFOPLIST_FILE = "$(SRCROOT)/Info.plist"; 510 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 511 | LIBRARY_SEARCH_PATHS = "$(inherited)"; 512 | OTHER_LDFLAGS = "-ObjC"; 513 | PRODUCT_NAME = IntegrationTests; 514 | }; 515 | name = Release; 516 | }; 517 | 83CBBA201A601CBA00E9B192 /* Debug */ = { 518 | isa = XCBuildConfiguration; 519 | buildSettings = { 520 | ALWAYS_SEARCH_USER_PATHS = NO; 521 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 522 | CLANG_CXX_LIBRARY = "libc++"; 523 | CLANG_ENABLE_MODULES = YES; 524 | CLANG_ENABLE_OBJC_ARC = YES; 525 | CLANG_WARN_BOOL_CONVERSION = YES; 526 | CLANG_WARN_CONSTANT_CONVERSION = YES; 527 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 528 | CLANG_WARN_EMPTY_BODY = YES; 529 | CLANG_WARN_ENUM_CONVERSION = YES; 530 | CLANG_WARN_INT_CONVERSION = YES; 531 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 532 | CLANG_WARN_UNREACHABLE_CODE = YES; 533 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 534 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 535 | COPY_PHASE_STRIP = NO; 536 | ENABLE_STRICT_OBJC_MSGSEND = YES; 537 | GCC_C_LANGUAGE_STANDARD = gnu99; 538 | GCC_DYNAMIC_NO_PIC = NO; 539 | GCC_OPTIMIZATION_LEVEL = 0; 540 | GCC_PREPROCESSOR_DEFINITIONS = ( 541 | "DEBUG=1", 542 | "$(inherited)", 543 | ); 544 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 545 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 546 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 547 | GCC_WARN_UNDECLARED_SELECTOR = YES; 548 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 549 | GCC_WARN_UNUSED_FUNCTION = YES; 550 | GCC_WARN_UNUSED_VARIABLE = YES; 551 | HEADER_SEARCH_PATHS = ( 552 | "$(inherited)", 553 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, 554 | "$(SRCROOT)/../React/**", 555 | ); 556 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 557 | MTL_ENABLE_DEBUG_INFO = YES; 558 | ONLY_ACTIVE_ARCH = YES; 559 | SDKROOT = iphoneos; 560 | }; 561 | name = Debug; 562 | }; 563 | 83CBBA211A601CBA00E9B192 /* Release */ = { 564 | isa = XCBuildConfiguration; 565 | buildSettings = { 566 | ALWAYS_SEARCH_USER_PATHS = NO; 567 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 568 | CLANG_CXX_LIBRARY = "libc++"; 569 | CLANG_ENABLE_MODULES = YES; 570 | CLANG_ENABLE_OBJC_ARC = YES; 571 | CLANG_WARN_BOOL_CONVERSION = YES; 572 | CLANG_WARN_CONSTANT_CONVERSION = YES; 573 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 574 | CLANG_WARN_EMPTY_BODY = YES; 575 | CLANG_WARN_ENUM_CONVERSION = YES; 576 | CLANG_WARN_INT_CONVERSION = YES; 577 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 578 | CLANG_WARN_UNREACHABLE_CODE = YES; 579 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 580 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 581 | COPY_PHASE_STRIP = YES; 582 | ENABLE_NS_ASSERTIONS = NO; 583 | ENABLE_STRICT_OBJC_MSGSEND = YES; 584 | GCC_C_LANGUAGE_STANDARD = gnu99; 585 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 586 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 587 | GCC_WARN_UNDECLARED_SELECTOR = YES; 588 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 589 | GCC_WARN_UNUSED_FUNCTION = YES; 590 | GCC_WARN_UNUSED_VARIABLE = YES; 591 | HEADER_SEARCH_PATHS = ( 592 | "$(inherited)", 593 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, 594 | "$(SRCROOT)/../React/**", 595 | ); 596 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 597 | MTL_ENABLE_DEBUG_INFO = NO; 598 | SDKROOT = iphoneos; 599 | VALIDATE_PRODUCT = YES; 600 | }; 601 | name = Release; 602 | }; 603 | /* End XCBuildConfiguration section */ 604 | 605 | /* Begin XCConfigurationList section */ 606 | 004D28AD1AAF61C70097A701 /* Build configuration list for PBXNativeTarget "IntegrationTestsTests" */ = { 607 | isa = XCConfigurationList; 608 | buildConfigurations = ( 609 | 004D28A61AAF61C70097A701 /* Debug */, 610 | 004D28A71AAF61C70097A701 /* Release */, 611 | ); 612 | defaultConfigurationIsVisible = 0; 613 | defaultConfigurationName = Release; 614 | }; 615 | 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "IntegrationTests" */ = { 616 | isa = XCConfigurationList; 617 | buildConfigurations = ( 618 | 13B07F941A680F5B00A75B9A /* Debug */, 619 | 13B07F951A680F5B00A75B9A /* Release */, 620 | ); 621 | defaultConfigurationIsVisible = 0; 622 | defaultConfigurationName = Release; 623 | }; 624 | 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "IntegrationTests" */ = { 625 | isa = XCConfigurationList; 626 | buildConfigurations = ( 627 | 83CBBA201A601CBA00E9B192 /* Debug */, 628 | 83CBBA211A601CBA00E9B192 /* Release */, 629 | ); 630 | defaultConfigurationIsVisible = 0; 631 | defaultConfigurationName = Release; 632 | }; 633 | /* End XCConfigurationList section */ 634 | }; 635 | rootObject = 83CBB9F71A601CBA00E9B192 /* Project object */; 636 | } 637 | -------------------------------------------------------------------------------- /IntegrationTests/IntegrationTests.xcodeproj/xcshareddata/xcschemes/IntegrationTests.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 44 | 45 | 47 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 65 | 66 | 75 | 76 | 82 | 83 | 84 | 85 | 86 | 87 | 93 | 94 | 100 | 101 | 102 | 103 | 105 | 106 | 109 | 110 | 111 | -------------------------------------------------------------------------------- /IntegrationTests/IntegrationTestsApp.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | * 9 | * @providesModule IntegrationTestsApp 10 | */ 11 | 'use strict'; 12 | 13 | var React = require('react-native'); 14 | var createClass = require('create-react-class'); 15 | 16 | var { 17 | AppRegistry, 18 | ScrollView, 19 | StyleSheet, 20 | Text, 21 | TouchableOpacity, 22 | View, 23 | } = React; 24 | 25 | var TESTS = [ 26 | require('./IntegrationTestHarnessTest'), 27 | require('./FSTest') 28 | ]; 29 | 30 | TESTS.forEach( 31 | (test) => AppRegistry.registerComponent(test.displayName, () => test) 32 | ); 33 | 34 | var IntegrationTestsApp = createClass({ 35 | getInitialState: function() { 36 | return { 37 | test: null, 38 | }; 39 | }, 40 | render: function() { 41 | if (this.state.test) { 42 | return ( 43 | 44 | 45 | 46 | ); 47 | } 48 | return ( 49 | 50 | 51 | Click on a test to run it in this shell for easier debugging and 52 | development. Run all tests in the testing envirnment with cmd+U in 53 | Xcode. 54 | 55 | 56 | 57 | {TESTS.map((test) => [ 58 | this.setState({test})}> 59 | 60 | 61 | {test.displayName} 62 | 63 | 64 | , 65 | 66 | ])} 67 | 68 | 69 | ); 70 | } 71 | }); 72 | 73 | var styles = StyleSheet.create({ 74 | container: { 75 | backgroundColor: 'white', 76 | marginTop: 40, 77 | margin: 15, 78 | }, 79 | row: { 80 | padding: 10, 81 | }, 82 | testName: { 83 | fontWeight: '500', 84 | }, 85 | separator: { 86 | height: 1, 87 | backgroundColor: '#bbbbbb', 88 | }, 89 | }); 90 | 91 | AppRegistry.registerComponent('IntegrationTestsApp', () => IntegrationTestsApp); 92 | -------------------------------------------------------------------------------- /IntegrationTests/IntegrationTestsTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | com.facebook.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /IntegrationTests/IntegrationTestsTests/IntegrationTestsTests.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | */ 9 | 10 | #import 11 | #import 12 | 13 | #import 14 | 15 | #import 16 | 17 | @interface IntegrationTestsTests : XCTestCase 18 | 19 | @end 20 | 21 | @implementation IntegrationTestsTests 22 | { 23 | RCTTestRunner *_runner; 24 | } 25 | 26 | - (void)setUp 27 | { 28 | #ifdef __LP64__ 29 | RCTAssert(!__LP64__, @"Tests should be run on 32-bit device simulators (e.g. iPhone 5)"); 30 | #endif 31 | NSString *version = [[UIDevice currentDevice] systemVersion]; 32 | RCTAssert([version integerValue] == 8, @"Tests should be run on iOS 8.x, found %@", version); 33 | _runner = RCTInitRunnerForApp(@"IntegrationTests/IntegrationTestsApp"); 34 | 35 | // If tests have changes, set recordMode = YES below and run the affected 36 | // tests on an iPhone5, iOS 8.1 simulator. 37 | _runner.recordMode = NO; 38 | } 39 | 40 | #pragma mark Logic Tests 41 | 42 | - (void)testTheTester 43 | { 44 | [_runner runTest:_cmd module:@"IntegrationTestHarnessTest"]; 45 | } 46 | 47 | - (void)testTheTester_waitOneFrame 48 | { 49 | [_runner runTest:_cmd 50 | module:@"IntegrationTestHarnessTest" 51 | initialProps:@{@"waitOneFrame": @YES} 52 | expectErrorBlock:nil]; 53 | } 54 | 55 | // TODO: this seems to stall forever - figure out why 56 | - (void)DISABLED_testTheTester_ExpectError 57 | { 58 | [_runner runTest:_cmd 59 | module:@"IntegrationTestHarnessTest" 60 | initialProps:@{@"shouldThrow": @YES} 61 | expectErrorRegex:@"because shouldThrow"]; 62 | } 63 | 64 | - (void)testFS 65 | { 66 | [_runner runTest:_cmd module:@"FSTest"]; 67 | } 68 | 69 | - (void)testZZZ_NotInRecordMode 70 | { 71 | RCTAssert(_runner.recordMode == NO, @"Don't forget to turn record mode back to NO before commit."); 72 | } 73 | 74 | @end 75 | -------------------------------------------------------------------------------- /IntegrationTests/IntegrationTestsTests/ReferenceImages/IntegrationTests-IntegrationTestsApp/testSimpleSnapshot_1@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naxel/react-native-fs/53ac0a75198c942564184f5675d5fcb9635c5751/IntegrationTests/IntegrationTestsTests/ReferenceImages/IntegrationTests-IntegrationTestsApp/testSimpleSnapshot_1@2x.png -------------------------------------------------------------------------------- /IntegrationTests/main.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | */ 9 | 10 | #import 11 | #import "AppDelegate.h" 12 | 13 | int main(int argc, char * argv[]) { 14 | @autoreleasepool { 15 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Johannes Lumpe 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /NSArray+Map.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSArray+Map.h 3 | // RNFS 4 | // 5 | // taken from http://stackoverflow.com/questions/6127638/nsarray-equivalent-of-map 6 | 7 | #import 8 | 9 | @interface NSArray (Map) 10 | 11 | - (NSArray *)rnfs_mapObjectsUsingBlock:(id (^)(id obj, NSUInteger idx))block; 12 | 13 | @end -------------------------------------------------------------------------------- /NSArray+Map.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSArray+Map.m 3 | // RNFS 4 | 5 | #import "NSArray+Map.h" 6 | 7 | @implementation NSArray (Map) 8 | 9 | - (NSArray *)rnfs_mapObjectsUsingBlock:(id (^)(id obj, NSUInteger idx))block 10 | { 11 | NSMutableArray *result = [NSMutableArray arrayWithCapacity:[self count]]; 12 | 13 | [self enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { 14 | [result addObject:block(obj, idx)]; 15 | }]; 16 | 17 | return result; 18 | } 19 | 20 | @end -------------------------------------------------------------------------------- /RNFS.podspec: -------------------------------------------------------------------------------- 1 | require 'json' 2 | pjson = JSON.parse(File.read('package.json')) 3 | 4 | Pod::Spec.new do |s| 5 | 6 | s.name = "RNFS" 7 | s.version = pjson["version"] 8 | s.homepage = "https://github.com/itinance/react-native-fs" 9 | s.summary = pjson["description"] 10 | s.license = pjson["license"] 11 | s.author = { "Johannes Lumpe" => "johannes@lum.pe" } 12 | 13 | s.ios.deployment_target = '7.0' 14 | 15 | s.source = { :git => "https://github.com/itinance/react-native-fs", :tag => "v#{s.version}" } 16 | s.source_files = '*.{h,m}' 17 | s.preserve_paths = "**/*.js" 18 | 19 | s.dependency 'React' 20 | end -------------------------------------------------------------------------------- /RNFS.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 645644281EB8DAA100672408 /* NSArray+Map.m in Sources */ = {isa = PBXBuildFile; fileRef = F1EB08BA1AFD0E6A008F8F2B /* NSArray+Map.m */; }; 11 | 645644291EB8DAA100672408 /* RNFSManager.m in Sources */ = {isa = PBXBuildFile; fileRef = F1E59BDE1ADD662800ACA28A /* RNFSManager.m */; }; 12 | 6456442A1EB8DAA100672408 /* Downloader.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BF740761C033A2E0057A1E7 /* Downloader.m */; }; 13 | 6456442B1EB8DAA100672408 /* Uploader.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BB364CB1CDA130000435A01 /* Uploader.m */; }; 14 | 8BB364CC1CDA130000435A01 /* Uploader.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BB364CB1CDA130000435A01 /* Uploader.m */; }; 15 | 8BF740771C033A2E0057A1E7 /* Downloader.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BF740761C033A2E0057A1E7 /* Downloader.m */; }; 16 | F1E59BDF1ADD662800ACA28A /* RNFSManager.m in Sources */ = {isa = PBXBuildFile; fileRef = F1E59BDE1ADD662800ACA28A /* RNFSManager.m */; }; 17 | F1EB08BB1AFD0E6A008F8F2B /* NSArray+Map.m in Sources */ = {isa = PBXBuildFile; fileRef = F1EB08BA1AFD0E6A008F8F2B /* NSArray+Map.m */; }; 18 | /* End PBXBuildFile section */ 19 | 20 | /* Begin PBXCopyFilesBuildPhase section */ 21 | 6456441D1EB8DA9100672408 /* CopyFiles */ = { 22 | isa = PBXCopyFilesBuildPhase; 23 | buildActionMask = 2147483647; 24 | dstPath = "include/$(PRODUCT_NAME)"; 25 | dstSubfolderSpec = 16; 26 | files = ( 27 | ); 28 | runOnlyForDeploymentPostprocessing = 0; 29 | }; 30 | F12AFB991ADAF8F800E0535D /* CopyFiles */ = { 31 | isa = PBXCopyFilesBuildPhase; 32 | buildActionMask = 2147483647; 33 | dstPath = "include/$(PRODUCT_NAME)"; 34 | dstSubfolderSpec = 16; 35 | files = ( 36 | ); 37 | runOnlyForDeploymentPostprocessing = 0; 38 | }; 39 | /* End PBXCopyFilesBuildPhase section */ 40 | 41 | /* Begin PBXFileReference section */ 42 | 6456441F1EB8DA9100672408 /* libRNFS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRNFS.a; sourceTree = BUILT_PRODUCTS_DIR; }; 43 | 8BB364CA1CDA130000435A01 /* Uploader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Uploader.h; sourceTree = ""; }; 44 | 8BB364CB1CDA130000435A01 /* Uploader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Uploader.m; sourceTree = ""; }; 45 | 8BF740751C033A2E0057A1E7 /* Downloader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Downloader.h; sourceTree = ""; }; 46 | 8BF740761C033A2E0057A1E7 /* Downloader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Downloader.m; sourceTree = ""; }; 47 | F12AFB9B1ADAF8F800E0535D /* libRNFS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRNFS.a; sourceTree = BUILT_PRODUCTS_DIR; }; 48 | F1E59BDD1ADD662800ACA28A /* RNFSManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNFSManager.h; sourceTree = ""; }; 49 | F1E59BDE1ADD662800ACA28A /* RNFSManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNFSManager.m; sourceTree = ""; }; 50 | F1EB08B91AFD0E6A008F8F2B /* NSArray+Map.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSArray+Map.h"; sourceTree = ""; }; 51 | F1EB08BA1AFD0E6A008F8F2B /* NSArray+Map.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSArray+Map.m"; sourceTree = ""; }; 52 | /* End PBXFileReference section */ 53 | 54 | /* Begin PBXFrameworksBuildPhase section */ 55 | 6456441C1EB8DA9100672408 /* Frameworks */ = { 56 | isa = PBXFrameworksBuildPhase; 57 | buildActionMask = 2147483647; 58 | files = ( 59 | ); 60 | runOnlyForDeploymentPostprocessing = 0; 61 | }; 62 | F12AFB981ADAF8F800E0535D /* Frameworks */ = { 63 | isa = PBXFrameworksBuildPhase; 64 | buildActionMask = 2147483647; 65 | files = ( 66 | ); 67 | runOnlyForDeploymentPostprocessing = 0; 68 | }; 69 | /* End PBXFrameworksBuildPhase section */ 70 | 71 | /* Begin PBXGroup section */ 72 | F12AFB921ADAF8F800E0535D = { 73 | isa = PBXGroup; 74 | children = ( 75 | F1EB08B91AFD0E6A008F8F2B /* NSArray+Map.h */, 76 | F1EB08BA1AFD0E6A008F8F2B /* NSArray+Map.m */, 77 | F1E59BDD1ADD662800ACA28A /* RNFSManager.h */, 78 | F1E59BDE1ADD662800ACA28A /* RNFSManager.m */, 79 | 8BF740751C033A2E0057A1E7 /* Downloader.h */, 80 | 8BF740761C033A2E0057A1E7 /* Downloader.m */, 81 | 8BB364CA1CDA130000435A01 /* Uploader.h */, 82 | 8BB364CB1CDA130000435A01 /* Uploader.m */, 83 | F12AFB9C1ADAF8F800E0535D /* Products */, 84 | ); 85 | sourceTree = ""; 86 | }; 87 | F12AFB9C1ADAF8F800E0535D /* Products */ = { 88 | isa = PBXGroup; 89 | children = ( 90 | F12AFB9B1ADAF8F800E0535D /* libRNFS.a */, 91 | 6456441F1EB8DA9100672408 /* libRNFS.a */, 92 | ); 93 | name = Products; 94 | sourceTree = ""; 95 | }; 96 | /* End PBXGroup section */ 97 | 98 | /* Begin PBXNativeTarget section */ 99 | 6456441E1EB8DA9100672408 /* RNFS-tvOS */ = { 100 | isa = PBXNativeTarget; 101 | buildConfigurationList = 645644271EB8DA9100672408 /* Build configuration list for PBXNativeTarget "RNFS-tvOS" */; 102 | buildPhases = ( 103 | 6456441B1EB8DA9100672408 /* Sources */, 104 | 6456441C1EB8DA9100672408 /* Frameworks */, 105 | 6456441D1EB8DA9100672408 /* CopyFiles */, 106 | ); 107 | buildRules = ( 108 | ); 109 | dependencies = ( 110 | ); 111 | name = "RNFS-tvOS"; 112 | productName = "RNFS-tvOS"; 113 | productReference = 6456441F1EB8DA9100672408 /* libRNFS.a */; 114 | productType = "com.apple.product-type.library.static"; 115 | }; 116 | F12AFB9A1ADAF8F800E0535D /* RNFS */ = { 117 | isa = PBXNativeTarget; 118 | buildConfigurationList = F12AFBAF1ADAF8F800E0535D /* Build configuration list for PBXNativeTarget "RNFS" */; 119 | buildPhases = ( 120 | F12AFB971ADAF8F800E0535D /* Sources */, 121 | F12AFB981ADAF8F800E0535D /* Frameworks */, 122 | F12AFB991ADAF8F800E0535D /* CopyFiles */, 123 | ); 124 | buildRules = ( 125 | ); 126 | dependencies = ( 127 | ); 128 | name = RNFS; 129 | productName = RNLocalNotification; 130 | productReference = F12AFB9B1ADAF8F800E0535D /* libRNFS.a */; 131 | productType = "com.apple.product-type.library.static"; 132 | }; 133 | /* End PBXNativeTarget section */ 134 | 135 | /* Begin PBXProject section */ 136 | F12AFB931ADAF8F800E0535D /* Project object */ = { 137 | isa = PBXProject; 138 | attributes = { 139 | LastUpgradeCheck = 0630; 140 | ORGANIZATIONNAME = "Johannes Lumpe"; 141 | TargetAttributes = { 142 | 6456441E1EB8DA9100672408 = { 143 | CreatedOnToolsVersion = 8.3.2; 144 | ProvisioningStyle = Automatic; 145 | }; 146 | F12AFB9A1ADAF8F800E0535D = { 147 | CreatedOnToolsVersion = 6.3; 148 | }; 149 | }; 150 | }; 151 | buildConfigurationList = F12AFB961ADAF8F800E0535D /* Build configuration list for PBXProject "RNFS" */; 152 | compatibilityVersion = "Xcode 3.2"; 153 | developmentRegion = English; 154 | hasScannedForEncodings = 0; 155 | knownRegions = ( 156 | en, 157 | ); 158 | mainGroup = F12AFB921ADAF8F800E0535D; 159 | productRefGroup = F12AFB9C1ADAF8F800E0535D /* Products */; 160 | projectDirPath = ""; 161 | projectRoot = ""; 162 | targets = ( 163 | F12AFB9A1ADAF8F800E0535D /* RNFS */, 164 | 6456441E1EB8DA9100672408 /* RNFS-tvOS */, 165 | ); 166 | }; 167 | /* End PBXProject section */ 168 | 169 | /* Begin PBXSourcesBuildPhase section */ 170 | 6456441B1EB8DA9100672408 /* Sources */ = { 171 | isa = PBXSourcesBuildPhase; 172 | buildActionMask = 2147483647; 173 | files = ( 174 | 645644281EB8DAA100672408 /* NSArray+Map.m in Sources */, 175 | 645644291EB8DAA100672408 /* RNFSManager.m in Sources */, 176 | 6456442A1EB8DAA100672408 /* Downloader.m in Sources */, 177 | 6456442B1EB8DAA100672408 /* Uploader.m in Sources */, 178 | ); 179 | runOnlyForDeploymentPostprocessing = 0; 180 | }; 181 | F12AFB971ADAF8F800E0535D /* Sources */ = { 182 | isa = PBXSourcesBuildPhase; 183 | buildActionMask = 2147483647; 184 | files = ( 185 | F1E59BDF1ADD662800ACA28A /* RNFSManager.m in Sources */, 186 | 8BB364CC1CDA130000435A01 /* Uploader.m in Sources */, 187 | F1EB08BB1AFD0E6A008F8F2B /* NSArray+Map.m in Sources */, 188 | 8BF740771C033A2E0057A1E7 /* Downloader.m in Sources */, 189 | ); 190 | runOnlyForDeploymentPostprocessing = 0; 191 | }; 192 | /* End PBXSourcesBuildPhase section */ 193 | 194 | /* Begin XCBuildConfiguration section */ 195 | 645644251EB8DA9100672408 /* Debug */ = { 196 | isa = XCBuildConfiguration; 197 | buildSettings = { 198 | CLANG_ANALYZER_NONNULL = YES; 199 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 200 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 201 | CLANG_WARN_INFINITE_RECURSION = YES; 202 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 203 | DEBUG_INFORMATION_FORMAT = dwarf; 204 | ENABLE_TESTABILITY = YES; 205 | OTHER_LDFLAGS = "-ObjC"; 206 | PRODUCT_NAME = RNFS; 207 | SDKROOT = appletvos; 208 | SKIP_INSTALL = YES; 209 | TVOS_DEPLOYMENT_TARGET = 10.2; 210 | }; 211 | name = Debug; 212 | }; 213 | 645644261EB8DA9100672408 /* Release */ = { 214 | isa = XCBuildConfiguration; 215 | buildSettings = { 216 | CLANG_ANALYZER_NONNULL = YES; 217 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 218 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 219 | CLANG_WARN_INFINITE_RECURSION = YES; 220 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 221 | OTHER_LDFLAGS = "-ObjC"; 222 | PRODUCT_NAME = RNFS; 223 | SDKROOT = appletvos; 224 | SKIP_INSTALL = YES; 225 | TVOS_DEPLOYMENT_TARGET = 10.2; 226 | }; 227 | name = Release; 228 | }; 229 | F12AFBAD1ADAF8F800E0535D /* Debug */ = { 230 | isa = XCBuildConfiguration; 231 | buildSettings = { 232 | ALWAYS_SEARCH_USER_PATHS = NO; 233 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 234 | CLANG_CXX_LIBRARY = "libc++"; 235 | CLANG_ENABLE_MODULES = YES; 236 | CLANG_ENABLE_OBJC_ARC = YES; 237 | CLANG_WARN_BOOL_CONVERSION = YES; 238 | CLANG_WARN_CONSTANT_CONVERSION = YES; 239 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 240 | CLANG_WARN_EMPTY_BODY = YES; 241 | CLANG_WARN_ENUM_CONVERSION = YES; 242 | CLANG_WARN_INT_CONVERSION = YES; 243 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 244 | CLANG_WARN_UNREACHABLE_CODE = YES; 245 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 246 | COPY_PHASE_STRIP = NO; 247 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 248 | ENABLE_STRICT_OBJC_MSGSEND = YES; 249 | GCC_C_LANGUAGE_STANDARD = gnu99; 250 | GCC_DYNAMIC_NO_PIC = NO; 251 | GCC_NO_COMMON_BLOCKS = YES; 252 | GCC_OPTIMIZATION_LEVEL = 0; 253 | GCC_PREPROCESSOR_DEFINITIONS = ( 254 | "DEBUG=1", 255 | "$(inherited)", 256 | ); 257 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 258 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 259 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 260 | GCC_WARN_UNDECLARED_SELECTOR = YES; 261 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 262 | GCC_WARN_UNUSED_FUNCTION = YES; 263 | GCC_WARN_UNUSED_VARIABLE = YES; 264 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 265 | MTL_ENABLE_DEBUG_INFO = YES; 266 | ONLY_ACTIVE_ARCH = YES; 267 | SDKROOT = iphoneos; 268 | }; 269 | name = Debug; 270 | }; 271 | F12AFBAE1ADAF8F800E0535D /* Release */ = { 272 | isa = XCBuildConfiguration; 273 | buildSettings = { 274 | ALWAYS_SEARCH_USER_PATHS = NO; 275 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 276 | CLANG_CXX_LIBRARY = "libc++"; 277 | CLANG_ENABLE_MODULES = YES; 278 | CLANG_ENABLE_OBJC_ARC = YES; 279 | CLANG_WARN_BOOL_CONVERSION = YES; 280 | CLANG_WARN_CONSTANT_CONVERSION = YES; 281 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 282 | CLANG_WARN_EMPTY_BODY = YES; 283 | CLANG_WARN_ENUM_CONVERSION = YES; 284 | CLANG_WARN_INT_CONVERSION = YES; 285 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 286 | CLANG_WARN_UNREACHABLE_CODE = YES; 287 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 288 | COPY_PHASE_STRIP = NO; 289 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 290 | ENABLE_NS_ASSERTIONS = NO; 291 | ENABLE_STRICT_OBJC_MSGSEND = YES; 292 | GCC_C_LANGUAGE_STANDARD = gnu99; 293 | GCC_NO_COMMON_BLOCKS = YES; 294 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 295 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 296 | GCC_WARN_UNDECLARED_SELECTOR = YES; 297 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 298 | GCC_WARN_UNUSED_FUNCTION = YES; 299 | GCC_WARN_UNUSED_VARIABLE = YES; 300 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 301 | MTL_ENABLE_DEBUG_INFO = NO; 302 | SDKROOT = iphoneos; 303 | VALIDATE_PRODUCT = YES; 304 | }; 305 | name = Release; 306 | }; 307 | F12AFBB01ADAF8F800E0535D /* Debug */ = { 308 | isa = XCBuildConfiguration; 309 | buildSettings = { 310 | HEADER_SEARCH_PATHS = ( 311 | "$(inherited)", 312 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, 313 | "$(SRCROOT)/../../React/**", 314 | "$(SRCROOT)/../react-native/React/**", 315 | "$(SRCROOT)/node_modules/react-native/React/**", 316 | ); 317 | OTHER_LDFLAGS = "-ObjC"; 318 | PRODUCT_NAME = "$(TARGET_NAME)"; 319 | SKIP_INSTALL = YES; 320 | }; 321 | name = Debug; 322 | }; 323 | F12AFBB11ADAF8F800E0535D /* Release */ = { 324 | isa = XCBuildConfiguration; 325 | buildSettings = { 326 | HEADER_SEARCH_PATHS = ( 327 | "$(inherited)", 328 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, 329 | "$(SRCROOT)/../../React/**", 330 | "$(SRCROOT)/../react-native/React/**", 331 | "$(SRCROOT)/node_modules/react-native/React/**", 332 | ); 333 | OTHER_LDFLAGS = "-ObjC"; 334 | PRODUCT_NAME = "$(TARGET_NAME)"; 335 | SKIP_INSTALL = YES; 336 | }; 337 | name = Release; 338 | }; 339 | /* End XCBuildConfiguration section */ 340 | 341 | /* Begin XCConfigurationList section */ 342 | 645644271EB8DA9100672408 /* Build configuration list for PBXNativeTarget "RNFS-tvOS" */ = { 343 | isa = XCConfigurationList; 344 | buildConfigurations = ( 345 | 645644251EB8DA9100672408 /* Debug */, 346 | 645644261EB8DA9100672408 /* Release */, 347 | ); 348 | defaultConfigurationIsVisible = 0; 349 | }; 350 | F12AFB961ADAF8F800E0535D /* Build configuration list for PBXProject "RNFS" */ = { 351 | isa = XCConfigurationList; 352 | buildConfigurations = ( 353 | F12AFBAD1ADAF8F800E0535D /* Debug */, 354 | F12AFBAE1ADAF8F800E0535D /* Release */, 355 | ); 356 | defaultConfigurationIsVisible = 0; 357 | defaultConfigurationName = Release; 358 | }; 359 | F12AFBAF1ADAF8F800E0535D /* Build configuration list for PBXNativeTarget "RNFS" */ = { 360 | isa = XCConfigurationList; 361 | buildConfigurations = ( 362 | F12AFBB01ADAF8F800E0535D /* Debug */, 363 | F12AFBB11ADAF8F800E0535D /* Release */, 364 | ); 365 | defaultConfigurationIsVisible = 0; 366 | defaultConfigurationName = Release; 367 | }; 368 | /* End XCConfigurationList section */ 369 | }; 370 | rootObject = F12AFB931ADAF8F800E0535D /* Project object */; 371 | } 372 | -------------------------------------------------------------------------------- /RNFSManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // RNFSManager.h 3 | // RNFSManager 4 | // 5 | // Created by Johannes Lumpe on 08/05/15. 6 | // Copyright (c) 2015 Johannes Lumpe. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | typedef void (^CompletionHandler)(); 13 | 14 | @interface RNFSManager : NSObject 15 | 16 | +(void)setCompletionHandlerForIdentifier: (NSString *)identifier completionHandler: (CompletionHandler)completionHandler; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /Uploader.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | typedef void (^UploadCompleteCallback)(NSString*, NSURLResponse *); 5 | typedef void (^UploadErrorCallback)(NSError*); 6 | typedef void (^UploadBeginCallback)(void); 7 | typedef void (^UploadProgressCallback)(NSNumber*, NSNumber*); 8 | 9 | @interface RNFSUploadParams : NSObject 10 | 11 | @property (copy) NSString* toUrl; 12 | @property (copy) NSArray* files; 13 | @property (copy) NSDictionary* headers; 14 | @property (copy) NSDictionary* fields; 15 | @property (copy) NSString* method; 16 | @property (copy) UploadCompleteCallback completeCallback; // Upload has finished (data written) 17 | @property (copy) UploadErrorCallback errorCallback; // Something gone wrong 18 | @property (copy) UploadBeginCallback beginCallback; // Upload has started 19 | @property (copy) UploadProgressCallback progressCallback; // Upload is progressing 20 | 21 | @end 22 | 23 | @interface RNFSUploader : NSObject 24 | 25 | - (void)uploadFiles:(RNFSUploadParams*)params; 26 | - (void)stopUpload; 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /Uploader.m: -------------------------------------------------------------------------------- 1 | #import "Uploader.h" 2 | 3 | @implementation RNFSUploadParams 4 | 5 | @end 6 | 7 | @interface RNFSUploader() 8 | 9 | @property (copy) RNFSUploadParams* params; 10 | 11 | @property (retain) NSURLSessionDataTask* task; 12 | 13 | @end 14 | 15 | @implementation RNFSUploader 16 | 17 | - (void)uploadFiles:(RNFSUploadParams*)params 18 | { 19 | _params = params; 20 | 21 | NSString *method = _params.method; 22 | NSURL *url = [NSURL URLWithString:_params.toUrl]; 23 | NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:url]; 24 | [req setHTTPMethod:method]; 25 | 26 | // set headers 27 | NSString *formBoundaryString = [self generateBoundaryString]; 28 | NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@", formBoundaryString]; 29 | [req setValue:contentType forHTTPHeaderField:@"Content-Type"]; 30 | for (NSString *key in _params.headers) { 31 | id val = [_params.headers objectForKey:key]; 32 | if ([val respondsToSelector:@selector(stringValue)]) { 33 | val = [val stringValue]; 34 | } 35 | if (![val isKindOfClass:[NSString class]]) { 36 | continue; 37 | } 38 | [req setValue:val forHTTPHeaderField:key]; 39 | } 40 | 41 | NSData *formBoundaryData = [[NSString stringWithFormat:@"--%@\r\n", formBoundaryString] dataUsingEncoding:NSUTF8StringEncoding]; 42 | NSMutableData* reqBody = [NSMutableData data]; 43 | 44 | // add fields 45 | for (NSString *key in _params.fields) { 46 | id val = [_params.fields objectForKey:key]; 47 | if ([val respondsToSelector:@selector(stringValue)]) { 48 | val = [val stringValue]; 49 | } 50 | if (![val isKindOfClass:[NSString class]]) { 51 | continue; 52 | } 53 | 54 | [reqBody appendData:formBoundaryData]; 55 | [reqBody appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n\r\n", key] dataUsingEncoding:NSUTF8StringEncoding]]; 56 | [reqBody appendData:[val dataUsingEncoding:NSUTF8StringEncoding]]; 57 | [reqBody appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]]; 58 | } 59 | 60 | // add files 61 | for (NSDictionary *file in _params.files) { 62 | NSString *name = file[@"name"]; 63 | NSString *filename = file[@"filename"]; 64 | NSString *filepath = file[@"filepath"]; 65 | NSString *filetype = file[@"filetype"]; 66 | 67 | // Check if file exists 68 | NSFileManager *fileManager = [NSFileManager defaultManager]; 69 | if (![fileManager fileExistsAtPath:filepath]){ 70 | // NSError* error = [NSError errorWithDomain:@"Uploader" code:NSURLErrorFileDoesNotExist userInfo:@{NSLocalizedDescriptionKey: [NSString stringWithFormat: @"Failed to open target file at path: %@", filepath]}]; 71 | // return _params.errorCallback(error); 72 | NSLog(@"Failed to open target file at path: %@", filepath); 73 | continue; 74 | } 75 | 76 | NSData *fileData = [NSData dataWithContentsOfFile:filepath]; 77 | 78 | [reqBody appendData:formBoundaryData]; 79 | [reqBody appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"%@\"\r\n", name.length ? name : filename, filename] dataUsingEncoding:NSUTF8StringEncoding]]; 80 | 81 | if (filetype) { 82 | [reqBody appendData:[[NSString stringWithFormat:@"Content-Type: %@\r\n", filetype] dataUsingEncoding:NSUTF8StringEncoding]]; 83 | } else { 84 | [reqBody appendData:[[NSString stringWithFormat:@"Content-Type: %@\r\n", [self mimeTypeForPath:filename]] dataUsingEncoding:NSUTF8StringEncoding]]; 85 | } 86 | 87 | [reqBody appendData:[[NSString stringWithFormat:@"Content-Length: %ld\r\n\r\n", (long)[fileData length]] dataUsingEncoding:NSUTF8StringEncoding]]; 88 | [reqBody appendData:fileData]; 89 | [reqBody appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]]; 90 | } 91 | 92 | // add end boundary 93 | NSData* end = [[NSString stringWithFormat:@"--%@--\r\n", formBoundaryString] dataUsingEncoding:NSUTF8StringEncoding]; 94 | [reqBody appendData:end]; 95 | 96 | // send request 97 | [req setHTTPBody:reqBody]; 98 | 99 | NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration]; 100 | NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfiguration delegate:(id)self delegateQueue:[NSOperationQueue mainQueue]]; 101 | _task = [session dataTaskWithRequest:req completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { 102 | NSString * str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; 103 | return _params.completeCallback(str, response); 104 | }]; 105 | [_task resume]; 106 | _params.beginCallback(); 107 | } 108 | 109 | - (NSString *)generateBoundaryString 110 | { 111 | NSString *uuid = [[NSUUID UUID] UUIDString]; 112 | return [NSString stringWithFormat:@"----%@", uuid]; 113 | } 114 | 115 | - (NSString *)mimeTypeForPath:(NSString *)filepath 116 | { 117 | NSString *fileExtension = [filepath pathExtension]; 118 | NSString *UTI = (__bridge_transfer NSString *)UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (__bridge CFStringRef)fileExtension, NULL); 119 | NSString *contentType = (__bridge_transfer NSString *)UTTypeCopyPreferredTagWithClass((__bridge CFStringRef)UTI, kUTTagClassMIMEType); 120 | 121 | if (contentType) { 122 | return contentType; 123 | } 124 | return @"application/octet-stream"; 125 | } 126 | 127 | - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error 128 | { 129 | if(error != nil) { 130 | return _params.errorCallback(error); 131 | } 132 | } 133 | 134 | - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSendBodyData:(int64_t)bytesSent totalBytesSent:(int64_t)totalBytesSent totalBytesExpectedToSend:(NSInteger)totalBytesExpectedToSend 135 | { 136 | return _params.progressCallback([NSNumber numberWithLongLong:totalBytesExpectedToSend], [NSNumber numberWithLongLong:totalBytesSent]); 137 | } 138 | 139 | - (void)stopUpload 140 | { 141 | [_task cancel]; 142 | } 143 | 144 | @end 145 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | def safeExtGet(prop, fallback) { 2 | rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback 3 | } 4 | 5 | buildscript { 6 | repositories { 7 | jcenter() 8 | } 9 | 10 | dependencies { 11 | classpath 'com.android.tools.build:gradle:1.5.0' 12 | } 13 | } 14 | 15 | apply plugin: 'com.android.library' 16 | 17 | android { 18 | compileSdkVersion safeExtGet('compileSdkVersion', 26) 19 | buildToolsVersion safeExtGet('buildToolsVersion', '26.0.3') 20 | 21 | defaultConfig { 22 | minSdkVersion safeExtGet('minSdkVersion', 16) 23 | targetSdkVersion safeExtGet('targetSdkVersion', 26) 24 | versionCode 1 25 | versionName "1.0" 26 | } 27 | lintOptions { 28 | abortOnError false 29 | } 30 | } 31 | 32 | dependencies { 33 | implementation 'com.facebook.react:react-native:+' 34 | } 35 | -------------------------------------------------------------------------------- /android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /android/src/main/java/com/rnfs/DownloadParams.java: -------------------------------------------------------------------------------- 1 | package com.rnfs; 2 | 3 | import java.io.File; 4 | import java.net.URL; 5 | import java.util.*; 6 | 7 | import com.facebook.react.bridge.ReadableMap; 8 | 9 | public class DownloadParams { 10 | public interface OnTaskCompleted { 11 | void onTaskCompleted(DownloadResult res); 12 | } 13 | 14 | public interface OnDownloadBegin { 15 | void onDownloadBegin(int statusCode, long contentLength, Map headers); 16 | } 17 | 18 | public interface OnDownloadProgress { 19 | void onDownloadProgress(long contentLength, long bytesWritten); 20 | } 21 | 22 | public URL src; 23 | public File dest; 24 | public ReadableMap headers; 25 | public float progressDivider; 26 | public int readTimeout; 27 | public int connectionTimeout; 28 | public OnTaskCompleted onTaskCompleted; 29 | public OnDownloadBegin onDownloadBegin; 30 | public OnDownloadProgress onDownloadProgress; 31 | } -------------------------------------------------------------------------------- /android/src/main/java/com/rnfs/DownloadResult.java: -------------------------------------------------------------------------------- 1 | package com.rnfs; 2 | 3 | public class DownloadResult { 4 | public int statusCode; 5 | public long bytesWritten; 6 | public Exception exception; 7 | } 8 | -------------------------------------------------------------------------------- /android/src/main/java/com/rnfs/Downloader.java: -------------------------------------------------------------------------------- 1 | package com.rnfs; 2 | 3 | import java.io.File; 4 | import java.io.FileOutputStream; 5 | import java.io.FileInputStream; 6 | import java.io.BufferedInputStream; 7 | import java.io.InputStream; 8 | import java.io.OutputStream; 9 | import java.io.IOException; 10 | import java.net.URL; 11 | import java.net.URLConnection; 12 | import java.net.HttpURLConnection; 13 | import java.util.*; 14 | import java.util.concurrent.atomic.AtomicBoolean; 15 | 16 | import android.util.Log; 17 | 18 | import android.os.AsyncTask; 19 | 20 | import com.facebook.react.bridge.ReadableMapKeySetIterator; 21 | 22 | public class Downloader extends AsyncTask { 23 | private DownloadParams mParam; 24 | private AtomicBoolean mAbort = new AtomicBoolean(false); 25 | DownloadResult res; 26 | 27 | protected DownloadResult doInBackground(DownloadParams... params) { 28 | mParam = params[0]; 29 | res = new DownloadResult(); 30 | 31 | new Thread(new Runnable() { 32 | public void run() { 33 | try { 34 | download(mParam, res); 35 | mParam.onTaskCompleted.onTaskCompleted(res); 36 | } catch (Exception ex) { 37 | res.exception = ex; 38 | mParam.onTaskCompleted.onTaskCompleted(res); 39 | } 40 | } 41 | }).start(); 42 | 43 | return res; 44 | } 45 | 46 | private void download(DownloadParams param, DownloadResult res) throws Exception { 47 | InputStream input = null; 48 | OutputStream output = null; 49 | HttpURLConnection connection = null; 50 | 51 | try { 52 | connection = (HttpURLConnection)param.src.openConnection(); 53 | 54 | ReadableMapKeySetIterator iterator = param.headers.keySetIterator(); 55 | 56 | while (iterator.hasNextKey()) { 57 | String key = iterator.nextKey(); 58 | String value = param.headers.getString(key); 59 | connection.setRequestProperty(key, value); 60 | } 61 | 62 | connection.setConnectTimeout(param.connectionTimeout); 63 | connection.setReadTimeout(param.readTimeout); 64 | connection.connect(); 65 | 66 | int statusCode = connection.getResponseCode(); 67 | long lengthOfFile = getContentLength(connection); 68 | 69 | boolean isRedirect = ( 70 | statusCode != HttpURLConnection.HTTP_OK && 71 | ( 72 | statusCode == HttpURLConnection.HTTP_MOVED_PERM || 73 | statusCode == HttpURLConnection.HTTP_MOVED_TEMP || 74 | statusCode == 307 || 75 | statusCode == 308 76 | ) 77 | ); 78 | 79 | if (isRedirect) { 80 | String redirectURL = connection.getHeaderField("Location"); 81 | connection.disconnect(); 82 | 83 | connection = (HttpURLConnection) new URL(redirectURL).openConnection(); 84 | connection.setConnectTimeout(5000); 85 | connection.connect(); 86 | 87 | statusCode = connection.getResponseCode(); 88 | lengthOfFile = getContentLength(connection); 89 | } 90 | if(statusCode >= 200 && statusCode < 300) { 91 | Map> headers = connection.getHeaderFields(); 92 | 93 | Map headersFlat = new HashMap(); 94 | 95 | for (Map.Entry> entry : headers.entrySet()) { 96 | String headerKey = entry.getKey(); 97 | String valueKey = entry.getValue().get(0); 98 | 99 | if (headerKey != null && valueKey != null) { 100 | headersFlat.put(headerKey, valueKey); 101 | } 102 | } 103 | 104 | mParam.onDownloadBegin.onDownloadBegin(statusCode, lengthOfFile, headersFlat); 105 | 106 | input = new BufferedInputStream(connection.getInputStream(), 8 * 1024); 107 | output = new FileOutputStream(param.dest); 108 | 109 | byte data[] = new byte[8 * 1024]; 110 | long total = 0; 111 | int count; 112 | double lastProgressValue = 0; 113 | 114 | while ((count = input.read(data)) != -1) { 115 | if (mAbort.get()) throw new Exception("Download has been aborted"); 116 | 117 | total += count; 118 | if (param.progressDivider <= 0) { 119 | publishProgress(new long[]{lengthOfFile, total}); 120 | } else { 121 | double progress = Math.round(((double) total * 100) / lengthOfFile); 122 | if (progress % param.progressDivider == 0) { 123 | if ((progress != lastProgressValue) || (total == lengthOfFile)) { 124 | Log.d("Downloader", "EMIT: " + String.valueOf(progress) + ", TOTAL:" + String.valueOf(total)); 125 | lastProgressValue = progress; 126 | publishProgress(new long[]{lengthOfFile, total}); 127 | } 128 | } 129 | } 130 | output.write(data, 0, count); 131 | } 132 | 133 | output.flush(); 134 | res.bytesWritten = total; 135 | } 136 | res.statusCode = statusCode; 137 | } finally { 138 | if (output != null) output.close(); 139 | if (input != null) input.close(); 140 | if (connection != null) connection.disconnect(); 141 | } 142 | } 143 | 144 | private long getContentLength(HttpURLConnection connection){ 145 | if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) { 146 | return connection.getContentLengthLong(); 147 | } 148 | return connection.getContentLength(); 149 | } 150 | 151 | protected void stop() { 152 | mAbort.set(true); 153 | } 154 | 155 | @Override 156 | protected void onProgressUpdate(long[]... values) { 157 | super.onProgressUpdate(values); 158 | mParam.onDownloadProgress.onDownloadProgress(values[0][0], values[0][1]); 159 | } 160 | 161 | protected void onPostExecute(Exception ex) { 162 | 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /android/src/main/java/com/rnfs/IORejectionException.java: -------------------------------------------------------------------------------- 1 | package com.rnfs; 2 | 3 | class IORejectionException extends Exception { 4 | private String code; 5 | 6 | public IORejectionException(String code, String message) { 7 | super(message); 8 | this.code = code; 9 | } 10 | 11 | public String getCode() { 12 | return code; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /android/src/main/java/com/rnfs/RNFSPackage.java: -------------------------------------------------------------------------------- 1 | package com.rnfs; 2 | 3 | import java.util.*; 4 | 5 | import com.facebook.react.ReactPackage; 6 | import com.facebook.react.bridge.NativeModule; 7 | import com.facebook.react.bridge.JavaScriptModule; 8 | import com.facebook.react.bridge.ReactApplicationContext; 9 | import com.facebook.react.uimanager.ViewManager; 10 | 11 | public class RNFSPackage implements ReactPackage { 12 | 13 | @Override 14 | public List createNativeModules(ReactApplicationContext reactContext) { 15 | List modules = new ArrayList<>(); 16 | modules.add(new RNFSManager(reactContext)); 17 | return modules; 18 | } 19 | 20 | // deprecated >= RN 0.47.0 21 | public List> createJSModules() { 22 | return Collections.emptyList(); 23 | } 24 | 25 | @Override 26 | public List createViewManagers(ReactApplicationContext reactContext) { 27 | return Arrays.asList(); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /android/src/main/java/com/rnfs/UploadParams.java: -------------------------------------------------------------------------------- 1 | package com.rnfs; 2 | 3 | import com.facebook.react.bridge.ReadableMap; 4 | 5 | import java.net.URL; 6 | import java.util.ArrayList; 7 | 8 | public class UploadParams { 9 | public interface onUploadComplete{ 10 | void onUploadComplete(UploadResult res); 11 | } 12 | public interface onUploadProgress{ 13 | void onUploadProgress(int totalBytesExpectedToSend,int totalBytesSent); 14 | } 15 | public interface onUploadBegin{ 16 | void onUploadBegin(); 17 | } 18 | public URL src; 19 | public ArrayList files; 20 | public String name; 21 | public ReadableMap headers; 22 | public ReadableMap fields; 23 | public String method; 24 | public onUploadComplete onUploadComplete; 25 | public onUploadProgress onUploadProgress; 26 | public onUploadBegin onUploadBegin; 27 | } 28 | -------------------------------------------------------------------------------- /android/src/main/java/com/rnfs/UploadResult.java: -------------------------------------------------------------------------------- 1 | package com.rnfs; 2 | 3 | import com.facebook.react.bridge.WritableMap; 4 | 5 | public class UploadResult { 6 | public int statusCode; 7 | public WritableMap headers; 8 | public Exception exception; 9 | public String body; 10 | } 11 | -------------------------------------------------------------------------------- /android/src/main/java/com/rnfs/Uploader.java: -------------------------------------------------------------------------------- 1 | package com.rnfs; 2 | 3 | import android.os.AsyncTask; 4 | import android.webkit.MimeTypeMap; 5 | 6 | import com.facebook.react.bridge.Arguments; 7 | import com.facebook.react.bridge.NoSuchKeyException; 8 | import com.facebook.react.bridge.ReadableMap; 9 | import com.facebook.react.bridge.ReadableMapKeySetIterator; 10 | import com.facebook.react.bridge.WritableMap; 11 | 12 | import java.io.BufferedInputStream; 13 | import java.io.BufferedReader; 14 | import java.io.DataOutputStream; 15 | import java.io.File; 16 | import java.io.FileInputStream; 17 | import java.io.InputStreamReader; 18 | import java.net.HttpURLConnection; 19 | import java.util.List; 20 | import java.util.Map; 21 | import java.util.concurrent.atomic.AtomicBoolean; 22 | 23 | public class Uploader extends AsyncTask { 24 | private UploadParams mParams; 25 | private UploadResult res; 26 | private AtomicBoolean mAbort = new AtomicBoolean(false); 27 | 28 | @Override 29 | protected UploadResult doInBackground(UploadParams... uploadParams) { 30 | mParams = uploadParams[0]; 31 | res = new UploadResult(); 32 | new Thread(new Runnable() { 33 | @Override 34 | public void run() { 35 | try { 36 | upload(mParams, res); 37 | mParams.onUploadComplete.onUploadComplete(res); 38 | } catch (Exception e) { 39 | res.exception = e; 40 | mParams.onUploadComplete.onUploadComplete(res); 41 | } 42 | } 43 | }).start(); 44 | return res; 45 | } 46 | 47 | private void upload(UploadParams params, UploadResult result) throws Exception { 48 | HttpURLConnection connection = null; 49 | DataOutputStream request = null; 50 | String crlf = "\r\n"; 51 | String twoHyphens = "--"; 52 | String boundary = "*****"; 53 | String tail = crlf + twoHyphens + boundary + twoHyphens + crlf; 54 | String metaData = "", stringData = ""; 55 | String[] fileHeader; 56 | int statusCode, byteSentTotal; 57 | int fileCount = 0; 58 | long totalFileLength = 0; 59 | BufferedInputStream responseStream = null; 60 | BufferedReader responseStreamReader = null; 61 | String name, filename, filetype; 62 | try { 63 | connection = (HttpURLConnection) params.src.openConnection(); 64 | connection.setDoOutput(true); 65 | ReadableMapKeySetIterator headerIterator = params.headers.keySetIterator(); 66 | connection.setRequestMethod(params.method); 67 | connection.setRequestProperty("Content-Type", "multipart/form-data;boundary=" + boundary); 68 | while (headerIterator.hasNextKey()) { 69 | String key = headerIterator.nextKey(); 70 | String value = params.headers.getString(key); 71 | connection.setRequestProperty(key, value); 72 | } 73 | 74 | ReadableMapKeySetIterator fieldsIterator = params.fields.keySetIterator(); 75 | 76 | while (fieldsIterator.hasNextKey()) { 77 | String key = fieldsIterator.nextKey(); 78 | String value = params.fields.getString(key); 79 | metaData += twoHyphens + boundary + crlf + "Content-Disposition: form-data; name=\"" + key + "\"" + crlf + crlf + value +crlf; 80 | } 81 | stringData += metaData; 82 | fileHeader = new String[params.files.toArray().length]; 83 | for (ReadableMap map : params.files) { 84 | try { 85 | name = map.getString("name"); 86 | filename = map.getString("filename"); 87 | filetype = map.getString("filetype"); 88 | } catch (NoSuchKeyException e) { 89 | name = map.getString("name"); 90 | filename = map.getString("filename"); 91 | filetype = getMimeType(map.getString("filepath")); 92 | } 93 | File file = new File(map.getString("filepath")); 94 | String fileHeaderType = twoHyphens + boundary + crlf + 95 | "Content-Disposition: form-data; name=\"" + name + "\"; filename=\"" + filename + "\"" + crlf + 96 | "Content-Type: " + filetype + crlf; 97 | long fileLength = file.length(); 98 | totalFileLength += fileLength ; 99 | if(params.files.toArray().length - 1 == fileCount){ 100 | totalFileLength += tail.length(); 101 | } 102 | String fileLengthHeader = "Content-length: " + fileLength + crlf; 103 | fileHeader[fileCount] = fileHeaderType + fileLengthHeader + crlf; 104 | stringData += fileHeaderType + fileLengthHeader + crlf; 105 | fileCount++; 106 | } 107 | fileCount = 0; 108 | mParams.onUploadBegin.onUploadBegin(); 109 | long requestLength = totalFileLength + stringData.length() + params.files.toArray().length * crlf.length(); 110 | connection.setRequestProperty("Content-length", "" +(int) requestLength); 111 | connection.setFixedLengthStreamingMode((int)requestLength); 112 | connection.connect(); 113 | 114 | request = new DataOutputStream(connection.getOutputStream()); 115 | request.writeBytes(metaData); 116 | 117 | byteSentTotal = 0; 118 | Runtime run = Runtime.getRuntime(); 119 | 120 | for (ReadableMap map : params.files) { 121 | request.writeBytes(fileHeader[fileCount]); 122 | File file = new File(map.getString("filepath")); 123 | int fileLength = (int) file.length(); 124 | int bytes_read = 0; 125 | BufferedInputStream bufInput = new BufferedInputStream(new FileInputStream(file)); 126 | int buffer_size =(int) Math.ceil(fileLength / 100.f); 127 | if(buffer_size > run.freeMemory() / 10.f) { 128 | buffer_size = (int) Math.ceil(run.freeMemory() / 10.f); 129 | } 130 | byte[] buffer = new byte[buffer_size]; 131 | while ((bytes_read = bufInput.read(buffer)) != -1) { 132 | request.write(buffer, 0, bytes_read); 133 | byteSentTotal += bytes_read; 134 | mParams.onUploadProgress.onUploadProgress((int) totalFileLength - tail.length(), byteSentTotal); 135 | } 136 | request.writeBytes(crlf); 137 | fileCount++; 138 | bufInput.close(); 139 | } 140 | request.writeBytes(tail); 141 | request.flush(); 142 | request.close(); 143 | 144 | responseStream = new BufferedInputStream(connection.getInputStream()); 145 | responseStreamReader = new BufferedReader(new InputStreamReader(responseStream)); 146 | WritableMap responseHeaders = Arguments.createMap(); 147 | Map> map = connection.getHeaderFields(); 148 | for (Map.Entry> entry : map.entrySet()) { 149 | int count = 0; 150 | responseHeaders.putString(entry.getKey(), entry.getValue().get(count)); 151 | } 152 | StringBuilder stringBuilder = new StringBuilder(); 153 | String line = ""; 154 | 155 | while ((line = responseStreamReader.readLine()) != null) { 156 | stringBuilder.append(line).append("\n"); 157 | } 158 | 159 | String response = stringBuilder.toString(); 160 | statusCode = connection.getResponseCode(); 161 | res.headers = responseHeaders; 162 | res.body = response; 163 | res.statusCode = statusCode; 164 | } finally { 165 | if (connection != null) 166 | connection.disconnect(); 167 | if (request != null) 168 | request.close(); 169 | if (responseStream != null) 170 | responseStream.close(); 171 | if (responseStreamReader != null) 172 | responseStreamReader.close(); 173 | } 174 | } 175 | 176 | protected String getMimeType(String path) { 177 | String type = null; 178 | String extension = MimeTypeMap.getFileExtensionFromUrl(path); 179 | if (extension != null) { 180 | type = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension.toLowerCase()); 181 | } 182 | if (type == null) { 183 | type = "*/*"; 184 | } 185 | return type; 186 | } 187 | 188 | protected void stop() { 189 | mAbort.set(true); 190 | } 191 | 192 | } 193 | -------------------------------------------------------------------------------- /index.d.ts: -------------------------------------------------------------------------------- 1 | type MkdirOptions = { 2 | NSURLIsExcludedFromBackupKey?: boolean // iOS only 3 | NSFileProtectionKey?: string // IOS only 4 | } 5 | 6 | type FileOptions = { 7 | NSFileProtectionKey?: string // IOS only 8 | } 9 | 10 | type ReadDirItem = { 11 | ctime: Date | undefined // The creation date of the file (iOS only) 12 | mtime: Date | undefined // The last modified date of the file 13 | name: string // The name of the item 14 | path: string // The absolute path to the item 15 | size: string // Size in bytes 16 | isFile: () => boolean // Is the file just a file? 17 | isDirectory: () => boolean // Is the file a directory? 18 | } 19 | 20 | type StatResult = { 21 | name: string | undefined // The name of the item TODO: why is this not documented? 22 | path: string // The absolute path to the item 23 | size: string // Size in bytes 24 | mode: number // UNIX file mode 25 | ctime: number // Created date 26 | mtime: number // Last modified date 27 | originalFilepath: string // In case of content uri this is the pointed file path, otherwise is the same as path 28 | isFile: () => boolean // Is the file just a file? 29 | isDirectory: () => boolean // Is the file a directory? 30 | } 31 | 32 | type Headers = { [name: string]: string } 33 | type Fields = { [name: string]: string } 34 | 35 | type DownloadFileOptions = { 36 | fromUrl: string // URL to download file from 37 | toFile: string // Local filesystem path to save the file to 38 | headers?: Headers // An object of headers to be passed to the server 39 | background?: boolean // Continue the download in the background after the app terminates (iOS only) 40 | discretionary?: boolean // Allow the OS to control the timing and speed of the download to improve perceived performance (iOS only) 41 | cacheable?: boolean // Whether the download can be stored in the shared NSURLCache (iOS only) 42 | progressDivider?: number 43 | begin?: (res: DownloadBeginCallbackResult) => void 44 | progress?: (res: DownloadProgressCallbackResult) => void 45 | resumable?: () => void // only supported on iOS yet 46 | connectionTimeout?: number // only supported on Android yet 47 | readTimeout?: number // supported on Android and iOS 48 | } 49 | 50 | type DownloadBeginCallbackResult = { 51 | jobId: number // The download job ID, required if one wishes to cancel the download. See `stopDownload`. 52 | statusCode: number // The HTTP status code 53 | contentLength: number // The total size in bytes of the download resource 54 | headers: Headers // The HTTP response headers from the server 55 | } 56 | 57 | type DownloadProgressCallbackResult = { 58 | jobId: number // The download job ID, required if one wishes to cancel the download. See `stopDownload`. 59 | contentLength: number // The total size in bytes of the download resource 60 | bytesWritten: number // The number of bytes written to the file so far 61 | } 62 | 63 | type DownloadResult = { 64 | jobId: number // The download job ID, required if one wishes to cancel the download. See `stopDownload`. 65 | statusCode: number // The HTTP status code 66 | bytesWritten: number // The number of bytes written to the file 67 | } 68 | 69 | type UploadFileOptions = { 70 | toUrl: string // URL to upload file to 71 | files: UploadFileItem[] // An array of objects with the file information to be uploaded. 72 | headers?: Headers // An object of headers to be passed to the server 73 | fields?: Fields // An object of fields to be passed to the server 74 | method?: string // Default is 'POST', supports 'POST' and 'PUT' 75 | beginCallback?: (res: UploadBeginCallbackResult) => void // deprecated 76 | progressCallback?: (res: UploadProgressCallbackResult) => void // deprecated 77 | begin?: (res: UploadBeginCallbackResult) => void 78 | progress?: (res: UploadProgressCallbackResult) => void 79 | } 80 | 81 | type UploadFileItem = { 82 | name: string // Name of the file, if not defined then filename is used 83 | filename: string // Name of file 84 | filepath: string // Path to file 85 | filetype: string // The mimetype of the file to be uploaded, if not defined it will get mimetype from `filepath` extension 86 | } 87 | 88 | type UploadBeginCallbackResult = { 89 | jobId: number // The upload job ID, required if one wishes to cancel the upload. See `stopUpload`. 90 | } 91 | 92 | type UploadProgressCallbackResult = { 93 | jobId: number // The upload job ID, required if one wishes to cancel the upload. See `stopUpload`. 94 | totalBytesExpectedToSend: number // The total number of bytes that will be sent to the server 95 | totalBytesSent: number // The number of bytes sent to the server 96 | } 97 | 98 | type UploadResult = { 99 | jobId: number // The upload job ID, required if one wishes to cancel the upload. See `stopUpload`. 100 | statusCode: number // The HTTP status code 101 | headers: Headers // The HTTP response headers from the server 102 | body: string // The HTTP response body 103 | } 104 | 105 | type FSInfoResult = { 106 | totalSpace: number // The total amount of storage space on the device (in bytes). 107 | freeSpace: number // The amount of available storage space on the device (in bytes). 108 | } 109 | 110 | export function mkdir(filepath: string, options?: MkdirOptions): Promise 111 | export function moveFile( 112 | filepath: string, 113 | destPath: string, 114 | options?: FileOptions 115 | ): Promise 116 | export function copyFile( 117 | filepath: string, 118 | destPath: string, 119 | options?: FileOptions 120 | ): Promise 121 | export function pathForBundle(bundleNamed: string): Promise 122 | export function pathForGroup(groupName: string): Promise 123 | export function getFSInfo(): Promise 124 | export function getAllExternalFilesDirs(): Promise 125 | export function unlink(filepath: string): Promise 126 | export function exists(filepath: string): Promise 127 | 128 | export function stopDownload(jobId: number): void 129 | 130 | export function resumeDownload(jobId: number): void 131 | 132 | export function isResumable(jobId: number): Promise 133 | 134 | export function stopUpload(jobId: number): void 135 | 136 | export function completeHandlerIOS(jobId: number): void 137 | 138 | export function readDir(dirpath: string): Promise 139 | 140 | /** 141 | * Android-only 142 | */ 143 | export function scanFile(path: string): Promise 144 | 145 | /** 146 | * Android-only 147 | */ 148 | export function readDirAssets(dirpath: string): Promise 149 | 150 | /** 151 | * Android-only 152 | */ 153 | export function existsAssets(filepath: string): Promise 154 | 155 | /** 156 | * Android-only 157 | */ 158 | export function existsRes(filepath: string): Promise 159 | 160 | /** 161 | * Node style version (lowercase d). Returns just the names 162 | */ 163 | export function readdir(dirpath: string): Promise 164 | 165 | /** 166 | * Android-only 167 | */ 168 | export function setReadable( 169 | filepath: string, 170 | readable: boolean, 171 | ownerOnly: boolean 172 | ): Promise 173 | 174 | export function stat(filepath: string): Promise 175 | 176 | export function readFile( 177 | filepath: string, 178 | encodingOrOptions?: any 179 | ): Promise 180 | export function read( 181 | filepath: string, 182 | length?: number, 183 | position?: number, 184 | encodingOrOptions?: any 185 | ): Promise 186 | 187 | /** 188 | * Android only 189 | */ 190 | export function readFileAssets( 191 | filepath: string, 192 | encodingOrOptions?: any 193 | ): Promise 194 | 195 | /** 196 | * Android only 197 | */ 198 | export function readFileRes( 199 | filepath: string, 200 | encodingOrOptions?: any 201 | ): Promise 202 | 203 | export function hash(filepath: string, algorithm: string): Promise 204 | 205 | /** 206 | * Android only 207 | */ 208 | export function copyFileAssets( 209 | filepath: string, 210 | destPath: string 211 | ): Promise 212 | 213 | /** 214 | * Android only 215 | */ 216 | export function copyFileRes( 217 | filepath: string, 218 | destPath: string 219 | ): Promise 220 | 221 | /** 222 | * iOS only 223 | * Copies fotos from asset-library (camera-roll) to a specific location 224 | * with a given width or height 225 | * @see: https://developer.apple.com/reference/photos/phimagemanager/1616964-requestimageforasset 226 | */ 227 | export function copyAssetsFileIOS( 228 | imageUri: string, 229 | destPath: string, 230 | width: number, 231 | height: number, 232 | scale?: number, 233 | compression?: number, 234 | resizeMode?: string 235 | ): Promise 236 | 237 | /** 238 | * iOS only 239 | * Copies fotos from asset-library (camera-roll) to a specific location 240 | * with a given width or height 241 | * @see: https://developer.apple.com/reference/photos/phimagemanager/1616964-requestimageforasset 242 | */ 243 | export function copyAssetsVideoIOS( 244 | imageUri: string, 245 | destPath: string 246 | ): Promise 247 | 248 | export function writeFile( 249 | filepath: string, 250 | contents: string, 251 | encodingOrOptions?: any 252 | ): Promise 253 | 254 | export function write( 255 | filepath: string, 256 | contents: string, 257 | position?: number, 258 | encodingOrOptions?: any 259 | ): Promise 260 | 261 | export function downloadFile( 262 | options: DownloadFileOptions 263 | ): { jobId: number; promise: Promise } 264 | 265 | export function uploadFiles( 266 | options: UploadFileOptions 267 | ): { jobId: number; promise: Promise } 268 | 269 | export function touch( 270 | filepath: string, 271 | mtime?: Date, 272 | ctime?: Date 273 | ): Promise 274 | 275 | export const MainBundlePath: string 276 | export const CachesDirectoryPath: string 277 | export const ExternalCachesDirectoryPath: string 278 | export const DocumentDirectoryPath: string 279 | export const ExternalDirectoryPath: string 280 | export const ExternalStorageDirectoryPath: string 281 | export const TemporaryDirectoryPath: string 282 | export const LibraryDirectoryPath: string 283 | export const PicturesDirectoryPath: string 284 | export const FileProtectionKeys: string 285 | -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES6", 4 | "module": "commonjs" 5 | } 6 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-fs", 3 | "version": "2.13.3", 4 | "description": "Native filesystem access for react-native", 5 | "main": "FS.common.js", 6 | "typings": "index.d.ts", 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1", 9 | "flow": "flow; test $? -eq 0 -o $? -eq 2" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git@github.com:itinance/react-native-fs.git" 14 | }, 15 | "keywords": [ 16 | "react-component", 17 | "react-native", 18 | "ios", 19 | "android", 20 | "fs", 21 | "filesystem", 22 | "download", 23 | "upload", 24 | "file-transfer" 25 | ], 26 | "author": "Johannes Lumpe (https://github.com/johanneslumpe)", 27 | "license": "MIT", 28 | "dependencies": { 29 | "base-64": "^0.1.0", 30 | "utf8": "^2.1.1" 31 | }, 32 | "devDependencies": { 33 | "flow-bin": "0.28.0", 34 | "react": "^16.2.0", 35 | "react-native": "^0.57.0", 36 | "prop-types": "15.6.0", 37 | "create-react-class": "^15.6.2", 38 | "react-native-windows": "^0.57.0" 39 | }, 40 | "peerDependencies": { 41 | "react": "^16.2.0", 42 | "react-native": ">=0.51.0 <1.0.0", 43 | "prop-types": "^15.6.0" 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /windows/.gitignore: -------------------------------------------------------------------------------- 1 | *AppPackages* 2 | *BundleArtifacts* 3 | *ReactAssets* 4 | 5 | #OS junk files 6 | [Tt]humbs.db 7 | *.DS_Store 8 | 9 | #Visual Studio files 10 | *.[Oo]bj 11 | *.user 12 | *.aps 13 | *.pch 14 | *.vspscc 15 | *.vssscc 16 | *_i.c 17 | *_p.c 18 | *.ncb 19 | *.suo 20 | *.tlb 21 | *.tlh 22 | *.bak 23 | *.[Cc]ache 24 | *.ilk 25 | *.log 26 | *.lib 27 | *.sbr 28 | *.sdf 29 | *.opensdf 30 | *.opendb 31 | *.unsuccessfulbuild 32 | ipch/ 33 | [Oo]bj/ 34 | [Bb]in 35 | [Dd]ebug*/ 36 | [Rr]elease*/ 37 | Ankh.NoLoad 38 | 39 | #MonoDevelop 40 | *.pidb 41 | *.userprefs 42 | 43 | #Tooling 44 | _ReSharper*/ 45 | *.resharper 46 | [Tt]est[Rr]esult* 47 | *.sass-cache 48 | 49 | #Project files 50 | [Bb]uild/ 51 | 52 | #Subversion files 53 | .svn 54 | 55 | # Office Temp Files 56 | ~$* 57 | 58 | # vim Temp Files 59 | *~ 60 | 61 | #NuGet 62 | packages/ 63 | *.nupkg 64 | 65 | #ncrunch 66 | *ncrunch* 67 | *crunch*.local.xml 68 | 69 | # visual studio database projects 70 | *.dbmdl 71 | 72 | #Test files 73 | *.testsettings 74 | 75 | #Other files 76 | *.DotSettings 77 | .vs/ 78 | *project.lock.json 79 | -------------------------------------------------------------------------------- /windows/.npmignore: -------------------------------------------------------------------------------- 1 | 2 | # Make sure we don't publish build artifacts to NPM 3 | ARM/ 4 | Debug/ 5 | x64/ 6 | x86/ 7 | bin/ 8 | obj/ 9 | .vs/ 10 | 11 | # Don't publish tests to NPM 12 | RNFS.Tests/ 13 | -------------------------------------------------------------------------------- /windows/RNFS.Tests/Assets/LockScreenLogo.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naxel/react-native-fs/53ac0a75198c942564184f5675d5fcb9635c5751/windows/RNFS.Tests/Assets/LockScreenLogo.scale-200.png -------------------------------------------------------------------------------- /windows/RNFS.Tests/Assets/SplashScreen.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naxel/react-native-fs/53ac0a75198c942564184f5675d5fcb9635c5751/windows/RNFS.Tests/Assets/SplashScreen.scale-200.png -------------------------------------------------------------------------------- /windows/RNFS.Tests/Assets/Square150x150Logo.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naxel/react-native-fs/53ac0a75198c942564184f5675d5fcb9635c5751/windows/RNFS.Tests/Assets/Square150x150Logo.scale-200.png -------------------------------------------------------------------------------- /windows/RNFS.Tests/Assets/Square44x44Logo.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naxel/react-native-fs/53ac0a75198c942564184f5675d5fcb9635c5751/windows/RNFS.Tests/Assets/Square44x44Logo.scale-200.png -------------------------------------------------------------------------------- /windows/RNFS.Tests/Assets/Square44x44Logo.targetsize-24_altform-unplated.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naxel/react-native-fs/53ac0a75198c942564184f5675d5fcb9635c5751/windows/RNFS.Tests/Assets/Square44x44Logo.targetsize-24_altform-unplated.png -------------------------------------------------------------------------------- /windows/RNFS.Tests/Assets/StoreLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naxel/react-native-fs/53ac0a75198c942564184f5675d5fcb9635c5751/windows/RNFS.Tests/Assets/StoreLogo.png -------------------------------------------------------------------------------- /windows/RNFS.Tests/Assets/Wide310x150Logo.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naxel/react-native-fs/53ac0a75198c942564184f5675d5fcb9635c5751/windows/RNFS.Tests/Assets/Wide310x150Logo.scale-200.png -------------------------------------------------------------------------------- /windows/RNFS.Tests/Package.appxmanifest: -------------------------------------------------------------------------------- 1 |  2 | 7 | 8 | 11 | 12 | 13 | 14 | 15 | RNFS.Tests 16 | ericroz 17 | Assets\StoreLogo.png 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 31 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /windows/RNFS.Tests/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | [assembly: AssemblyTitle("RNFS.Tests")] 6 | [assembly: AssemblyDescription("")] 7 | [assembly: AssemblyConfiguration("")] 8 | [assembly: AssemblyCompany("")] 9 | [assembly: AssemblyProduct("RNFS.Tests")] 10 | [assembly: AssemblyCopyright("Copyright © 2017")] 11 | [assembly: AssemblyTrademark("")] 12 | [assembly: AssemblyCulture("")] 13 | [assembly: AssemblyMetadata("TargetPlatform","UAP")] 14 | 15 | // [assembly: AssemblyVersion("1.0.*")] 16 | [assembly: AssemblyVersion("1.0.0.0")] 17 | [assembly: AssemblyFileVersion("1.0.0.0")] 18 | [assembly: ComVisible(false)] -------------------------------------------------------------------------------- /windows/RNFS.Tests/Properties/UnitTestApp.rd.xml: -------------------------------------------------------------------------------- 1 | 17 | 18 | 19 | 20 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /windows/RNFS.Tests/RNFS.Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | x86 7 | {8D2229AC-F6EC-4FBD-9AAC-FE4792DA98C6} 8 | AppContainerExe 9 | Properties 10 | RNFS.Tests 11 | RNFS.Tests 12 | en-US 13 | UAP 14 | 10.0.14393.0 15 | 10.0.14393.0 16 | 14 17 | 512 18 | {A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 19 | RNFS.Tests_TemporaryKey.pfx 20 | $(VisualStudioVersion) 21 | PackageReference 22 | 23 | 24 | true 25 | bin\x86\Debug\ 26 | DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP 27 | ;2008 28 | full 29 | x86 30 | false 31 | prompt 32 | true 33 | 34 | 35 | bin\x86\Release\ 36 | TRACE;NETFX_CORE;WINDOWS_UWP 37 | true 38 | ;2008 39 | pdbonly 40 | x86 41 | false 42 | prompt 43 | true 44 | true 45 | 46 | 47 | true 48 | bin\ARM\Debug\ 49 | DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP 50 | ;2008 51 | full 52 | ARM 53 | false 54 | prompt 55 | true 56 | 57 | 58 | bin\ARM\Release\ 59 | TRACE;NETFX_CORE;WINDOWS_UWP 60 | true 61 | ;2008 62 | pdbonly 63 | ARM 64 | false 65 | prompt 66 | true 67 | true 68 | 69 | 70 | true 71 | bin\x64\Debug\ 72 | DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP 73 | ;2008 74 | full 75 | x64 76 | false 77 | prompt 78 | true 79 | 80 | 81 | bin\x64\Release\ 82 | TRACE;NETFX_CORE;WINDOWS_UWP 83 | true 84 | ;2008 85 | pdbonly 86 | x64 87 | false 88 | prompt 89 | true 90 | true 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | UnitTestApp.xaml 99 | 100 | 101 | 102 | 103 | 104 | MSBuild:Compile 105 | Designer 106 | 107 | 108 | 109 | 110 | Designer 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | {c7673ad5-e3aa-468c-a5fd-fa38154e205c} 127 | ReactNative 128 | 129 | 130 | {746610d0-8693-11e7-a20d-bf83f7366778} 131 | RNFS 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 6.0.6 140 | 141 | 142 | 1.3.2 143 | 144 | 145 | 1.3.2 146 | 147 | 148 | 149 | 14.0 150 | 151 | 152 | 159 | -------------------------------------------------------------------------------- /windows/RNFS.Tests/RNFS.Tests.nuget.props: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | True 5 | NuGet 6 | C:\src\react-native-fs\windows\RNFS.Tests\project.lock.json 7 | $(UserProfile)\.nuget\packages\ 8 | C:\Users\ericroz\.nuget\packages\ 9 | ProjectJson 10 | 4.3.0 11 | 12 | 13 | $(MSBuildAllProjects);$(MSBuildThisFileFullPath) 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /windows/RNFS.Tests/RNFS.Tests.nuget.targets: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | $(MSBuildAllProjects);$(MSBuildThisFileFullPath) 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /windows/RNFS.Tests/RNFS.Tests_TemporaryKey.pfx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naxel/react-native-fs/53ac0a75198c942564184f5675d5fcb9635c5751/windows/RNFS.Tests/RNFS.Tests_TemporaryKey.pfx -------------------------------------------------------------------------------- /windows/RNFS.Tests/UnitTestApp.xaml: -------------------------------------------------------------------------------- 1 |  7 | 8 | 9 | -------------------------------------------------------------------------------- /windows/RNFS.Tests/UnitTestApp.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Runtime.InteropServices.WindowsRuntime; 6 | using Windows.ApplicationModel; 7 | using Windows.ApplicationModel.Activation; 8 | using Windows.Foundation; 9 | using Windows.Foundation.Collections; 10 | using Windows.UI.Xaml; 11 | using Windows.UI.Xaml.Controls; 12 | using Windows.UI.Xaml.Controls.Primitives; 13 | using Windows.UI.Xaml.Data; 14 | using Windows.UI.Xaml.Input; 15 | using Windows.UI.Xaml.Media; 16 | using Windows.UI.Xaml.Navigation; 17 | 18 | namespace RNFS.Tests 19 | { 20 | /// 21 | /// Provides application-specific behavior to supplement the default Application class. 22 | /// 23 | sealed partial class App : Application 24 | { 25 | /// 26 | /// Initializes the singleton application object. This is the first line of authored code 27 | /// executed, and as such is the logical equivalent of main() or WinMain(). 28 | /// 29 | public App() 30 | { 31 | this.InitializeComponent(); 32 | this.Suspending += OnSuspending; 33 | } 34 | 35 | /// 36 | /// Invoked when the application is launched normally by the end user. Other entry points 37 | /// will be used such as when the application is launched to open a specific file. 38 | /// 39 | /// Details about the launch request and process. 40 | protected override void OnLaunched(LaunchActivatedEventArgs e) 41 | { 42 | 43 | #if DEBUG 44 | if (System.Diagnostics.Debugger.IsAttached) 45 | { 46 | this.DebugSettings.EnableFrameRateCounter = true; 47 | } 48 | #endif 49 | 50 | Frame rootFrame = Window.Current.Content as Frame; 51 | 52 | // Do not repeat app initialization when the Window already has content, 53 | // just ensure that the window is active 54 | if (rootFrame == null) 55 | { 56 | // Create a Frame to act as the navigation context and navigate to the first page 57 | rootFrame = new Frame(); 58 | 59 | rootFrame.NavigationFailed += OnNavigationFailed; 60 | 61 | if (e.PreviousExecutionState == ApplicationExecutionState.Terminated) 62 | { 63 | //TODO: Load state from previously suspended application 64 | } 65 | 66 | // Place the frame in the current Window 67 | Window.Current.Content = rootFrame; 68 | } 69 | 70 | Microsoft.VisualStudio.TestPlatform.TestExecutor.UnitTestClient.CreateDefaultUI(); 71 | 72 | // Ensure the current window is active 73 | Window.Current.Activate(); 74 | 75 | Microsoft.VisualStudio.TestPlatform.TestExecutor.UnitTestClient.Run(e.Arguments); 76 | } 77 | 78 | /// 79 | /// Invoked when Navigation to a certain page fails 80 | /// 81 | /// The Frame which failed navigation 82 | /// Details about the navigation failure 83 | void OnNavigationFailed(object sender, NavigationFailedEventArgs e) 84 | { 85 | throw new Exception("Failed to load Page " + e.SourcePageType.FullName); 86 | } 87 | 88 | /// 89 | /// Invoked when application execution is being suspended. Application state is saved 90 | /// without knowing whether the application will be terminated or resumed with the contents 91 | /// of memory still intact. 92 | /// 93 | /// The source of the suspend request. 94 | /// Details about the suspend request. 95 | private void OnSuspending(object sender, SuspendingEventArgs e) 96 | { 97 | var deferral = e.SuspendingOperation.GetDeferral(); 98 | //TODO: Save application state and stop any background activity 99 | deferral.Complete(); 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /windows/RNFS.sln: -------------------------------------------------------------------------------- 1 | Microsoft Visual Studio Solution File, Format Version 12.00 2 | # Visual Studio 15 3 | VisualStudioVersion = 15.0.26730.3 4 | MinimumVisualStudioVersion = 10.0.40219.1 5 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RNFS", "RNFs\RNFS.csproj", "{746610D0-8693-11E7-A20D-BF83F7366778}" 6 | EndProject 7 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ReactNative", "..\node_modules\react-native-windows\ReactWindows\ReactNative\ReactNative.csproj", "{C7673AD5-E3AA-468C-A5FD-FA38154E205C}" 8 | EndProject 9 | Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "ReactNative.Shared", "..\node_modules\react-native-windows\ReactWindows\ReactNative.Shared\ReactNative.Shared.shproj", "{EEA8B852-4D07-48E1-8294-A21AB5909FE5}" 10 | EndProject 11 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ChakraBridge", "..\node_modules\react-native-windows\ReactWindows\ChakraBridge\ChakraBridge.vcxproj", "{4B72C796-16D5-4E3A-81C0-3E36F531E578}" 12 | EndProject 13 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RNFS.Tests", "RNFS.Tests\RNFS.Tests.csproj", "{8D2229AC-F6EC-4FBD-9AAC-FE4792DA98C6}" 14 | EndProject 15 | Global 16 | GlobalSection(SharedMSBuildProjectFiles) = preSolution 17 | ..\node_modules\react-native-windows\ReactWindows\ReactNative.Shared\ReactNative.Shared.projitems*{c7673ad5-e3aa-468c-a5fd-fa38154e205c}*SharedItemsImports = 4 18 | ..\node_modules\react-native-windows\Yoga\csharp\Facebook.Yoga\Facebook.Yoga.Shared.projitems*{c7673ad5-e3aa-468c-a5fd-fa38154e205c}*SharedItemsImports = 4 19 | ..\node_modules\react-native-windows\ReactWindows\ReactNative.Shared\ReactNative.Shared.projitems*{eea8b852-4d07-48e1-8294-a21ab5909fe5}*SharedItemsImports = 13 20 | EndGlobalSection 21 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 22 | Debug|ARM = Debug|ARM 23 | Debug|x64 = Debug|x64 24 | Debug|x86 = Debug|x86 25 | Development|ARM = Development|ARM 26 | Development|x64 = Development|x64 27 | Development|x86 = Development|x86 28 | Release|ARM = Release|ARM 29 | Release|x64 = Release|x64 30 | Release|x86 = Release|x86 31 | EndGlobalSection 32 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 33 | {746610D0-8693-11E7-A20D-BF83F7366778}.Debug|ARM.ActiveCfg = Debug|ARM 34 | {746610D0-8693-11E7-A20D-BF83F7366778}.Debug|ARM.Build.0 = Debug|ARM 35 | {746610D0-8693-11E7-A20D-BF83F7366778}.Debug|x64.ActiveCfg = Debug|x64 36 | {746610D0-8693-11E7-A20D-BF83F7366778}.Debug|x64.Build.0 = Debug|x64 37 | {746610D0-8693-11E7-A20D-BF83F7366778}.Debug|x86.ActiveCfg = Debug|x86 38 | {746610D0-8693-11E7-A20D-BF83F7366778}.Debug|x86.Build.0 = Debug|x86 39 | {746610D0-8693-11E7-A20D-BF83F7366778}.Development|ARM.ActiveCfg = Development|ARM 40 | {746610D0-8693-11E7-A20D-BF83F7366778}.Development|ARM.Build.0 = Development|ARM 41 | {746610D0-8693-11E7-A20D-BF83F7366778}.Development|x64.ActiveCfg = Development|x64 42 | {746610D0-8693-11E7-A20D-BF83F7366778}.Development|x64.Build.0 = Development|x64 43 | {746610D0-8693-11E7-A20D-BF83F7366778}.Development|x86.ActiveCfg = Development|x86 44 | {746610D0-8693-11E7-A20D-BF83F7366778}.Development|x86.Build.0 = Development|x86 45 | {746610D0-8693-11E7-A20D-BF83F7366778}.Release|ARM.ActiveCfg = Release|ARM 46 | {746610D0-8693-11E7-A20D-BF83F7366778}.Release|ARM.Build.0 = Release|ARM 47 | {746610D0-8693-11E7-A20D-BF83F7366778}.Release|x64.ActiveCfg = Release|x64 48 | {746610D0-8693-11E7-A20D-BF83F7366778}.Release|x64.Build.0 = Release|x64 49 | {746610D0-8693-11E7-A20D-BF83F7366778}.Release|x86.ActiveCfg = Release|x86 50 | {746610D0-8693-11E7-A20D-BF83F7366778}.Release|x86.Build.0 = Release|x86 51 | {C7673AD5-E3AA-468C-A5FD-FA38154E205C}.Debug|ARM.ActiveCfg = Debug|ARM 52 | {C7673AD5-E3AA-468C-A5FD-FA38154E205C}.Debug|ARM.Build.0 = Debug|ARM 53 | {C7673AD5-E3AA-468C-A5FD-FA38154E205C}.Debug|x64.ActiveCfg = Debug|x64 54 | {C7673AD5-E3AA-468C-A5FD-FA38154E205C}.Debug|x64.Build.0 = Debug|x64 55 | {C7673AD5-E3AA-468C-A5FD-FA38154E205C}.Debug|x86.ActiveCfg = Debug|x86 56 | {C7673AD5-E3AA-468C-A5FD-FA38154E205C}.Debug|x86.Build.0 = Debug|x86 57 | {C7673AD5-E3AA-468C-A5FD-FA38154E205C}.Development|ARM.ActiveCfg = Debug|ARM 58 | {C7673AD5-E3AA-468C-A5FD-FA38154E205C}.Development|ARM.Build.0 = Debug|ARM 59 | {C7673AD5-E3AA-468C-A5FD-FA38154E205C}.Development|x64.ActiveCfg = Debug|x64 60 | {C7673AD5-E3AA-468C-A5FD-FA38154E205C}.Development|x64.Build.0 = Debug|x64 61 | {C7673AD5-E3AA-468C-A5FD-FA38154E205C}.Development|x86.ActiveCfg = Debug|x86 62 | {C7673AD5-E3AA-468C-A5FD-FA38154E205C}.Development|x86.Build.0 = Debug|x86 63 | {C7673AD5-E3AA-468C-A5FD-FA38154E205C}.Release|ARM.ActiveCfg = Release|ARM 64 | {C7673AD5-E3AA-468C-A5FD-FA38154E205C}.Release|ARM.Build.0 = Release|ARM 65 | {C7673AD5-E3AA-468C-A5FD-FA38154E205C}.Release|x64.ActiveCfg = Release|x64 66 | {C7673AD5-E3AA-468C-A5FD-FA38154E205C}.Release|x64.Build.0 = Release|x64 67 | {C7673AD5-E3AA-468C-A5FD-FA38154E205C}.Release|x86.ActiveCfg = Release|x86 68 | {C7673AD5-E3AA-468C-A5FD-FA38154E205C}.Release|x86.Build.0 = Release|x86 69 | {4B72C796-16D5-4E3A-81C0-3E36F531E578}.Debug|ARM.ActiveCfg = Debug|ARM 70 | {4B72C796-16D5-4E3A-81C0-3E36F531E578}.Debug|ARM.Build.0 = Debug|ARM 71 | {4B72C796-16D5-4E3A-81C0-3E36F531E578}.Debug|x64.ActiveCfg = Debug|x64 72 | {4B72C796-16D5-4E3A-81C0-3E36F531E578}.Debug|x64.Build.0 = Debug|x64 73 | {4B72C796-16D5-4E3A-81C0-3E36F531E578}.Debug|x86.ActiveCfg = Debug|Win32 74 | {4B72C796-16D5-4E3A-81C0-3E36F531E578}.Debug|x86.Build.0 = Debug|Win32 75 | {4B72C796-16D5-4E3A-81C0-3E36F531E578}.Development|ARM.ActiveCfg = Debug|ARM 76 | {4B72C796-16D5-4E3A-81C0-3E36F531E578}.Development|ARM.Build.0 = Debug|ARM 77 | {4B72C796-16D5-4E3A-81C0-3E36F531E578}.Development|x64.ActiveCfg = Debug|x64 78 | {4B72C796-16D5-4E3A-81C0-3E36F531E578}.Development|x64.Build.0 = Debug|x64 79 | {4B72C796-16D5-4E3A-81C0-3E36F531E578}.Development|x86.ActiveCfg = Debug|Win32 80 | {4B72C796-16D5-4E3A-81C0-3E36F531E578}.Development|x86.Build.0 = Debug|Win32 81 | {4B72C796-16D5-4E3A-81C0-3E36F531E578}.Release|ARM.ActiveCfg = Release|ARM 82 | {4B72C796-16D5-4E3A-81C0-3E36F531E578}.Release|ARM.Build.0 = Release|ARM 83 | {4B72C796-16D5-4E3A-81C0-3E36F531E578}.Release|x64.ActiveCfg = Release|x64 84 | {4B72C796-16D5-4E3A-81C0-3E36F531E578}.Release|x64.Build.0 = Release|x64 85 | {4B72C796-16D5-4E3A-81C0-3E36F531E578}.Release|x86.ActiveCfg = Release|Win32 86 | {4B72C796-16D5-4E3A-81C0-3E36F531E578}.Release|x86.Build.0 = Release|Win32 87 | {8D2229AC-F6EC-4FBD-9AAC-FE4792DA98C6}.Debug|ARM.ActiveCfg = Debug|ARM 88 | {8D2229AC-F6EC-4FBD-9AAC-FE4792DA98C6}.Debug|ARM.Build.0 = Debug|ARM 89 | {8D2229AC-F6EC-4FBD-9AAC-FE4792DA98C6}.Debug|ARM.Deploy.0 = Debug|ARM 90 | {8D2229AC-F6EC-4FBD-9AAC-FE4792DA98C6}.Debug|x64.ActiveCfg = Debug|x64 91 | {8D2229AC-F6EC-4FBD-9AAC-FE4792DA98C6}.Debug|x64.Build.0 = Debug|x64 92 | {8D2229AC-F6EC-4FBD-9AAC-FE4792DA98C6}.Debug|x64.Deploy.0 = Debug|x64 93 | {8D2229AC-F6EC-4FBD-9AAC-FE4792DA98C6}.Debug|x86.ActiveCfg = Debug|x86 94 | {8D2229AC-F6EC-4FBD-9AAC-FE4792DA98C6}.Debug|x86.Build.0 = Debug|x86 95 | {8D2229AC-F6EC-4FBD-9AAC-FE4792DA98C6}.Debug|x86.Deploy.0 = Debug|x86 96 | {8D2229AC-F6EC-4FBD-9AAC-FE4792DA98C6}.Development|ARM.ActiveCfg = Debug|ARM 97 | {8D2229AC-F6EC-4FBD-9AAC-FE4792DA98C6}.Development|ARM.Build.0 = Debug|ARM 98 | {8D2229AC-F6EC-4FBD-9AAC-FE4792DA98C6}.Development|ARM.Deploy.0 = Debug|ARM 99 | {8D2229AC-F6EC-4FBD-9AAC-FE4792DA98C6}.Development|x64.ActiveCfg = Debug|x64 100 | {8D2229AC-F6EC-4FBD-9AAC-FE4792DA98C6}.Development|x64.Build.0 = Debug|x64 101 | {8D2229AC-F6EC-4FBD-9AAC-FE4792DA98C6}.Development|x64.Deploy.0 = Debug|x64 102 | {8D2229AC-F6EC-4FBD-9AAC-FE4792DA98C6}.Development|x86.ActiveCfg = Debug|x86 103 | {8D2229AC-F6EC-4FBD-9AAC-FE4792DA98C6}.Development|x86.Build.0 = Debug|x86 104 | {8D2229AC-F6EC-4FBD-9AAC-FE4792DA98C6}.Development|x86.Deploy.0 = Debug|x86 105 | {8D2229AC-F6EC-4FBD-9AAC-FE4792DA98C6}.Release|ARM.ActiveCfg = Release|ARM 106 | {8D2229AC-F6EC-4FBD-9AAC-FE4792DA98C6}.Release|ARM.Build.0 = Release|ARM 107 | {8D2229AC-F6EC-4FBD-9AAC-FE4792DA98C6}.Release|ARM.Deploy.0 = Release|ARM 108 | {8D2229AC-F6EC-4FBD-9AAC-FE4792DA98C6}.Release|x64.ActiveCfg = Release|x64 109 | {8D2229AC-F6EC-4FBD-9AAC-FE4792DA98C6}.Release|x64.Build.0 = Release|x64 110 | {8D2229AC-F6EC-4FBD-9AAC-FE4792DA98C6}.Release|x64.Deploy.0 = Release|x64 111 | {8D2229AC-F6EC-4FBD-9AAC-FE4792DA98C6}.Release|x86.ActiveCfg = Release|x86 112 | {8D2229AC-F6EC-4FBD-9AAC-FE4792DA98C6}.Release|x86.Build.0 = Release|x86 113 | {8D2229AC-F6EC-4FBD-9AAC-FE4792DA98C6}.Release|x86.Deploy.0 = Release|x86 114 | EndGlobalSection 115 | GlobalSection(SolutionProperties) = preSolution 116 | HideSolutionNode = FALSE 117 | EndGlobalSection 118 | GlobalSection(ExtensibilityGlobals) = postSolution 119 | SolutionGuid = {FBB6610F-6707-4A34-9D3B-3E0709F7822A} 120 | EndGlobalSection 121 | EndGlobal 122 | -------------------------------------------------------------------------------- /windows/RNFS/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("RNFS")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("RNFS")] 13 | [assembly: AssemblyCopyright("Copyright © 2016")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Version information for an assembly consists of the following four values: 18 | // 19 | // Major Version 20 | // Minor Version 21 | // Build Number 22 | // Revision 23 | // 24 | // You can specify all the values or you can default the Build and Revision Numbers 25 | // by using the '*' as shown below: 26 | // [assembly: AssemblyVersion("1.0.*")] 27 | [assembly: AssemblyVersion("1.0.0.0")] 28 | [assembly: AssemblyFileVersion("1.0.0.0")] 29 | [assembly: ComVisible(false)] 30 | 31 | [assembly: InternalsVisibleTo("RNFS.Tests")] -------------------------------------------------------------------------------- /windows/RNFS/Properties/RNFS.rd.xml: -------------------------------------------------------------------------------- 1 | 2 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /windows/RNFS/RNFS.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | x86 7 | {746610D0-8693-11E7-A20D-BF83F7366778} 8 | Library 9 | Properties 10 | RNFS 11 | RNFS 12 | en-US 13 | UAP 14 | 10.0.14393.0 15 | 10.0.14393.0 16 | 14 17 | 512 18 | {A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 19 | ..\..\node_modules 20 | PackageReference 21 | 22 | 23 | ..\.. 24 | 25 | 26 | x86 27 | true 28 | bin\x86\Debug\ 29 | DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP 30 | ;2008 31 | full 32 | x86 33 | false 34 | prompt 35 | 36 | 37 | x86 38 | bin\x86\Release\ 39 | TRACE;NETFX_CORE;WINDOWS_UWP 40 | true 41 | ;2008 42 | pdbonly 43 | x86 44 | false 45 | prompt 46 | 47 | 48 | ARM 49 | true 50 | bin\ARM\Debug\ 51 | DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP 52 | ;2008 53 | full 54 | ARM 55 | false 56 | prompt 57 | 58 | 59 | ARM 60 | bin\ARM\Release\ 61 | TRACE;NETFX_CORE;WINDOWS_UWP 62 | true 63 | ;2008 64 | pdbonly 65 | ARM 66 | false 67 | prompt 68 | 69 | 70 | x64 71 | true 72 | bin\x64\Debug\ 73 | DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP 74 | ;2008 75 | full 76 | x64 77 | false 78 | prompt 79 | 80 | 81 | x64 82 | bin\x64\Release\ 83 | TRACE;NETFX_CORE;WINDOWS_UWP 84 | true 85 | ;2008 86 | pdbonly 87 | x64 88 | false 89 | prompt 90 | 91 | 92 | true 93 | bin\x86\Development\ 94 | DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP 95 | ;2008 96 | true 97 | full 98 | x86 99 | false 100 | prompt 101 | MinimumRecommendedRules.ruleset 102 | 103 | 104 | true 105 | bin\ARM\Development\ 106 | DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP 107 | ;2008 108 | true 109 | full 110 | ARM 111 | false 112 | prompt 113 | MinimumRecommendedRules.ruleset 114 | 115 | 116 | true 117 | bin\x64\Development\ 118 | DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP 119 | ;2008 120 | true 121 | full 122 | x64 123 | false 124 | prompt 125 | MinimumRecommendedRules.ruleset 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | {c7673ad5-e3aa-468c-a5fd-fa38154e205c} 136 | ReactNative 137 | 138 | 139 | 140 | 141 | 6.0.6 142 | 143 | 144 | 145 | 14.0 146 | 147 | 148 | 155 | -------------------------------------------------------------------------------- /windows/RNFS/RNFSManager.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json.Linq; 2 | using ReactNative.Bridge; 3 | using ReactNative.Modules.Core; 4 | using ReactNative.Modules.Network; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.IO; 8 | using System.Linq; 9 | using System.Net.Http; 10 | using System.Security.Cryptography; 11 | using System.Text; 12 | using System.Threading; 13 | using System.Threading.Tasks; 14 | using Windows.ApplicationModel; 15 | using Windows.Storage; 16 | 17 | namespace RNFS 18 | { 19 | class RNFSManager : ReactContextNativeModuleBase 20 | { 21 | private const int FileType = 0; 22 | private const int DirectoryType = 1; 23 | 24 | private static readonly IReadOnlyDictionary> s_hashAlgorithms = 25 | new Dictionary> 26 | { 27 | { "md5", () => MD5.Create() }, 28 | { "sha1", () => SHA1.Create() }, 29 | { "sha256", () => SHA256.Create() }, 30 | { "sha384", () => SHA384.Create() }, 31 | { "sha512", () => SHA512.Create() }, 32 | }; 33 | 34 | private readonly TaskCancellationManager _tasks = new TaskCancellationManager(); 35 | private readonly HttpClient _httpClient = new HttpClient(); 36 | 37 | private RCTNativeAppEventEmitter _emitter; 38 | 39 | public RNFSManager(ReactContext reactContext) 40 | : base(reactContext) 41 | { 42 | } 43 | 44 | public override string Name 45 | { 46 | get 47 | { 48 | return "RNFSManager"; 49 | } 50 | } 51 | 52 | internal RCTNativeAppEventEmitter Emitter 53 | { 54 | get 55 | { 56 | if (_emitter == null) 57 | { 58 | return Context.GetJavaScriptModule(); 59 | } 60 | 61 | return _emitter; 62 | } 63 | set 64 | { 65 | _emitter = value; 66 | } 67 | } 68 | 69 | [Obsolete] 70 | public override IReadOnlyDictionary Constants 71 | { 72 | get 73 | { 74 | var constants = new Dictionary 75 | { 76 | { "RNFSMainBundlePath", Package.Current.InstalledLocation.Path }, 77 | { "RNFSCachesDirectoryPath", ApplicationData.Current.LocalCacheFolder.Path }, 78 | { "RNFSRoamingDirectoryPath", ApplicationData.Current.RoamingFolder.Path }, 79 | { "RNFSDocumentDirectoryPath", ApplicationData.Current.LocalFolder.Path }, 80 | { "RNFSTemporaryDirectoryPath", ApplicationData.Current.TemporaryFolder.Path }, 81 | { "RNFSFileTypeRegular", 0 }, 82 | { "RNFSFileTypeDirectory", 1 }, 83 | }; 84 | 85 | var external = GetFolderPathSafe(() => KnownFolders.RemovableDevices); 86 | if (external != null) 87 | { 88 | var externalItems = KnownFolders.RemovableDevices.GetItemsAsync().AsTask().Result; 89 | if (externalItems.Count > 0) 90 | { 91 | constants.Add("RNFSExternalDirectoryPath", externalItems[0].Path); 92 | } 93 | constants.Add("RNFSExternalDirectoryPaths", externalItems.Select(i => i.Path).ToArray()); 94 | } 95 | 96 | var pictures = GetFolderPathSafe(() => KnownFolders.PicturesLibrary); 97 | if (pictures != null) 98 | { 99 | constants.Add("RNFSPicturesDirectoryPath", pictures); 100 | } 101 | 102 | return constants; 103 | } 104 | } 105 | 106 | [ReactMethod] 107 | public async void writeFile(string filepath, string base64Content, JObject options, IPromise promise) 108 | { 109 | try 110 | { 111 | // TODO: open file on background thread? 112 | using (var file = File.OpenWrite(filepath)) 113 | { 114 | var data = Convert.FromBase64String(base64Content); 115 | await file.WriteAsync(data, 0, data.Length).ConfigureAwait(false); 116 | } 117 | 118 | promise.Resolve(null); 119 | } 120 | catch (Exception ex) 121 | { 122 | Reject(promise, filepath, ex); 123 | } 124 | } 125 | 126 | [ReactMethod] 127 | public async void appendFile(string filepath, string base64Content, IPromise promise) 128 | { 129 | try 130 | { 131 | // TODO: open file on background thread? 132 | using (var file = File.Open(filepath, FileMode.Append)) 133 | { 134 | var data = Convert.FromBase64String(base64Content); 135 | await file.WriteAsync(data, 0, data.Length).ConfigureAwait(false); 136 | } 137 | 138 | promise.Resolve(null); 139 | } 140 | catch (Exception ex) 141 | { 142 | Reject(promise, filepath, ex); 143 | } 144 | } 145 | 146 | [ReactMethod] 147 | public async void write(string filepath, string base64Content, int position, IPromise promise) 148 | { 149 | try 150 | { 151 | // TODO: open file on background thread? 152 | using (var file = File.OpenWrite(filepath)) 153 | { 154 | if (position >= 0) 155 | { 156 | file.Position = position; 157 | } 158 | 159 | var data = Convert.FromBase64String(base64Content); 160 | await file.WriteAsync(data, 0, data.Length).ConfigureAwait(false); 161 | } 162 | 163 | promise.Resolve(null); 164 | } 165 | catch (Exception ex) 166 | { 167 | Reject(promise, filepath, ex); 168 | } 169 | } 170 | 171 | [ReactMethod] 172 | public void exists(string filepath, IPromise promise) 173 | { 174 | try 175 | { 176 | promise.Resolve(File.Exists(filepath) || Directory.Exists(filepath)); 177 | } 178 | catch (Exception ex) 179 | { 180 | Reject(promise, filepath, ex); 181 | } 182 | } 183 | 184 | [ReactMethod] 185 | public async void readFile(string filepath, IPromise promise) 186 | { 187 | try 188 | { 189 | if (!File.Exists(filepath)) 190 | { 191 | RejectFileNotFound(promise, filepath); 192 | return; 193 | } 194 | 195 | // TODO: open file on background thread? 196 | string base64Content; 197 | using (var file = File.OpenRead(filepath)) 198 | { 199 | var length = (int)file.Length; 200 | var buffer = new byte[length]; 201 | await file.ReadAsync(buffer, 0, length).ConfigureAwait(false); 202 | base64Content = Convert.ToBase64String(buffer); 203 | } 204 | 205 | promise.Resolve(base64Content); 206 | } 207 | catch (Exception ex) 208 | { 209 | Reject(promise, filepath, ex); 210 | } 211 | } 212 | 213 | [ReactMethod] 214 | public async void read(string filepath, int length, int position, IPromise promise) 215 | { 216 | try 217 | { 218 | if (!File.Exists(filepath)) 219 | { 220 | RejectFileNotFound(promise, filepath); 221 | return; 222 | } 223 | 224 | // TODO: open file on background thread? 225 | string base64Content; 226 | using (var file = File.OpenRead(filepath)) 227 | { 228 | file.Position = position; 229 | var buffer = new byte[length]; 230 | await file.ReadAsync(buffer, 0, length).ConfigureAwait(false); 231 | base64Content = Convert.ToBase64String(buffer); 232 | } 233 | 234 | promise.Resolve(base64Content); 235 | } 236 | catch (Exception ex) 237 | { 238 | Reject(promise, filepath, ex); 239 | } 240 | } 241 | 242 | [ReactMethod] 243 | public async void hash(string filepath, string algorithm, IPromise promise) 244 | { 245 | var hashAlgorithmFactory = default(Func); 246 | if (!s_hashAlgorithms.TryGetValue(algorithm, out hashAlgorithmFactory)) 247 | { 248 | promise.Reject(null, "Invalid hash algorithm."); 249 | return; 250 | } 251 | 252 | try 253 | { 254 | if (!File.Exists(filepath)) 255 | { 256 | RejectFileNotFound(promise, filepath); 257 | return; 258 | } 259 | 260 | await Task.Run(() => 261 | { 262 | var hexBuilder = new StringBuilder(); 263 | using (var hashAlgorithm = hashAlgorithmFactory()) 264 | { 265 | hashAlgorithm.Initialize(); 266 | var hash = default(byte[]); 267 | using (var file = File.OpenRead(filepath)) 268 | { 269 | hash = hashAlgorithm.ComputeHash(file); 270 | } 271 | 272 | foreach (var b in hash) 273 | { 274 | hexBuilder.Append(string.Format("{0:x2}", b)); 275 | } 276 | } 277 | 278 | promise.Resolve(hexBuilder.ToString()); 279 | }).ConfigureAwait(false); 280 | } 281 | catch (Exception ex) 282 | { 283 | Reject(promise, filepath, ex); 284 | } 285 | } 286 | 287 | [ReactMethod] 288 | public void moveFile(string filepath, string destPath, JObject options, IPromise promise) 289 | { 290 | try 291 | { 292 | // TODO: move file on background thread? 293 | File.Move(filepath, destPath); 294 | promise.Resolve(true); 295 | } 296 | catch (Exception ex) 297 | { 298 | Reject(promise, filepath, ex); 299 | } 300 | } 301 | 302 | [ReactMethod] 303 | public async void copyFile(string filepath, string destPath, JObject options, IPromise promise) 304 | { 305 | try 306 | { 307 | await Task.Run(() => File.Copy(filepath, destPath)).ConfigureAwait(false); 308 | promise.Resolve(null); 309 | 310 | } 311 | catch (Exception ex) 312 | { 313 | Reject(promise, filepath, ex); 314 | } 315 | } 316 | 317 | [ReactMethod] 318 | public async void readDir(string directory, IPromise promise) 319 | { 320 | try 321 | { 322 | await Task.Run(() => 323 | { 324 | var info = new DirectoryInfo(directory); 325 | if (!info.Exists) 326 | { 327 | promise.Reject(null, "Folder does not exist"); 328 | return; 329 | } 330 | 331 | var fileMaps = new JArray(); 332 | foreach (var item in info.EnumerateFileSystemInfos()) 333 | { 334 | var fileMap = new JObject 335 | { 336 | { "mtime", ConvertToUnixTimestamp(item.LastWriteTime) }, 337 | { "name", item.Name }, 338 | { "path", item.FullName }, 339 | }; 340 | 341 | var fileItem = item as FileInfo; 342 | if (fileItem != null) 343 | { 344 | fileMap.Add("type", FileType); 345 | fileMap.Add("size", fileItem.Length); 346 | } 347 | else 348 | { 349 | fileMap.Add("type", DirectoryType); 350 | fileMap.Add("size", 0); 351 | } 352 | 353 | fileMaps.Add(fileMap); 354 | } 355 | 356 | promise.Resolve(fileMaps); 357 | }); 358 | } 359 | catch (Exception ex) 360 | { 361 | Reject(promise, directory, ex); 362 | } 363 | } 364 | 365 | [ReactMethod] 366 | public void stat(string filepath, IPromise promise) 367 | { 368 | try 369 | { 370 | FileSystemInfo fileSystemInfo = new FileInfo(filepath); 371 | if (!fileSystemInfo.Exists) 372 | { 373 | fileSystemInfo = new DirectoryInfo(filepath); 374 | if (!fileSystemInfo.Exists) 375 | { 376 | promise.Reject(null, "File does not exist."); 377 | return; 378 | } 379 | } 380 | 381 | var fileInfo = fileSystemInfo as FileInfo; 382 | var statMap = new JObject 383 | { 384 | { "ctime", ConvertToUnixTimestamp(fileSystemInfo.CreationTime) }, 385 | { "mtime", ConvertToUnixTimestamp(fileSystemInfo.LastWriteTime) }, 386 | { "size", fileInfo?.Length ?? 0 }, 387 | { "type", fileInfo != null ? FileType: DirectoryType }, 388 | }; 389 | 390 | promise.Resolve(statMap); 391 | } 392 | catch (Exception ex) 393 | { 394 | Reject(promise, filepath, ex); 395 | } 396 | } 397 | 398 | [ReactMethod] 399 | public async void unlink(string filepath, IPromise promise) 400 | { 401 | try 402 | { 403 | var directoryInfo = new DirectoryInfo(filepath); 404 | var fileInfo = default(FileInfo); 405 | if (directoryInfo.Exists) 406 | { 407 | await Task.Run(() => Directory.Delete(filepath, true)).ConfigureAwait(false); 408 | } 409 | else if ((fileInfo = new FileInfo(filepath)).Exists) 410 | { 411 | await Task.Run(() => File.Delete(filepath)).ConfigureAwait(false); 412 | } 413 | else 414 | { 415 | promise.Reject(null, "File does not exist."); 416 | return; 417 | } 418 | 419 | promise.Resolve(null); 420 | } 421 | catch (Exception ex) 422 | { 423 | Reject(promise, filepath, ex); 424 | } 425 | } 426 | 427 | [ReactMethod] 428 | public async void mkdir(string filepath, JObject options, IPromise promise) 429 | { 430 | try 431 | { 432 | await Task.Run(() => Directory.CreateDirectory(filepath)).ConfigureAwait(false); 433 | promise.Resolve(null); 434 | } 435 | catch (Exception ex) 436 | { 437 | Reject(promise, filepath, ex); 438 | } 439 | } 440 | 441 | [ReactMethod] 442 | public async void downloadFile(JObject options, IPromise promise) 443 | { 444 | var filepath = options.Value("toFile"); 445 | 446 | try 447 | { 448 | var url = new Uri(options.Value("fromUrl")); 449 | var jobId = options.Value("jobId"); 450 | var headers = (JObject)options["headers"]; 451 | var progressDivider = options.Value("progressDivider"); 452 | 453 | var request = new HttpRequestMessage(HttpMethod.Get, url); 454 | foreach (var header in headers) 455 | { 456 | request.Headers.Add(header.Key, header.Value.Value()); 457 | } 458 | 459 | await _tasks.AddAndInvokeAsync(jobId, token => 460 | ProcessRequestAsync(promise, request, filepath, jobId, progressDivider, token)); 461 | } 462 | catch (Exception ex) 463 | { 464 | Reject(promise, filepath, ex); 465 | } 466 | } 467 | 468 | [ReactMethod] 469 | public void stopDownload(int jobId) 470 | { 471 | _tasks.Cancel(jobId); 472 | } 473 | 474 | [ReactMethod] 475 | public async void getFSInfo(IPromise promise) 476 | { 477 | try 478 | { 479 | var properties = await ApplicationData.Current.LocalFolder.Properties.RetrievePropertiesAsync( 480 | new[] 481 | { 482 | "System.FreeSpace", 483 | "System.Capacity", 484 | }) 485 | .AsTask() 486 | .ConfigureAwait(false); 487 | 488 | promise.Resolve(new JObject 489 | { 490 | { "freeSpace", (ulong)properties["System.FreeSpace"] }, 491 | { "totalSpace", (ulong)properties["System.Capacity"] }, 492 | }); 493 | } 494 | catch (Exception) 495 | { 496 | promise.Reject(null, "getFSInfo is not available"); 497 | } 498 | } 499 | 500 | [ReactMethod] 501 | public async void touch(string filepath, double mtime, double ctime, IPromise promise) 502 | { 503 | try 504 | { 505 | await Task.Run(() => 506 | { 507 | var fileInfo = new FileInfo(filepath); 508 | if (!fileInfo.Exists) 509 | { 510 | using (File.Create(filepath)) { } 511 | } 512 | 513 | fileInfo.CreationTimeUtc = ConvertFromUnixTimestamp(ctime); 514 | fileInfo.LastWriteTimeUtc = ConvertFromUnixTimestamp(mtime); 515 | 516 | promise.Resolve(fileInfo.FullName); 517 | }); 518 | } 519 | catch (Exception ex) 520 | { 521 | Reject(promise, filepath, ex); 522 | } 523 | } 524 | 525 | public override void OnReactInstanceDispose() 526 | { 527 | _tasks.CancelAllTasks(); 528 | _httpClient.Dispose(); 529 | } 530 | 531 | private async Task ProcessRequestAsync(IPromise promise, HttpRequestMessage request, string filepath, int jobId, int progressIncrement, CancellationToken token) 532 | { 533 | try 534 | { 535 | using (var response = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, token)) 536 | { 537 | var headersMap = new JObject(); 538 | foreach (var header in response.Headers) 539 | { 540 | headersMap.Add(header.Key, string.Join(",", header.Value)); 541 | } 542 | 543 | var contentLength = response.Content.Headers.ContentLength; 544 | SendEvent($"DownloadBegin-{jobId}", new JObject 545 | { 546 | { "jobId", jobId }, 547 | { "statusCode", (int)response.StatusCode }, 548 | { "contentLength", contentLength }, 549 | { "headers", headersMap }, 550 | }); 551 | 552 | // TODO: open file on background thread? 553 | long totalRead = 0; 554 | using (var fileStream = File.OpenWrite(filepath)) 555 | using (var stream = await response.Content.ReadAsStreamAsync()) 556 | { 557 | var contentLengthForProgress = contentLength ?? -1; 558 | var nextProgressIncrement = progressIncrement; 559 | var buffer = new byte[8 * 1024]; 560 | var read = 0; 561 | while ((read = await stream.ReadAsync(buffer, 0, buffer.Length)) > 0) 562 | { 563 | token.ThrowIfCancellationRequested(); 564 | 565 | await fileStream.WriteAsync(buffer, 0, read); 566 | if (contentLengthForProgress >= 0) 567 | { 568 | totalRead += read; 569 | if (totalRead * 100 / contentLengthForProgress >= nextProgressIncrement || 570 | totalRead == contentLengthForProgress) 571 | { 572 | SendEvent("DownloadProgress-" + jobId, new JObject 573 | { 574 | { "jobId", jobId }, 575 | { "contentLength", contentLength }, 576 | { "bytesWritten", totalRead }, 577 | }); 578 | 579 | nextProgressIncrement += progressIncrement; 580 | } 581 | } 582 | } 583 | } 584 | 585 | promise.Resolve(new JObject 586 | { 587 | { "jobId", jobId }, 588 | { "statusCode", (int)response.StatusCode }, 589 | { "bytesWritten", totalRead }, 590 | }); 591 | } 592 | } 593 | finally 594 | { 595 | request.Dispose(); 596 | } 597 | } 598 | 599 | private void Reject(IPromise promise, String filepath, Exception ex) 600 | { 601 | if (ex is FileNotFoundException) { 602 | RejectFileNotFound(promise, filepath); 603 | return; 604 | } 605 | 606 | promise.Reject(ex); 607 | } 608 | 609 | private void RejectFileNotFound(IPromise promise, String filepath) 610 | { 611 | promise.Reject("ENOENT", "ENOENT: no such file or directory, open '" + filepath + "'"); 612 | } 613 | 614 | private void SendEvent(string eventName, JObject eventData) 615 | { 616 | Emitter.emit(eventName, eventData); 617 | } 618 | 619 | private static string GetFolderPathSafe(Func getFolder) 620 | { 621 | try 622 | { 623 | return getFolder().Path; 624 | } 625 | catch (UnauthorizedAccessException) 626 | { 627 | return null; 628 | } 629 | } 630 | 631 | public static double ConvertToUnixTimestamp(DateTime date) 632 | { 633 | var origin = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); 634 | var diff = date.ToUniversalTime() - origin; 635 | return Math.Floor(diff.TotalSeconds); 636 | } 637 | 638 | public static DateTime ConvertFromUnixTimestamp(double timestamp) 639 | { 640 | var origin = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); 641 | var diff = TimeSpan.FromSeconds(timestamp); 642 | var dateTimeUtc = origin + diff; 643 | return dateTimeUtc.ToLocalTime(); 644 | } 645 | } 646 | } 647 | -------------------------------------------------------------------------------- /windows/RNFS/RNFSPackage.cs: -------------------------------------------------------------------------------- 1 | using ReactNative.Bridge; 2 | using ReactNative.Modules.Core; 3 | using ReactNative.UIManager; 4 | using System; 5 | using System.Collections.Generic; 6 | 7 | namespace RNFS 8 | { 9 | /// 10 | /// Package defining core framework modules (e.g., ). 11 | /// It should be used for modules that require special integration with 12 | /// other framework parts (e.g., with the list of packages to load view 13 | /// managers from). 14 | /// 15 | public class RNFSPackage : IReactPackage 16 | { 17 | /// 18 | /// Creates the list of native modules to register with the react 19 | /// instance. 20 | /// 21 | /// The react application context. 22 | /// The list of native modules. 23 | public IReadOnlyList CreateNativeModules(ReactContext reactContext) 24 | { 25 | return new List 26 | { 27 | new RNFSManager(reactContext), 28 | }; 29 | } 30 | 31 | /// 32 | /// Creates the list of JavaScript modules to register with the 33 | /// react instance. 34 | /// 35 | /// The list of JavaScript modules. 36 | public IReadOnlyList CreateJavaScriptModulesConfig() 37 | { 38 | return new List(0); 39 | } 40 | 41 | /// 42 | /// Creates the list of view managers that should be registered with 43 | /// the . 44 | /// 45 | /// The react application context. 46 | /// The list of view managers. 47 | public IReadOnlyList CreateViewManagers( 48 | ReactContext reactContext) 49 | { 50 | return new List(0); 51 | } 52 | } 53 | } 54 | --------------------------------------------------------------------------------