[ios][storage] Multi-app support for iOS storage
This commit is contained in:
parent
0d117f6827
commit
ec6d91b6e8
|
@ -15,6 +15,372 @@ RCT_EXPORT_MODULE(RNFirebaseStorage);
|
||||||
return dispatch_queue_create("com.invertase.firebase.storage", DISPATCH_QUEUE_SERIAL);
|
return dispatch_queue_create("com.invertase.firebase.storage", DISPATCH_QUEUE_SERIAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
delete
|
||||||
|
|
||||||
|
@url https://firebase.google.com/docs/reference/js/firebase.storage.Reference#delete
|
||||||
|
@param NSString path
|
||||||
|
*/
|
||||||
|
RCT_EXPORT_METHOD(delete:(NSString *) appName
|
||||||
|
path:(NSString *) path
|
||||||
|
resolver:(RCTPromiseResolveBlock) resolve
|
||||||
|
rejecter:(RCTPromiseRejectBlock) reject) {
|
||||||
|
FIRStorageReference *fileRef = [self getReference:path appName:appName];
|
||||||
|
|
||||||
|
[fileRef deleteWithCompletion:^(NSError *_Nullable error) {
|
||||||
|
if (error != nil) {
|
||||||
|
[self promiseRejectStorageException:reject error:error];
|
||||||
|
} else {
|
||||||
|
resolve([NSNull null]);
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
getDownloadURL
|
||||||
|
|
||||||
|
@url https://firebase.google.com/docs/reference/js/firebase.storage.Reference#getDownloadURL
|
||||||
|
@param NSString path
|
||||||
|
*/
|
||||||
|
RCT_EXPORT_METHOD(getDownloadURL:(NSString *) appName
|
||||||
|
path:(NSString *) path
|
||||||
|
resolver:(RCTPromiseResolveBlock) resolve
|
||||||
|
rejecter:(RCTPromiseRejectBlock) reject) {
|
||||||
|
FIRStorageReference *fileRef = [self getReference:path appName:appName];
|
||||||
|
|
||||||
|
[fileRef downloadURLWithCompletion:^(NSURL *_Nullable URL, NSError *_Nullable error) {
|
||||||
|
if (error != nil) {
|
||||||
|
[self promiseRejectStorageException:reject error:error];
|
||||||
|
} else {
|
||||||
|
resolve([URL absoluteString]);
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
getMetadata
|
||||||
|
|
||||||
|
@url https://firebase.google.com/docs/reference/js/firebase.storage.Reference#getMetadata
|
||||||
|
@param NSString path
|
||||||
|
*/
|
||||||
|
RCT_EXPORT_METHOD(getMetadata:(NSString *) appName
|
||||||
|
path:(NSString *) path
|
||||||
|
resolver:(RCTPromiseResolveBlock) resolve
|
||||||
|
rejecter:(RCTPromiseRejectBlock) reject) {
|
||||||
|
FIRStorageReference *fileRef = [self getReference:path appName:appName];
|
||||||
|
|
||||||
|
[fileRef metadataWithCompletion:^(FIRStorageMetadata *_Nullable metadata, NSError *_Nullable error) {
|
||||||
|
if (error != nil) {
|
||||||
|
[self promiseRejectStorageException:reject error:error];
|
||||||
|
} else {
|
||||||
|
resolve([metadata dictionaryRepresentation]);
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
updateMetadata
|
||||||
|
|
||||||
|
@url https://firebase.google.com/docs/reference/js/firebase.storage.Reference#updateMetadata
|
||||||
|
@param NSString path
|
||||||
|
@param NSDictionary metadata
|
||||||
|
*/
|
||||||
|
RCT_EXPORT_METHOD(updateMetadata:(NSString *) appName
|
||||||
|
path:(NSString *) path
|
||||||
|
metadata:(NSDictionary *) metadata
|
||||||
|
resolver:(RCTPromiseResolveBlock) resolve
|
||||||
|
rejecter:(RCTPromiseRejectBlock) reject) {
|
||||||
|
FIRStorageReference *fileRef = [self getReference:path appName:appName];
|
||||||
|
FIRStorageMetadata *firmetadata = [self buildMetadataFromMap:metadata];
|
||||||
|
|
||||||
|
[fileRef updateMetadata:firmetadata completion:^(FIRStorageMetadata *_Nullable metadata, NSError *_Nullable error) {
|
||||||
|
if (error != nil) {
|
||||||
|
[self promiseRejectStorageException:reject error:error];
|
||||||
|
} else {
|
||||||
|
resolve([metadata dictionaryRepresentation]);
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
downloadFile
|
||||||
|
|
||||||
|
@url https://firebase.google.com/docs/reference/js/firebase.storage.Reference#downloadFile
|
||||||
|
@param NSString path
|
||||||
|
@param NSString localPath
|
||||||
|
*/
|
||||||
|
RCT_EXPORT_METHOD(downloadFile:(NSString *) appName
|
||||||
|
path:(NSString *) path
|
||||||
|
localPath:(NSString *) localPath
|
||||||
|
resolver:(RCTPromiseResolveBlock) resolve
|
||||||
|
rejecter:(RCTPromiseRejectBlock) reject) {
|
||||||
|
FIRStorageReference *fileRef = [self getReference:path appName:appName];
|
||||||
|
NSURL *localFile = [NSURL fileURLWithPath:localPath];
|
||||||
|
FIRStorageDownloadTask *downloadTask = [fileRef writeToFile:localFile];
|
||||||
|
|
||||||
|
// listen for state changes, errors, and completion of the download.
|
||||||
|
[downloadTask observeStatus:FIRStorageTaskStatusResume handler:^(FIRStorageTaskSnapshot *snapshot) {
|
||||||
|
// download resumed, also fires when the upload starts
|
||||||
|
NSDictionary *event = [self getDownloadTaskAsDictionary:snapshot];
|
||||||
|
[self sendJSEvent:appName type:STORAGE_EVENT path:path title:STORAGE_STATE_CHANGED props:event];
|
||||||
|
}];
|
||||||
|
|
||||||
|
[downloadTask observeStatus:FIRStorageTaskStatusPause handler:^(FIRStorageTaskSnapshot *snapshot) {
|
||||||
|
// download paused
|
||||||
|
NSDictionary *event = [self getDownloadTaskAsDictionary:snapshot];
|
||||||
|
[self sendJSEvent:appName type:STORAGE_EVENT path:path title:STORAGE_STATE_CHANGED props:event];
|
||||||
|
}];
|
||||||
|
|
||||||
|
[downloadTask observeStatus:FIRStorageTaskStatusProgress handler:^(FIRStorageTaskSnapshot *snapshot) {
|
||||||
|
// download reported progress
|
||||||
|
NSDictionary *event = [self getDownloadTaskAsDictionary:snapshot];
|
||||||
|
[self sendJSEvent:appName type:STORAGE_EVENT path:path title:STORAGE_STATE_CHANGED props:event];
|
||||||
|
}];
|
||||||
|
|
||||||
|
[downloadTask observeStatus:FIRStorageTaskStatusSuccess handler:^(FIRStorageTaskSnapshot *snapshot) {
|
||||||
|
// download completed successfully
|
||||||
|
NSDictionary *resp = [self getDownloadTaskAsDictionary:snapshot];
|
||||||
|
[self sendJSEvent:appName type:STORAGE_EVENT path:path title:STORAGE_DOWNLOAD_SUCCESS props:resp];
|
||||||
|
resolve(resp);
|
||||||
|
}];
|
||||||
|
|
||||||
|
[downloadTask observeStatus:FIRStorageTaskStatusFailure handler:^(FIRStorageTaskSnapshot *snapshot) {
|
||||||
|
// download task failed
|
||||||
|
// TODO sendJSError event
|
||||||
|
if (snapshot.error != nil) {
|
||||||
|
[self promiseRejectStorageException:reject error:snapshot.error];
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
setMaxDownloadRetryTime
|
||||||
|
|
||||||
|
@url https://firebase.google.com/docs/reference/js/firebase.storage.Storage#setMaxDownloadRetryTime
|
||||||
|
@param NSNumber milliseconds
|
||||||
|
*/
|
||||||
|
RCT_EXPORT_METHOD(setMaxDownloadRetryTime:(NSString *) appName
|
||||||
|
milliseconds:(NSNumber *) milliseconds) {
|
||||||
|
[[FIRStorage storageForApp:[FIRApp appNamed:appName]] setMaxDownloadRetryTime:[milliseconds doubleValue]];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
setMaxOperationRetryTime
|
||||||
|
|
||||||
|
@url https://firebase.google.com/docs/reference/js/firebase.storage.Storage#setMaxOperationRetryTime
|
||||||
|
@param NSNumber milliseconds
|
||||||
|
*/
|
||||||
|
RCT_EXPORT_METHOD(setMaxOperationRetryTime:(NSString *) appName
|
||||||
|
milliseconds:(NSNumber *) milliseconds) {
|
||||||
|
[[FIRStorage storageForApp:[FIRApp appNamed:appName]] setMaxOperationRetryTime:[milliseconds doubleValue]];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
setMaxUploadRetryTime
|
||||||
|
|
||||||
|
@url https://firebase.google.com/docs/reference/js/firebase.storage.Storage#setMaxUploadRetryTime
|
||||||
|
*/
|
||||||
|
RCT_EXPORT_METHOD(setMaxUploadRetryTime:(NSString *) appName
|
||||||
|
milliseconds:(NSNumber *) milliseconds) {
|
||||||
|
[[FIRStorage storageForApp:[FIRApp appNamed:appName]] setMaxUploadRetryTime:[milliseconds doubleValue]];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
putFile
|
||||||
|
|
||||||
|
@url https://firebase.google.com/docs/reference/js/firebase.storage.Reference#putFile
|
||||||
|
@param NSString path
|
||||||
|
@param NSString localPath
|
||||||
|
@param NSDictionary metadata
|
||||||
|
*/
|
||||||
|
RCT_EXPORT_METHOD(putFile:(NSString *) appName
|
||||||
|
path:(NSString *) path
|
||||||
|
localPath:(NSString *) localPath
|
||||||
|
metadata:(NSDictionary *) metadata
|
||||||
|
resolver:(RCTPromiseResolveBlock) resolve
|
||||||
|
rejecter:(RCTPromiseRejectBlock) reject) {
|
||||||
|
if ([localPath hasPrefix:@"assets-library://"] || [localPath hasPrefix:@"ph://"]) {
|
||||||
|
PHFetchResult *assets;
|
||||||
|
|
||||||
|
if ([localPath hasPrefix:@"assets-library://"]) {
|
||||||
|
NSURL *localFile = [[NSURL alloc] initWithString:localPath];
|
||||||
|
assets = [PHAsset fetchAssetsWithALAssetURLs:@[localFile] options:nil];
|
||||||
|
} else {
|
||||||
|
NSString *assetId = [localPath substringFromIndex:@"ph://".length];
|
||||||
|
assets = [PHAsset fetchAssetsWithLocalIdentifiers:@[assetId] options:nil];
|
||||||
|
}
|
||||||
|
|
||||||
|
PHAsset *asset = [assets firstObject];
|
||||||
|
|
||||||
|
// this is based on http://stackoverflow.com/questions/35241449
|
||||||
|
if (asset.mediaType == PHAssetMediaTypeImage) {
|
||||||
|
// images
|
||||||
|
PHImageRequestOptions *options = [PHImageRequestOptions new];
|
||||||
|
options.networkAccessAllowed = true;
|
||||||
|
[[PHImageManager defaultManager] requestImageDataForAsset:asset options:options resultHandler:^(NSData *imageData, NSString *dataUTI, UIImageOrientation orientation, NSDictionary *info) {
|
||||||
|
if (info[PHImageErrorKey] == nil) {
|
||||||
|
[self uploadData:appName data:imageData metadata:metadata path:path resolver:resolve rejecter:reject];
|
||||||
|
} else {
|
||||||
|
reject(@"storage/request-image-data-failed", @"Could not obtain image data for the specified file.", nil);
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
} else if (asset.mediaType == PHAssetMediaTypeVideo) {
|
||||||
|
// video
|
||||||
|
PHVideoRequestOptions *options = [PHVideoRequestOptions new];
|
||||||
|
options.networkAccessAllowed = true;
|
||||||
|
[[PHImageManager defaultManager] requestExportSessionForVideo:asset options:options exportPreset:AVAssetExportPresetHighestQuality resultHandler:^(AVAssetExportSession *_Nullable exportSession, NSDictionary *_Nullable info) {
|
||||||
|
if (info[PHImageErrorKey] == nil) {
|
||||||
|
NSURL *tempUrl = [self temporaryFileUrl];
|
||||||
|
exportSession.outputURL = tempUrl;
|
||||||
|
|
||||||
|
NSArray<PHAssetResource *> *resources = [PHAssetResource assetResourcesForAsset:asset];
|
||||||
|
for (PHAssetResource *resource in resources) {
|
||||||
|
exportSession.outputFileType = resource.uniformTypeIdentifier;
|
||||||
|
if (exportSession.outputFileType != nil) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
[exportSession exportAsynchronouslyWithCompletionHandler:^{
|
||||||
|
if (exportSession.status == AVAssetExportSessionStatusCompleted) {
|
||||||
|
[self uploadFile:appName url:tempUrl metadata:metadata path:path resolver:resolve rejecter:reject];
|
||||||
|
// we're not cleaning up the temporary file at the moment, just relying on the OS to do that in it's own time - todo?
|
||||||
|
} else {
|
||||||
|
reject(@"storage/temporary-file-failure", @"Unable to create temporary file for upload.", nil);
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
} else {
|
||||||
|
reject(@"storage/export-session-failure", @"Unable to create export session for asset.", nil);
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
NSData *data = [[NSFileManager defaultManager] contentsAtPath:localPath];
|
||||||
|
[self uploadData:appName data:data metadata:metadata path:path resolver:resolve rejecter:reject];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSURL *)temporaryFileUrl {
|
||||||
|
NSString *filename = [NSString stringWithFormat:@"%@.tmp", [[NSProcessInfo processInfo] globallyUniqueString]];
|
||||||
|
return [[NSURL fileURLWithPath:NSTemporaryDirectory()] URLByAppendingPathComponent:filename];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)uploadFile:(NSString *)appName url:(NSURL *)url metadata:(NSDictionary *)metadata path:(NSString *)path resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject {
|
||||||
|
FIRStorageReference *fileRef = [self getReference:path appName:appName];
|
||||||
|
FIRStorageMetadata *firmetadata = [self buildMetadataFromMap:metadata];
|
||||||
|
FIRStorageUploadTask *uploadTask = [fileRef putFile:url metadata:firmetadata];
|
||||||
|
[self addUploadObservers:appName uploadTask:uploadTask path:path resolver:resolve rejecter:reject];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)uploadData:(NSString *)appName data:(NSData *)data metadata:(NSDictionary *)metadata path:(NSString *)path resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject {
|
||||||
|
FIRStorageReference *fileRef = [self getReference:path appName:appName];
|
||||||
|
FIRStorageMetadata *firmetadata = [self buildMetadataFromMap:metadata];
|
||||||
|
FIRStorageUploadTask *uploadTask = [fileRef putData:data metadata:firmetadata];
|
||||||
|
[self addUploadObservers:appName uploadTask:uploadTask path:path resolver:resolve rejecter:reject];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)addUploadObservers:(NSString *)appName uploadTask:(FIRStorageUploadTask *)uploadTask path:(NSString *)path resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject {
|
||||||
|
// listen for state changes, errors, and completion of the upload.
|
||||||
|
[uploadTask observeStatus:FIRStorageTaskStatusResume handler:^(FIRStorageTaskSnapshot *snapshot) {
|
||||||
|
// upload resumed, also fires when the upload starts
|
||||||
|
NSDictionary *event = [self getUploadTaskAsDictionary:snapshot];
|
||||||
|
[self sendJSEvent:appName type:STORAGE_EVENT path:path title:STORAGE_STATE_CHANGED props:event];
|
||||||
|
}];
|
||||||
|
|
||||||
|
[uploadTask observeStatus:FIRStorageTaskStatusPause handler:^(FIRStorageTaskSnapshot *snapshot) {
|
||||||
|
// upload paused
|
||||||
|
NSDictionary *event = [self getUploadTaskAsDictionary:snapshot];
|
||||||
|
[self sendJSEvent:appName type:STORAGE_EVENT path:path title:STORAGE_STATE_CHANGED props:event];
|
||||||
|
}];
|
||||||
|
[uploadTask observeStatus:FIRStorageTaskStatusProgress handler:^(FIRStorageTaskSnapshot *snapshot) {
|
||||||
|
// upload reported progress
|
||||||
|
NSDictionary *event = [self getUploadTaskAsDictionary:snapshot];
|
||||||
|
[self sendJSEvent:appName type:STORAGE_EVENT path:path title:STORAGE_STATE_CHANGED props:event];
|
||||||
|
}];
|
||||||
|
|
||||||
|
[uploadTask observeStatus:FIRStorageTaskStatusSuccess handler:^(FIRStorageTaskSnapshot *snapshot) {
|
||||||
|
// upload completed successfully
|
||||||
|
NSDictionary *resp = [self getUploadTaskAsDictionary:snapshot];
|
||||||
|
[self sendJSEvent:appName type:STORAGE_EVENT path:path title:STORAGE_STATE_CHANGED props:resp];
|
||||||
|
[self sendJSEvent:appName type:STORAGE_EVENT path:path title:STORAGE_UPLOAD_SUCCESS props:resp];
|
||||||
|
resolve(resp);
|
||||||
|
}];
|
||||||
|
|
||||||
|
[uploadTask observeStatus:FIRStorageTaskStatusFailure handler:^(FIRStorageTaskSnapshot *snapshot) {
|
||||||
|
if (snapshot.error != nil) {
|
||||||
|
[self promiseRejectStorageException:reject error:snapshot.error];
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (FIRStorageReference *)getReference:(NSString *)path
|
||||||
|
appName:(NSString *)appName {
|
||||||
|
if ([path hasPrefix:@"url::"]) {
|
||||||
|
NSString *url = [path substringFromIndex:5];
|
||||||
|
return [[FIRStorage storageForApp:[FIRApp appNamed:appName]] referenceForURL:url];
|
||||||
|
} else {
|
||||||
|
return [[FIRStorage storageForApp:[FIRApp appNamed:appName]] referenceWithPath:path];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSDictionary *)getDownloadTaskAsDictionary:(FIRStorageTaskSnapshot *)task {
|
||||||
|
return @{@"bytesTransferred": @(task.progress.completedUnitCount), @"ref": task.reference.fullPath, @"state": [self getTaskStatus:task.status], @"totalBytes": @(task.progress.totalUnitCount)};
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSDictionary *)getUploadTaskAsDictionary:(FIRStorageTaskSnapshot *)task {
|
||||||
|
NSString *downloadUrl = [task.metadata.downloadURL absoluteString];
|
||||||
|
FIRStorageMetadata *metadata = [[FIRStorageMetadata alloc] initWithDictionary:[task.metadata dictionaryRepresentation]];
|
||||||
|
return @{@"bytesTransferred": @(task.progress.completedUnitCount), @"downloadUrl": downloadUrl != nil ? downloadUrl : [NSNull null], @"metadata": metadata != nil ? metadata : [NSNull null], @"ref": task.reference.fullPath, @"state": [self getTaskStatus:task.status], @"totalBytes": @(task.progress.totalUnitCount)};
|
||||||
|
}
|
||||||
|
|
||||||
|
- (FIRStorageMetadata *)buildMetadataFromMap:(NSDictionary *)metadata {
|
||||||
|
NSMutableDictionary *metaCopy = [metadata mutableCopy];
|
||||||
|
[metaCopy removeObjectForKey:@"customMetadata"];
|
||||||
|
FIRStorageMetadata *storageMetadata = [[FIRStorageMetadata alloc] initWithDictionary:metaCopy];
|
||||||
|
storageMetadata.customMetadata = [metadata[@"customMetadata"] mutableCopy];
|
||||||
|
return storageMetadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString *)getTaskStatus:(FIRStorageTaskStatus)status {
|
||||||
|
if (status == FIRStorageTaskStatusResume || status == FIRStorageTaskStatusProgress) {
|
||||||
|
return @"running";
|
||||||
|
} else if (status == FIRStorageTaskStatusPause) {
|
||||||
|
return @"paused";
|
||||||
|
} else if (status == FIRStorageTaskStatusSuccess) {
|
||||||
|
return @"success";
|
||||||
|
} else if (status == FIRStorageTaskStatusFailure) {
|
||||||
|
return @"error";
|
||||||
|
} else {
|
||||||
|
return @"unknown";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString *)getPathForDirectory:(int)directory {
|
||||||
|
NSArray *paths = NSSearchPathForDirectoriesInDomains(directory, NSUserDomainMask, YES);
|
||||||
|
return [paths firstObject];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSDictionary *)constantsToExport {
|
||||||
|
return @{@"MAIN_BUNDLE_PATH": [[NSBundle mainBundle] bundlePath], @"CACHES_DIRECTORY_PATH": [self getPathForDirectory:NSCachesDirectory], @"DOCUMENT_DIRECTORY_PATH": [self getPathForDirectory:NSDocumentDirectory], @"EXTERNAL_DIRECTORY_PATH": [NSNull null], @"EXTERNAL_STORAGE_DIRECTORY_PATH": [NSNull null], @"TEMP_DIRECTORY_PATH": NSTemporaryDirectory(), @"LIBRARY_DIRECTORY_PATH": [self getPathForDirectory:NSLibraryDirectory], @"FILETYPE_REGULAR": NSFileTypeRegular, @"FILETYPE_DIRECTORY": NSFileTypeDirectory};
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSArray<NSString *> *)supportedEvents {
|
||||||
|
return @[STORAGE_EVENT, STORAGE_ERROR];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)sendJSError:(NSString *)appName error:(NSError *)error path:(NSString *)path {
|
||||||
|
NSDictionary *evt = @{@"path": path, @"message": [error debugDescription]};
|
||||||
|
[self sendJSEvent:appName type:STORAGE_ERROR path:path title:STORAGE_ERROR props:evt];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)sendJSEvent:(NSString *)appName type:(NSString *)type path:(NSString *)path title:(NSString *)title props:(NSDictionary *)props {
|
||||||
|
@try {
|
||||||
|
[self sendEventWithName:type body:@{@"eventName": title, @"appName": appName, @"path": path, @"body": props}];
|
||||||
|
} @catch (NSException *err) {
|
||||||
|
NSLog(@"An error occurred in sendJSEvent: %@", [err debugDescription]);
|
||||||
|
NSLog(@"Tried to send: %@ with %@", title, props);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Reject a promise with a storage exception
|
Reject a promise with a storage exception
|
||||||
|
|
||||||
|
@ -92,392 +458,6 @@ RCT_EXPORT_MODULE(RNFirebaseStorage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
delete
|
|
||||||
|
|
||||||
@url https://firebase.google.com/docs/reference/js/firebase.storage.Reference#delete
|
|
||||||
@param NSString path
|
|
||||||
*/
|
|
||||||
RCT_EXPORT_METHOD(delete:
|
|
||||||
(NSString *) path
|
|
||||||
resolver:
|
|
||||||
(RCTPromiseResolveBlock) resolve
|
|
||||||
rejecter:
|
|
||||||
(RCTPromiseRejectBlock) reject) {
|
|
||||||
FIRStorageReference *fileRef = [self getReference:path];
|
|
||||||
|
|
||||||
[fileRef deleteWithCompletion:^(NSError *_Nullable error) {
|
|
||||||
if (error != nil) {
|
|
||||||
[self promiseRejectStorageException:reject error:error];
|
|
||||||
} else {
|
|
||||||
resolve([NSNull null]);
|
|
||||||
}
|
|
||||||
}];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
getDownloadURL
|
|
||||||
|
|
||||||
@url https://firebase.google.com/docs/reference/js/firebase.storage.Reference#getDownloadURL
|
|
||||||
@param NSString path
|
|
||||||
*/
|
|
||||||
RCT_EXPORT_METHOD(getDownloadURL:
|
|
||||||
(NSString *) path
|
|
||||||
resolver:
|
|
||||||
(RCTPromiseResolveBlock) resolve
|
|
||||||
rejecter:
|
|
||||||
(RCTPromiseRejectBlock) reject) {
|
|
||||||
FIRStorageReference *fileRef = [self getReference:path];
|
|
||||||
|
|
||||||
[fileRef downloadURLWithCompletion:^(NSURL *_Nullable URL, NSError *_Nullable error) {
|
|
||||||
if (error != nil) {
|
|
||||||
[self promiseRejectStorageException:reject error:error];
|
|
||||||
} else {
|
|
||||||
resolve([URL absoluteString]);
|
|
||||||
}
|
|
||||||
}];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
getMetadata
|
|
||||||
|
|
||||||
@url https://firebase.google.com/docs/reference/js/firebase.storage.Reference#getMetadata
|
|
||||||
@param NSString path
|
|
||||||
*/
|
|
||||||
RCT_EXPORT_METHOD(getMetadata:
|
|
||||||
(NSString *) path
|
|
||||||
resolver:
|
|
||||||
(RCTPromiseResolveBlock) resolve
|
|
||||||
rejecter:
|
|
||||||
(RCTPromiseRejectBlock) reject) {
|
|
||||||
FIRStorageReference *fileRef = [self getReference:path];
|
|
||||||
|
|
||||||
[fileRef metadataWithCompletion:^(FIRStorageMetadata *_Nullable metadata, NSError *_Nullable error) {
|
|
||||||
if (error != nil) {
|
|
||||||
[self promiseRejectStorageException:reject error:error];
|
|
||||||
} else {
|
|
||||||
resolve([metadata dictionaryRepresentation]);
|
|
||||||
}
|
|
||||||
}];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
updateMetadata
|
|
||||||
|
|
||||||
@url https://firebase.google.com/docs/reference/js/firebase.storage.Reference#updateMetadata
|
|
||||||
@param NSString path
|
|
||||||
@param NSDictionary metadata
|
|
||||||
*/
|
|
||||||
RCT_EXPORT_METHOD(updateMetadata:
|
|
||||||
(NSString *) path
|
|
||||||
metadata:
|
|
||||||
(NSDictionary *) metadata
|
|
||||||
resolver:
|
|
||||||
(RCTPromiseResolveBlock) resolve
|
|
||||||
rejecter:
|
|
||||||
(RCTPromiseRejectBlock) reject) {
|
|
||||||
FIRStorageReference *fileRef = [self getReference:path];
|
|
||||||
FIRStorageMetadata *firmetadata = [self buildMetadataFromMap:metadata];
|
|
||||||
|
|
||||||
[fileRef updateMetadata:firmetadata completion:^(FIRStorageMetadata *_Nullable metadata, NSError *_Nullable error) {
|
|
||||||
if (error != nil) {
|
|
||||||
[self promiseRejectStorageException:reject error:error];
|
|
||||||
} else {
|
|
||||||
resolve([metadata dictionaryRepresentation]);
|
|
||||||
}
|
|
||||||
}];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
downloadFile
|
|
||||||
|
|
||||||
@url https://firebase.google.com/docs/reference/js/firebase.storage.Reference#downloadFile
|
|
||||||
@param NSString path
|
|
||||||
@param NSString localPath
|
|
||||||
*/
|
|
||||||
RCT_EXPORT_METHOD(downloadFile:
|
|
||||||
(NSString *) appName
|
|
||||||
path:
|
|
||||||
(NSString *) path
|
|
||||||
localPath:
|
|
||||||
(NSString *) localPath
|
|
||||||
resolver:
|
|
||||||
(RCTPromiseResolveBlock) resolve
|
|
||||||
rejecter:
|
|
||||||
(RCTPromiseRejectBlock) reject) {
|
|
||||||
FIRStorageReference *fileRef = [self getReference:path];
|
|
||||||
NSURL *localFile = [NSURL fileURLWithPath:localPath];
|
|
||||||
FIRStorageDownloadTask *downloadTask = [fileRef writeToFile:localFile];
|
|
||||||
|
|
||||||
// listen for state changes, errors, and completion of the download.
|
|
||||||
[downloadTask observeStatus:FIRStorageTaskStatusResume handler:^(FIRStorageTaskSnapshot *snapshot) {
|
|
||||||
// download resumed, also fires when the upload starts
|
|
||||||
NSDictionary *event = [self getDownloadTaskAsDictionary:snapshot];
|
|
||||||
[self sendJSEvent:appName type:STORAGE_EVENT path:path title:STORAGE_STATE_CHANGED props:event];
|
|
||||||
}];
|
|
||||||
|
|
||||||
[downloadTask observeStatus:FIRStorageTaskStatusPause handler:^(FIRStorageTaskSnapshot *snapshot) {
|
|
||||||
// download paused
|
|
||||||
NSDictionary *event = [self getDownloadTaskAsDictionary:snapshot];
|
|
||||||
[self sendJSEvent:appName type:STORAGE_EVENT path:path title:STORAGE_STATE_CHANGED props:event];
|
|
||||||
}];
|
|
||||||
|
|
||||||
[downloadTask observeStatus:FIRStorageTaskStatusProgress handler:^(FIRStorageTaskSnapshot *snapshot) {
|
|
||||||
// download reported progress
|
|
||||||
NSDictionary *event = [self getDownloadTaskAsDictionary:snapshot];
|
|
||||||
[self sendJSEvent:appName type:STORAGE_EVENT path:path title:STORAGE_STATE_CHANGED props:event];
|
|
||||||
}];
|
|
||||||
|
|
||||||
[downloadTask observeStatus:FIRStorageTaskStatusSuccess handler:^(FIRStorageTaskSnapshot *snapshot) {
|
|
||||||
// download completed successfully
|
|
||||||
NSDictionary *resp = [self getDownloadTaskAsDictionary:snapshot];
|
|
||||||
[self sendJSEvent:appName type:STORAGE_EVENT path:path title:STORAGE_DOWNLOAD_SUCCESS props:resp];
|
|
||||||
resolve(resp);
|
|
||||||
}];
|
|
||||||
|
|
||||||
[downloadTask observeStatus:FIRStorageTaskStatusFailure handler:^(FIRStorageTaskSnapshot *snapshot) {
|
|
||||||
// download task failed
|
|
||||||
// TODO sendJSError event
|
|
||||||
if (snapshot.error != nil) {
|
|
||||||
[self promiseRejectStorageException:reject error:snapshot.error];
|
|
||||||
}
|
|
||||||
}];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
setMaxDownloadRetryTime
|
|
||||||
|
|
||||||
@url https://firebase.google.com/docs/reference/js/firebase.storage.Storage#setMaxDownloadRetryTime
|
|
||||||
@param NSNumber milliseconds
|
|
||||||
*/
|
|
||||||
RCT_EXPORT_METHOD(setMaxDownloadRetryTime:
|
|
||||||
(NSNumber *) milliseconds) {
|
|
||||||
[[FIRStorage storage] setMaxDownloadRetryTime:[milliseconds doubleValue]];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
setMaxOperationRetryTime
|
|
||||||
|
|
||||||
@url https://firebase.google.com/docs/reference/js/firebase.storage.Storage#setMaxOperationRetryTime
|
|
||||||
@param NSNumber milliseconds
|
|
||||||
*/
|
|
||||||
RCT_EXPORT_METHOD(setMaxOperationRetryTime:
|
|
||||||
(NSNumber *) milliseconds) {
|
|
||||||
[[FIRStorage storage] setMaxOperationRetryTime:[milliseconds doubleValue]];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
setMaxUploadRetryTime
|
|
||||||
|
|
||||||
@url https://firebase.google.com/docs/reference/js/firebase.storage.Storage#setMaxUploadRetryTime
|
|
||||||
*/
|
|
||||||
RCT_EXPORT_METHOD(setMaxUploadRetryTime:
|
|
||||||
(NSNumber *) milliseconds) {
|
|
||||||
[[FIRStorage storage] setMaxUploadRetryTime:[milliseconds doubleValue]];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
putFile
|
|
||||||
|
|
||||||
@url https://firebase.google.com/docs/reference/js/firebase.storage.Reference#putFile
|
|
||||||
@param NSString path
|
|
||||||
@param NSString localPath
|
|
||||||
@param NSDictionary metadata
|
|
||||||
*/
|
|
||||||
RCT_EXPORT_METHOD(putFile:
|
|
||||||
(NSString *) appName
|
|
||||||
path:
|
|
||||||
(NSString *) path
|
|
||||||
localPath:
|
|
||||||
(NSString *) localPath
|
|
||||||
metadata:
|
|
||||||
(NSDictionary *) metadata
|
|
||||||
resolver:
|
|
||||||
(RCTPromiseResolveBlock) resolve
|
|
||||||
rejecter:
|
|
||||||
(RCTPromiseRejectBlock) reject) {
|
|
||||||
if ([localPath hasPrefix:@"assets-library://"] || [localPath hasPrefix:@"ph://"]) {
|
|
||||||
PHFetchResult *assets;
|
|
||||||
|
|
||||||
if ([localPath hasPrefix:@"assets-library://"]) {
|
|
||||||
NSURL *localFile = [[NSURL alloc] initWithString:localPath];
|
|
||||||
assets = [PHAsset fetchAssetsWithALAssetURLs:@[localFile] options:nil];
|
|
||||||
} else {
|
|
||||||
NSString *assetId = [localPath substringFromIndex:@"ph://".length];
|
|
||||||
assets = [PHAsset fetchAssetsWithLocalIdentifiers:@[assetId] options:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
PHAsset *asset = [assets firstObject];
|
|
||||||
|
|
||||||
// this is based on http://stackoverflow.com/questions/35241449
|
|
||||||
if (asset.mediaType == PHAssetMediaTypeImage) {
|
|
||||||
// images
|
|
||||||
PHImageRequestOptions *options = [PHImageRequestOptions new];
|
|
||||||
options.networkAccessAllowed = true;
|
|
||||||
[[PHImageManager defaultManager] requestImageDataForAsset:asset options:options resultHandler:^(NSData *imageData, NSString *dataUTI, UIImageOrientation orientation, NSDictionary *info) {
|
|
||||||
if (info[PHImageErrorKey] == nil) {
|
|
||||||
[self uploadData:appName data:imageData metadata:metadata path:path resolver:resolve rejecter:reject];
|
|
||||||
} else {
|
|
||||||
reject(@"storage/request-image-data-failed", @"Could not obtain image data for the specified file.", nil);
|
|
||||||
}
|
|
||||||
}];
|
|
||||||
} else if (asset.mediaType == PHAssetMediaTypeVideo) {
|
|
||||||
// video
|
|
||||||
PHVideoRequestOptions *options = [PHVideoRequestOptions new];
|
|
||||||
options.networkAccessAllowed = true;
|
|
||||||
[[PHImageManager defaultManager] requestExportSessionForVideo:asset options:options exportPreset:AVAssetExportPresetHighestQuality resultHandler:^(AVAssetExportSession *_Nullable exportSession, NSDictionary *_Nullable info) {
|
|
||||||
if (info[PHImageErrorKey] == nil) {
|
|
||||||
NSURL *tempUrl = [self temporaryFileUrl];
|
|
||||||
exportSession.outputURL = tempUrl;
|
|
||||||
|
|
||||||
NSArray<PHAssetResource *> *resources = [PHAssetResource assetResourcesForAsset:asset];
|
|
||||||
for (PHAssetResource *resource in resources) {
|
|
||||||
exportSession.outputFileType = resource.uniformTypeIdentifier;
|
|
||||||
if (exportSession.outputFileType != nil) break;
|
|
||||||
}
|
|
||||||
|
|
||||||
[exportSession exportAsynchronouslyWithCompletionHandler:^{
|
|
||||||
if (exportSession.status == AVAssetExportSessionStatusCompleted) {
|
|
||||||
[self uploadFile:appName url:tempUrl metadata:metadata path:path resolver:resolve rejecter:reject];
|
|
||||||
// we're not cleaning up the temporary file at the moment, just relying on the OS to do that in it's own time - todo?
|
|
||||||
} else {
|
|
||||||
reject(@"storage/temporary-file-failure", @"Unable to create temporary file for upload.", nil);
|
|
||||||
}
|
|
||||||
}];
|
|
||||||
} else {
|
|
||||||
reject(@"storage/export-session-failure", @"Unable to create export session for asset.", nil);
|
|
||||||
}
|
|
||||||
}];
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
NSData *data = [[NSFileManager defaultManager] contentsAtPath:localPath];
|
|
||||||
[self uploadData:appName data:data metadata:metadata path:path resolver:resolve rejecter:reject];
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSURL *)temporaryFileUrl {
|
|
||||||
NSString *filename = [NSString stringWithFormat:@"%@.tmp", [[NSProcessInfo processInfo] globallyUniqueString]];
|
|
||||||
return [[NSURL fileURLWithPath:NSTemporaryDirectory()] URLByAppendingPathComponent:filename];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)uploadFile:(NSString *)appName url:(NSURL *)url metadata:(NSDictionary *)metadata path:(NSString *)path resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject {
|
|
||||||
FIRStorageReference *fileRef = [self getReference:path];
|
|
||||||
FIRStorageMetadata *firmetadata = [self buildMetadataFromMap:metadata];
|
|
||||||
FIRStorageUploadTask *uploadTask = [fileRef putFile:url metadata:firmetadata];
|
|
||||||
[self addUploadObservers:appName uploadTask:uploadTask path:path resolver:resolve rejecter:reject];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)uploadData:(NSString *)appName data:(NSData *)data metadata:(NSDictionary *)metadata path:(NSString *)path resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject {
|
|
||||||
FIRStorageReference *fileRef = [self getReference:path];
|
|
||||||
FIRStorageMetadata *firmetadata = [self buildMetadataFromMap:metadata];
|
|
||||||
FIRStorageUploadTask *uploadTask = [fileRef putData:data metadata:firmetadata];
|
|
||||||
[self addUploadObservers:appName uploadTask:uploadTask path:path resolver:resolve rejecter:reject];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)addUploadObservers:(NSString *)appName uploadTask:(FIRStorageUploadTask *)uploadTask path:(NSString *)path resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject {
|
|
||||||
// listen for state changes, errors, and completion of the upload.
|
|
||||||
[uploadTask observeStatus:FIRStorageTaskStatusResume handler:^(FIRStorageTaskSnapshot *snapshot) {
|
|
||||||
// upload resumed, also fires when the upload starts
|
|
||||||
NSDictionary *event = [self getUploadTaskAsDictionary:snapshot];
|
|
||||||
[self sendJSEvent:appName type:STORAGE_EVENT path:path title:STORAGE_STATE_CHANGED props:event];
|
|
||||||
}];
|
|
||||||
|
|
||||||
[uploadTask observeStatus:FIRStorageTaskStatusPause handler:^(FIRStorageTaskSnapshot *snapshot) {
|
|
||||||
// upload paused
|
|
||||||
NSDictionary *event = [self getUploadTaskAsDictionary:snapshot];
|
|
||||||
[self sendJSEvent:appName type:STORAGE_EVENT path:path title:STORAGE_STATE_CHANGED props:event];
|
|
||||||
}];
|
|
||||||
[uploadTask observeStatus:FIRStorageTaskStatusProgress handler:^(FIRStorageTaskSnapshot *snapshot) {
|
|
||||||
// upload reported progress
|
|
||||||
NSDictionary *event = [self getUploadTaskAsDictionary:snapshot];
|
|
||||||
[self sendJSEvent:appName type:STORAGE_EVENT path:path title:STORAGE_STATE_CHANGED props:event];
|
|
||||||
}];
|
|
||||||
|
|
||||||
[uploadTask observeStatus:FIRStorageTaskStatusSuccess handler:^(FIRStorageTaskSnapshot *snapshot) {
|
|
||||||
// upload completed successfully
|
|
||||||
NSDictionary *resp = [self getUploadTaskAsDictionary:snapshot];
|
|
||||||
[self sendJSEvent:appName type:STORAGE_EVENT path:path title:STORAGE_STATE_CHANGED props:resp];
|
|
||||||
[self sendJSEvent:appName type:STORAGE_EVENT path:path title:STORAGE_UPLOAD_SUCCESS props:resp];
|
|
||||||
resolve(resp);
|
|
||||||
}];
|
|
||||||
|
|
||||||
[uploadTask observeStatus:FIRStorageTaskStatusFailure handler:^(FIRStorageTaskSnapshot *snapshot) {
|
|
||||||
if (snapshot.error != nil) {
|
|
||||||
[self promiseRejectStorageException:reject error:snapshot.error];
|
|
||||||
}
|
|
||||||
}];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (FIRStorageReference *)getReference:(NSString *)path {
|
|
||||||
if ([path hasPrefix:@"url::"]) {
|
|
||||||
NSString *url = [path substringFromIndex:5];
|
|
||||||
return [[FIRStorage storage] referenceForURL:url];
|
|
||||||
} else {
|
|
||||||
return [[FIRStorage storage] referenceWithPath:path];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSDictionary *)getDownloadTaskAsDictionary:(FIRStorageTaskSnapshot *)task {
|
|
||||||
return @{@"bytesTransferred": @(task.progress.completedUnitCount), @"ref": task.reference.fullPath, @"state": [self getTaskStatus:task.status], @"totalBytes": @(task.progress.totalUnitCount)};
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSDictionary *)getUploadTaskAsDictionary:(FIRStorageTaskSnapshot *)task {
|
|
||||||
NSString *downloadUrl = [task.metadata.downloadURL absoluteString];
|
|
||||||
FIRStorageMetadata *metadata = [[FIRStorageMetadata alloc] initWithDictionary:[task.metadata dictionaryRepresentation]];
|
|
||||||
return @{@"bytesTransferred": @(task.progress.completedUnitCount), @"downloadUrl": downloadUrl != nil ? downloadUrl : [NSNull null], @"metadata": metadata != nil ? metadata : [NSNull null], @"ref": task.reference.fullPath, @"state": [self getTaskStatus:task.status], @"totalBytes": @(task.progress.totalUnitCount)};
|
|
||||||
}
|
|
||||||
|
|
||||||
- (FIRStorageMetadata *)buildMetadataFromMap:(NSDictionary *)metadata {
|
|
||||||
NSMutableDictionary *metaCopy = [metadata mutableCopy];
|
|
||||||
[metaCopy removeObjectForKey:@"customMetadata"];
|
|
||||||
FIRStorageMetadata *storageMetadata = [[FIRStorageMetadata alloc] initWithDictionary:metaCopy];
|
|
||||||
storageMetadata.customMetadata = [metadata[@"customMetadata"] mutableCopy];
|
|
||||||
return storageMetadata;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSString *)getTaskStatus:(FIRStorageTaskStatus)status {
|
|
||||||
if (status == FIRStorageTaskStatusResume || status == FIRStorageTaskStatusProgress) {
|
|
||||||
return @"running";
|
|
||||||
} else if (status == FIRStorageTaskStatusPause) {
|
|
||||||
return @"paused";
|
|
||||||
} else if (status == FIRStorageTaskStatusSuccess) {
|
|
||||||
return @"success";
|
|
||||||
} else if (status == FIRStorageTaskStatusFailure) {
|
|
||||||
return @"error";
|
|
||||||
} else {
|
|
||||||
return @"unknown";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSString *)getPathForDirectory:(int)directory {
|
|
||||||
NSArray *paths = NSSearchPathForDirectoriesInDomains(directory, NSUserDomainMask, YES);
|
|
||||||
return [paths firstObject];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSDictionary *)constantsToExport {
|
|
||||||
return @{@"MAIN_BUNDLE_PATH": [[NSBundle mainBundle] bundlePath], @"CACHES_DIRECTORY_PATH": [self getPathForDirectory:NSCachesDirectory], @"DOCUMENT_DIRECTORY_PATH": [self getPathForDirectory:NSDocumentDirectory], @"EXTERNAL_DIRECTORY_PATH": [NSNull null], @"EXTERNAL_STORAGE_DIRECTORY_PATH": [NSNull null], @"TEMP_DIRECTORY_PATH": NSTemporaryDirectory(), @"LIBRARY_DIRECTORY_PATH": [self getPathForDirectory:NSLibraryDirectory], @"FILETYPE_REGULAR": NSFileTypeRegular, @"FILETYPE_DIRECTORY": NSFileTypeDirectory};
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSArray<NSString *> *)supportedEvents {
|
|
||||||
return @[STORAGE_EVENT, STORAGE_ERROR];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)sendJSError:(NSString *)appName error:(NSError *)error path:(NSString *)path {
|
|
||||||
NSDictionary *evt = @{@"path": path, @"message": [error debugDescription]};
|
|
||||||
[self sendJSEvent:appName type:STORAGE_ERROR path:path title:STORAGE_ERROR props:evt];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)sendJSEvent:(NSString *)appName type:(NSString *)type path:(NSString *)path title:(NSString *)title props:(NSDictionary *)props {
|
|
||||||
@try {
|
|
||||||
[self sendEventWithName:type body:@{@"eventName": title, @"appName": appName, @"path": path, @"body": props}];
|
|
||||||
} @catch (NSException *err) {
|
|
||||||
NSLog(@"An error occurred in sendJSEvent: %@", [err debugDescription]);
|
|
||||||
NSLog(@"Tried to send: %@ with %@", title, props);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
Loading…
Reference in New Issue