From 51c811c495aecb433606a016ad67a438c717c97a Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 21 Dec 2022 16:01:32 +0400 Subject: [PATCH 1/7] feat: wip macos support --- ios/RCTConvert+RNDocumentPicker.h | 3 ++ ios/RCTConvert+RNDocumentPicker.m | 3 ++ ios/RNDocumentPicker.h | 2 ++ ios/RNDocumentPicker.m | 50 ++++++++++++++++++++-------- react-native-document-picker.podspec | 5 ++- 5 files changed, 48 insertions(+), 15 deletions(-) diff --git a/ios/RCTConvert+RNDocumentPicker.h b/ios/RCTConvert+RNDocumentPicker.h index 7d698507..da9f48bb 100644 --- a/ios/RCTConvert+RNDocumentPicker.h +++ b/ios/RCTConvert+RNDocumentPicker.h @@ -1,5 +1,8 @@ #import + +#if __has_include() @import UIKit; +#endif @interface RCTConvert(RNDocumentPicker) diff --git a/ios/RCTConvert+RNDocumentPicker.m b/ios/RCTConvert+RNDocumentPicker.m index 55b6fe20..a39aea4a 100644 --- a/ios/RCTConvert+RNDocumentPicker.m +++ b/ios/RCTConvert+RNDocumentPicker.m @@ -1,5 +1,6 @@ #import "RCTConvert+RNDocumentPicker.h" +#if __has_include() @implementation RCTConvert (RNDocumentPicker) // TODO how to de-duplicate from https://github.com/facebook/react-native/blob/v0.66.0/React/Views/RCTModalHostViewManager.m? RCT_ENUM_CONVERTER( @@ -26,3 +27,5 @@ @implementation RCTConvert (RNDocumentPicker) integerValue) @end + +#endif diff --git a/ios/RNDocumentPicker.h b/ios/RNDocumentPicker.h index 695da83c..fad323a0 100644 --- a/ios/RNDocumentPicker.h +++ b/ios/RNDocumentPicker.h @@ -1,6 +1,8 @@ #import +#if __has_include() @import UIKit; +#endif @interface RNDocumentPicker : NSObject diff --git a/ios/RNDocumentPicker.m b/ios/RNDocumentPicker.m index 51e6d5b7..7195ce15 100644 --- a/ios/RNDocumentPicker.m +++ b/ios/RNDocumentPicker.m @@ -1,6 +1,8 @@ #import "RNDocumentPicker.h" +#if __has_include() #import +#endif #import #import @@ -22,11 +24,19 @@ static NSString *const FIELD_SIZE = @"size"; +#if __has_include() @interface RNDocumentPicker () @end +#else +@interface RNDocumentPicker () +@end + +#endif @implementation RNDocumentPicker { +#if __has_include() UIDocumentPickerMode mode; +#endif NSString *copyDestination; RNCPromiseWrapper* promiseWrapper; NSMutableArray *urlsInOpenMode; @@ -66,29 +76,39 @@ - (dispatch_queue_t)methodQueue resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { - mode = options[@"mode"] && [options[@"mode"] isEqualToString:@"open"] ? UIDocumentPickerModeOpen : UIDocumentPickerModeImport; - copyDestination = options[@"copyTo"]; - UIModalPresentationStyle presentationStyle = [RCTConvert UIModalPresentationStyle:options[@"presentationStyle"]]; - UIModalTransitionStyle transitionStyle = [RCTConvert UIModalTransitionStyle:options[@"transitionStyle"]]; - [promiseWrapper setPromiseWithInProgressCheck:resolve rejecter:reject fromCallSite:@"pick"]; - NSArray *allowedUTIs = [RCTConvert NSArray:options[OPTION_TYPE]]; - UIDocumentPickerViewController *documentPicker = [[UIDocumentPickerViewController alloc] initWithDocumentTypes:allowedUTIs inMode:mode]; + #if __has_include() + mode = options[@"mode"] && [options[@"mode"] isEqualToString:@"open"] ? UIDocumentPickerModeOpen : UIDocumentPickerModeImport; + copyDestination = options[@"copyTo"]; + UIModalPresentationStyle presentationStyle = [RCTConvert UIModalPresentationStyle:options[@"presentationStyle"]]; + UIModalTransitionStyle transitionStyle = [RCTConvert UIModalTransitionStyle:options[@"transitionStyle"]]; + [promiseWrapper setPromiseWithInProgressCheck:resolve rejecter:reject fromCallSite:@"pick"]; - documentPicker.modalPresentationStyle = presentationStyle; - documentPicker.modalTransitionStyle = transitionStyle; + NSArray *allowedUTIs = [RCTConvert NSArray:options[OPTION_TYPE]]; + UIDocumentPickerViewController *documentPicker = [[UIDocumentPickerViewController alloc] initWithDocumentTypes:allowedUTIs inMode:mode]; - documentPicker.delegate = self; - documentPicker.presentationController.delegate = self; + documentPicker.modalPresentationStyle = presentationStyle; + documentPicker.modalTransitionStyle = transitionStyle; - documentPicker.allowsMultipleSelection = [RCTConvert BOOL:options[OPTION_MULTIPLE]]; + documentPicker.delegate = self; + documentPicker.presentationController.delegate = self; - UIViewController *rootViewController = RCTPresentedViewController(); + documentPicker.allowsMultipleSelection = [RCTConvert BOOL:options[OPTION_MULTIPLE]]; - [rootViewController presentViewController:documentPicker animated:YES completion:nil]; + UIViewController *rootViewController = RCTPresentedViewController(); + + [rootViewController presentViewController:documentPicker animated:YES completion:nil]; + #else + NSOpenPanel *op = [NSOpenPanel openPanel]; + op.canChooseFiles = YES; + op.canChooseDirectories = YES; + [op runModal]; + // self.txtFilePath.stringValue = [op.URLs firstObject]; + #endif } +#if __has_include() - (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentsAtURLs:(NSArray *)urls { NSMutableArray *results = [NSMutableArray array]; @@ -239,4 +259,6 @@ - (void)rejectAsUserCancellationError [promiseWrapper reject:@"user canceled the document picker" withCode:E_DOCUMENT_PICKER_CANCELED withError:error]; } +#endif @end + diff --git a/react-native-document-picker.podspec b/react-native-document-picker.podspec index 91557137..56f20de0 100644 --- a/react-native-document-picker.podspec +++ b/react-native-document-picker.podspec @@ -10,7 +10,10 @@ Pod::Spec.new do |s| s.license = package["license"] s.authors = package["author"] - s.platforms = { :ios => "11.0" } + s.ios.deployment_target = '11.0' + # s.tvos.deployment_target = '9.2' + s.osx.deployment_target = '10.10' + s.source = { :git => "https://github.com/rnmods/react-native-document-picker.git", :tag => "v#{s.version}" } s.source_files = "ios/**/*.{h,m,mm}" From c3ce67bd64ac4ec41dcbd7c67bb9ddb6ec97f2c5 Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 21 Dec 2022 16:22:11 +0400 Subject: [PATCH 2/7] feat: macos minimal implementation --- ios/RNDocumentPicker.m | 75 +++++++++++++++++++++++++++--------------- 1 file changed, 48 insertions(+), 27 deletions(-) diff --git a/ios/RNDocumentPicker.m b/ios/RNDocumentPicker.m index 7195ce15..ec1d4a55 100644 --- a/ios/RNDocumentPicker.m +++ b/ios/RNDocumentPicker.m @@ -99,40 +99,38 @@ - (dispatch_queue_t)methodQueue [rootViewController presentViewController:documentPicker animated:YES completion:nil]; #else + [promiseWrapper setPromiseWithInProgressCheck:resolve rejecter:reject fromCallSite:@"pick"]; NSOpenPanel *op = [NSOpenPanel openPanel]; op.canChooseFiles = YES; op.canChooseDirectories = YES; [op runModal]; - // self.txtFilePath.stringValue = [op.URLs firstObject]; - #endif -} - -#if __has_include() -- (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentsAtURLs:(NSArray *)urls -{ - NSMutableArray *results = [NSMutableArray array]; - for (id url in urls) { - NSError *error; - NSMutableDictionary *result = [self getMetadataForUrl:url error:&error]; - if (result) { - [results addObject:result]; - } else { - [promiseWrapper reject:E_INVALID_DATA_RETURNED withError:error]; - return; + NSMutableArray *results = [NSMutableArray array]; + for (id url in op.URLs) { + NSError *error; + NSMutableDictionary *result = [self getMetadataForUrl:url error:&error]; + if (result) { + [results addObject:result]; + } else { + [promiseWrapper reject:E_INVALID_DATA_RETURNED withError:error]; + return; + } } - } - - [promiseWrapper resolve:results]; +// NSLog(url); + [promiseWrapper resolve:results]; + // self.txtFilePath.stringValue = ; + #endif } - (NSMutableDictionary *)getMetadataForUrl:(NSURL *)url error:(NSError **)error { __block NSMutableDictionary *result = [NSMutableDictionary dictionary]; - if (mode == UIDocumentPickerModeOpen) { - [urlsInOpenMode addObject:url]; - } +#if __has_include() + if (mode == UIDocumentPickerModeOpen) { + [urlsInOpenMode addObject:url]; + } +#endif // TODO handle error [url startAccessingSecurityScopedResource]; @@ -143,6 +141,7 @@ - (NSMutableDictionary *)getMetadataForUrl:(NSURL *)url error:(NSError **)error // TODO double check this implemenation, see eg. https://developer.apple.com/documentation/foundation/nsfilecoordinator/1412420-prepareforreadingitemsaturls [coordinator coordinateReadingItemAtURL:url options:NSFileCoordinatorReadingResolvesSymbolicLink error:&fileError byAccessor:^(NSURL *newURL) { // If the coordinated operation fails, then the accessor block never runs +#if __has_include() result[FIELD_URI] = ((mode == UIDocumentPickerModeOpen) ? url : newURL).absoluteString; NSError *copyError; @@ -154,7 +153,8 @@ - (NSMutableDictionary *)getMetadataForUrl:(NSURL *)url error:(NSError **)error result[FIELD_COPY_ERR] = copyError.localizedDescription; result[FIELD_FILE_COPY_URI] = [NSNull null]; } - + +#endif result[FIELD_NAME] = newURL.lastPathComponent; NSError *attributesError = nil; @@ -180,10 +180,12 @@ - (NSMutableDictionary *)getMetadataForUrl:(NSURL *)url error:(NSError **)error result[FIELD_TYPE] = [NSNull null]; } }]; - - if (mode != UIDocumentPickerModeOpen) { - [url stopAccessingSecurityScopedResource]; - } + +#if __has_include() + if (mode != UIDocumentPickerModeOpen) { + [url stopAccessingSecurityScopedResource]; + } +#endif if (fileError) { *error = fileError; @@ -193,6 +195,25 @@ - (NSMutableDictionary *)getMetadataForUrl:(NSURL *)url error:(NSError **)error } } +#if __has_include() +- (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentsAtURLs:(NSArray *)urls +{ + NSMutableArray *results = [NSMutableArray array]; + for (id url in urls) { + NSError *error; + NSMutableDictionary *result = [self getMetadataForUrl:url error:&error]; + if (result) { + [results addObject:result]; + } else { + [promiseWrapper reject:E_INVALID_DATA_RETURNED withError:error]; + return; + } + } + + [promiseWrapper resolve:results]; +} + + RCT_EXPORT_METHOD(releaseSecureAccess:(NSArray *)uris resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) From cbd19b8c1861bb53b4650168a073e200b263b019 Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 21 Dec 2022 16:31:31 +0400 Subject: [PATCH 3/7] feat: add more fixes --- ios/RCTConvert+RNDocumentPicker.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ios/RCTConvert+RNDocumentPicker.h b/ios/RCTConvert+RNDocumentPicker.h index da9f48bb..69eae4fe 100644 --- a/ios/RCTConvert+RNDocumentPicker.h +++ b/ios/RCTConvert+RNDocumentPicker.h @@ -6,7 +6,8 @@ @interface RCTConvert(RNDocumentPicker) +#if __has_include() + (UIModalPresentationStyle)UIModalPresentationStyle:(NSString*)value; + (UIModalTransitionStyle)UIModalTransitionStyle:(NSString*)value; - +#endif @end From 05416b654a7e7538c9655cca78a2b100b0db9d47 Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 21 Dec 2022 16:39:53 +0400 Subject: [PATCH 4/7] feat: add more fixes --- ios/RNDocumentPicker.m | 2 -- 1 file changed, 2 deletions(-) diff --git a/ios/RNDocumentPicker.m b/ios/RNDocumentPicker.m index ec1d4a55..5c9ed507 100644 --- a/ios/RNDocumentPicker.m +++ b/ios/RNDocumentPicker.m @@ -116,9 +116,7 @@ - (dispatch_queue_t)methodQueue return; } } -// NSLog(url); [promiseWrapper resolve:results]; - // self.txtFilePath.stringValue = ; #endif } From 2c7a6a53bdebc51667c1c68fd0626adf002ed1a5 Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 21 Dec 2022 16:48:58 +0400 Subject: [PATCH 5/7] feat: transfer more functionality --- ios/RNDocumentPicker.m | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/ios/RNDocumentPicker.m b/ios/RNDocumentPicker.m index 5c9ed507..65e9620d 100644 --- a/ios/RNDocumentPicker.m +++ b/ios/RNDocumentPicker.m @@ -76,15 +76,16 @@ - (dispatch_queue_t)methodQueue resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { + + copyDestination = options[@"copyTo"]; + NSArray *allowedUTIs = [RCTConvert NSArray:options[OPTION_TYPE]]; #if __has_include() mode = options[@"mode"] && [options[@"mode"] isEqualToString:@"open"] ? UIDocumentPickerModeOpen : UIDocumentPickerModeImport; - copyDestination = options[@"copyTo"]; UIModalPresentationStyle presentationStyle = [RCTConvert UIModalPresentationStyle:options[@"presentationStyle"]]; UIModalTransitionStyle transitionStyle = [RCTConvert UIModalTransitionStyle:options[@"transitionStyle"]]; [promiseWrapper setPromiseWithInProgressCheck:resolve rejecter:reject fromCallSite:@"pick"]; - NSArray *allowedUTIs = [RCTConvert NSArray:options[OPTION_TYPE]]; UIDocumentPickerViewController *documentPicker = [[UIDocumentPickerViewController alloc] initWithDocumentTypes:allowedUTIs inMode:mode]; documentPicker.modalPresentationStyle = presentationStyle; @@ -93,8 +94,6 @@ - (dispatch_queue_t)methodQueue documentPicker.delegate = self; documentPicker.presentationController.delegate = self; - documentPicker.allowsMultipleSelection = [RCTConvert BOOL:options[OPTION_MULTIPLE]]; - UIViewController *rootViewController = RCTPresentedViewController(); [rootViewController presentViewController:documentPicker animated:YES completion:nil]; @@ -103,6 +102,7 @@ - (dispatch_queue_t)methodQueue NSOpenPanel *op = [NSOpenPanel openPanel]; op.canChooseFiles = YES; op.canChooseDirectories = YES; + op.allowsMultipleSelection = [RCTConvert BOOL:options[OPTION_MULTIPLE]]; [op runModal]; NSMutableArray *results = [NSMutableArray array]; @@ -139,9 +139,8 @@ - (NSMutableDictionary *)getMetadataForUrl:(NSURL *)url error:(NSError **)error // TODO double check this implemenation, see eg. https://developer.apple.com/documentation/foundation/nsfilecoordinator/1412420-prepareforreadingitemsaturls [coordinator coordinateReadingItemAtURL:url options:NSFileCoordinatorReadingResolvesSymbolicLink error:&fileError byAccessor:^(NSURL *newURL) { // If the coordinated operation fails, then the accessor block never runs -#if __has_include() - result[FIELD_URI] = ((mode == UIDocumentPickerModeOpen) ? url : newURL).absoluteString; + result[FIELD_URI] = url.absoluteString; NSError *copyError; NSString *maybeFileCopyPath = copyDestination ? [RNDocumentPicker copyToUniqueDestinationFrom:newURL usingDestinationPreset:copyDestination error:copyError].absoluteString : nil; @@ -151,8 +150,7 @@ - (NSMutableDictionary *)getMetadataForUrl:(NSURL *)url error:(NSError **)error result[FIELD_COPY_ERR] = copyError.localizedDescription; result[FIELD_FILE_COPY_URI] = [NSNull null]; } - -#endif + result[FIELD_NAME] = newURL.lastPathComponent; NSError *attributesError = nil; @@ -210,6 +208,7 @@ - (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocum [promiseWrapper resolve:results]; } +#endif RCT_EXPORT_METHOD(releaseSecureAccess:(NSArray *)uris @@ -261,6 +260,7 @@ + (NSURL *)getDirectoryForFileCopy:(NSString *)copyToDirectory return [NSURL fileURLWithPath:NSTemporaryDirectory() isDirectory:YES]; } +#if __has_include() - (void)documentPickerWasCancelled:(UIDocumentPickerViewController *)controller { [self rejectAsUserCancellationError]; @@ -270,6 +270,7 @@ - (void)presentationControllerDidDismiss:(UIPresentationController *)presentatio { [self rejectAsUserCancellationError]; } +#endif - (void)rejectAsUserCancellationError { @@ -278,6 +279,5 @@ - (void)rejectAsUserCancellationError [promiseWrapper reject:@"user canceled the document picker" withCode:E_DOCUMENT_PICKER_CANCELED withError:error]; } -#endif @end From 526250c566cb824d651df39e6a06f13eb9896934 Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 21 Dec 2022 16:59:34 +0400 Subject: [PATCH 6/7] chore: update readme --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index debcb9f2..e1023ee3 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,8 @@ If you want to ask questions, we opened [GH discussions](https://github.com/rnmo A React Native wrapper for: -- Apple's `UIDocumentPickerViewController` +- iOS `UIDocumentPickerViewController` +- OSX `NSOpenPanel` - Android's `Intent.ACTION_GET_CONTENT` - Windows `Windows.Storage.Pickers` From feaf6541e6aa9029d7fda38f088412a373502c59 Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 21 Dec 2022 17:27:06 +0400 Subject: [PATCH 7/7] chore: update readme --- README.md | 2 +- react-native-document-picker.podspec | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e1023ee3..d23c7e87 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ A React Native wrapper for: -Requires RN >= 0.63, Android 5.0+ and iOS 11+ +Requires RN >= 0.63, Android 5.0+, iOS 11+ and OSX 10.5 # Table of Contents diff --git a/react-native-document-picker.podspec b/react-native-document-picker.podspec index 56f20de0..37d73e88 100644 --- a/react-native-document-picker.podspec +++ b/react-native-document-picker.podspec @@ -12,7 +12,7 @@ Pod::Spec.new do |s| s.ios.deployment_target = '11.0' # s.tvos.deployment_target = '9.2' - s.osx.deployment_target = '10.10' + s.osx.deployment_target = '10.5' s.source = { :git => "https://github.com/rnmods/react-native-document-picker.git", :tag => "v#{s.version}" }