11#import " ReactNativeShareExtension.h"
22#import " React/RCTRootView.h"
33
4- #define ITEM_IDENTIFIER @" public.url"
5- #define IMAGE_IDENTIFIER @" public.image"
6-
74NSExtensionContext * extensionContext;
85
96@implementation ReactNativeShareExtension {
@@ -34,16 +31,18 @@ - (void)viewDidLoad {
3431}
3532
3633
37- RCT_EXPORT_METHOD (close) {
38- [extensionContext completeRequestReturningItems: nil
39- completionHandler: nil ];
34+ RCT_EXPORT_METHOD (close:(NSString *)appGroupId) {
35+ [self cleanUpTempFiles: appGroupId];
36+ [extensionContext completeRequestReturningItems: nil
37+ completionHandler: nil ];
4038}
4139
4240RCT_REMAP_METHOD (data,
43- resolver:(RCTPromiseResolveBlock)resolve
44- rejecter:(RCTPromiseRejectBlock)reject)
41+ appGroupId: (NSString *)appGroupId
42+ resolver:(RCTPromiseResolveBlock)resolve
43+ rejecter:(RCTPromiseRejectBlock)reject)
4544{
46- [self extractDataFromContext: extensionContext withCallback: ^(NSArray * items ,NSException * err) {
45+ [self extractDataFromContext: extensionContext withAppGroup: appGroupId andCallback: ^(NSArray * items ,NSError * err) {
4746 if (items == nil ) {
4847 resolve (nil );
4948 return ;
@@ -53,80 +52,215 @@ - (void)viewDidLoad {
5352}
5453
5554RCT_REMAP_METHOD (dataMulti,
55+ appGroupId: (NSString *)appGroupId
5656 resolverMulti:(RCTPromiseResolveBlock)resolve
5757 rejecterMulti:(RCTPromiseRejectBlock)reject)
5858{
59- [self extractDataFromContext: extensionContext withCallback: ^(NSArray * items ,NSException * err) {
59+ [self extractDataFromContext: extensionContext withAppGroup: appGroupId andCallback: ^(NSArray * items ,NSError * err) {
60+ if (err) {
61+ reject (@" dataMulti" , @" Failed to extract attachment content" , err);
62+ return ;
63+ }
6064 resolve (items);
6165 }];
6266}
6367
64- typedef void (^ProviderCallback)(NSURL *url , NSString *contentType, NSException *exception );
68+ typedef void (^ProviderCallback)(NSString *content , NSString *contentType, BOOL owner, NSError *err );
6569
66- - (void )extractDataFromContext : (NSExtensionContext *)context withCallback : ( void (^)(NSArray *items ,NSException *exception ))callback {
70+ - (void )extractDataFromContext : (NSExtensionContext *)context withAppGroup : ( NSString *) appGroupId andCallback : ( void (^)(NSArray *items ,NSError *err ))callback {
6771 @try {
6872 NSExtensionItem *item = [context.inputItems firstObject ];
6973 NSArray *attachments = item.attachments ;
7074 NSMutableArray *items = [[NSMutableArray alloc ] init ];
71-
75+
7276 __block int attachmentIdx = 0 ;
7377 __block ProviderCallback providerCb = nil ;
74- providerCb = ^ void (NSURL *url, NSString *contentType, NSException *exception) {
75- if (exception) {
76- callback (nil , exception);
78+ __block __weak ProviderCallback weakProviderCb = nil ;
79+ providerCb = ^ void (NSString *content, NSString *contentType, BOOL owner, NSError *err) {
80+ if (err) {
81+ callback (nil , err);
7782 return ;
7883 }
79-
80- [items addObject: @{
81- @" type" : contentType,
82- @" value" : [url absoluteString ]
83- }];
84+
85+ if (content != nil ) {
86+ [items addObject: @{
87+ @" type" : contentType,
88+ @" value" : content,
89+ @" owner" : [NSNumber numberWithBool: owner],
90+ }];
91+ }
8492
8593 ++attachmentIdx;
8694 if (attachmentIdx == [attachments count ]) {
8795 callback (items, nil );
8896 } else {
89- [self extractDataFromProvider: attachments[attachmentIdx] withCallback: providerCb ];
97+ [self extractDataFromProvider: attachments[attachmentIdx] withAppGroup: appGroupId andCallback: weakProviderCb ];
9098 }
9199 };
92- [self extractDataFromProvider: attachments[0 ] withCallback: providerCb];
100+ weakProviderCb = providerCb;
101+ [self extractDataFromProvider: attachments[0 ] withAppGroup: appGroupId andCallback: providerCb];
93102 }
94- @catch (NSException *exception) {
95- callback (nil ,exception);
103+ @catch (NSException *exc) {
104+ NSError *error = [NSError errorWithDomain: @" fiftythree.paste" code: 1 userInfo: @{
105+ @" reason" : [exc description ]
106+ }];
107+ callback (nil , error);
96108 }
97109}
98110
99- - (void )extractDataFromProvider : (NSItemProvider *)provider withCallback : (void (^)(NSURL * url, NSString * contentType ,NSException *exception))callback {
100- NSItemProvider *urlProvider = nil ;
101- NSItemProvider *imageProvider = nil ;
102-
103- if ([provider hasItemConformingToTypeIdentifier: ITEM_IDENTIFIER]) {
104- urlProvider = provider;
105- }else if ([provider hasItemConformingToTypeIdentifier: IMAGE_IDENTIFIER]){
106- imageProvider = provider;
111+ - (void )extractDataFromProvider : (NSItemProvider *)provider withAppGroup : (NSString *) appGroupId andCallback : (void (^)(NSString * content, NSString * contentType, BOOL owner, NSError *err))callback {
112+
113+ if ([provider hasItemConformingToTypeIdentifier: @" public.image" ]) {
114+ [provider loadItemForTypeIdentifier: @" public.image" options: nil completionHandler: ^(id <NSSecureCoding , NSObject > item, NSError *error) {
115+ if (error) {
116+ callback (nil , nil , NO , error);
117+ return ;
118+ }
119+
120+ @try {
121+ if ([item isKindOfClass: NSURL .class]) {
122+ NSURL *url = (NSURL *)item;
123+ return callback ([url absoluteString ], @" public.image" , NO , nil );
124+ } else if ([item isKindOfClass: UIImage.class]) {
125+ UIImage *image = (UIImage *)item;
126+ NSString *fileName = [NSString stringWithFormat: @" %@ .jpg" , [[NSUUID UUID ] UUIDString ]];
127+ NSURL *tempContainerURL = [ReactNativeShareExtension tempContainerURL: appGroupId];
128+ if (tempContainerURL == nil ){
129+ return callback (nil , nil , NO , nil );
130+ }
131+
132+ NSURL *tempFileURL = [tempContainerURL URLByAppendingPathComponent: fileName];
133+ BOOL created = [UIImageJPEGRepresentation (image, 0.95 ) writeToFile: [tempFileURL path ] atomically: YES ];
134+ if (created) {
135+ return callback ([tempFileURL absoluteString ], @" public.image" , YES , nil );
136+ } else {
137+ return callback (nil , nil , NO , nil );
138+ }
139+ } else if ([item isKindOfClass: NSData .class]) {
140+ NSString *fileName = [NSString stringWithFormat: @" %@ .jpg" , [[NSUUID UUID ] UUIDString ]];
141+ NSData *data = (NSData *)item;
142+ UIImage *image = [UIImage imageWithData: data];
143+ NSURL *tempContainerURL = [ReactNativeShareExtension tempContainerURL: appGroupId];
144+ if (tempContainerURL == nil ){
145+ return callback (nil , nil , NO , nil );
146+ }
147+ NSURL *tempFileURL = [tempContainerURL URLByAppendingPathComponent: fileName];
148+ BOOL created = [UIImageJPEGRepresentation (image, 0.95 ) writeToFile: [tempFileURL path ] atomically: YES ];
149+ if (created) {
150+ return callback ([tempFileURL absoluteString ], @" public.image" , YES , nil );
151+ } else {
152+ return callback (nil , nil , NO , nil );
153+ }
154+ } else {
155+ // Do nothing, some type we don't support.
156+ return callback (nil , nil , NO , nil );
157+ }
158+ }
159+ @catch (NSException *exc) {
160+ NSError *error = [NSError errorWithDomain: @" fiftythree.paste" code: 2 userInfo: @{
161+ @" reason" : [exc description ]
162+ }];
163+ callback (nil , nil , NO , error);
164+ }
165+ }];
166+ return ;
107167 }
108-
109- if (urlProvider) {
110- [urlProvider loadItemForTypeIdentifier: ITEM_IDENTIFIER options: nil completionHandler: ^(id <NSSecureCoding > item, NSError *error) {
111- NSURL *url = (NSURL *)item;
112-
113- if (callback) {
114- callback (url,@" text/plain" ,nil );
168+
169+ if ([provider hasItemConformingToTypeIdentifier: @" public.file-url" ]) {
170+ [provider loadItemForTypeIdentifier: @" public.file-url" options: nil completionHandler: ^(id <NSSecureCoding , NSObject > item, NSError *error) {
171+ if (error) {
172+ callback (nil , nil , NO , error);
173+ return ;
174+ }
175+
176+ if ([item isKindOfClass: NSURL .class]) {
177+ return callback ([(NSURL *)item absoluteString ], @" public.file-url" , NO , nil );
178+ } else if ([item isKindOfClass: NSString .class]) {
179+ return callback ((NSString *)item, @" public.file-url" , NO , nil );
115180 }
181+ callback (nil , nil , NO , nil );
116182 }];
117- }else if (imageProvider){
118- [imageProvider loadItemForTypeIdentifier: IMAGE_IDENTIFIER options: nil completionHandler: ^(id <NSSecureCoding > item, NSError *error) {
119- NSURL *url = (NSURL *)item;
120-
121- if (callback) {
122- callback (url,[[[url absoluteString ] pathExtension ] lowercaseString ] ,nil );
183+ return ;
184+ }
185+
186+ if ([provider hasItemConformingToTypeIdentifier: @" public.url" ]) {
187+ [provider loadItemForTypeIdentifier: @" public.url" options: nil completionHandler: ^(id <NSSecureCoding , NSObject > item, NSError *error) {
188+ if (error) {
189+ callback (nil , nil , NO , error);
190+ return ;
191+ }
192+
193+ if ([item isKindOfClass: NSURL .class]) {
194+ return callback ([(NSURL *)item absoluteString ], @" public.url" , NO , nil );
195+ } else if ([item isKindOfClass: NSString .class]) {
196+ return callback ((NSString *)item, @" public.url" , NO , nil );
123197 }
124198 }];
125- } else {
126- if (callback) {
127- callback (nil , nil ,[NSException exceptionWithName: @" Error" reason: @" couldn't find provider" userInfo: nil ]);
199+ return ;
200+ }
201+
202+ if ([provider hasItemConformingToTypeIdentifier: @" public.plain-text" ]) {
203+ [provider loadItemForTypeIdentifier: @" public.plain-text" options: nil completionHandler: ^(id <NSSecureCoding , NSObject > item, NSError *error) {
204+ if (error) {
205+ callback (nil , nil , NO , error);
206+ return ;
207+ }
208+
209+ if ([item isKindOfClass: NSString .class]) {
210+ return callback ((NSString *)item, @" public.plain-text" , NO , nil );
211+ } else if ([item isKindOfClass: NSAttributedString .class]) {
212+ NSAttributedString *str = (NSAttributedString *)item;
213+ return callback ([str string ], @" public.plain-text" , NO , nil );
214+ } else if ([item isKindOfClass: NSData .class]) {
215+ NSString *str = [[NSString alloc ] initWithData: (NSData *)item encoding: NSUTF8StringEncoding];
216+ if (str) {
217+ return callback (str, @" public.plain-text" , NO , nil );
218+ } else {
219+ return callback (nil , nil , NO , nil );
220+ }
221+ } else {
222+ return callback (nil , nil , NO , nil );
223+ }
224+ }];
225+ return ;
226+ }
227+
228+ callback (nil , nil , NO , nil );
229+ }
230+
231+ + (NSURL *) tempContainerURL : (NSString *)appGroupId {
232+ NSFileManager *manager = [NSFileManager defaultManager ];
233+ NSURL *containerURL = [manager containerURLForSecurityApplicationGroupIdentifier: appGroupId];
234+ NSURL *tempDirectoryURL = [containerURL URLByAppendingPathComponent: @" shareTempItems" ];
235+ if (![manager fileExistsAtPath: [tempDirectoryURL path ]]) {
236+ NSError *err;
237+ [manager createDirectoryAtURL: tempDirectoryURL withIntermediateDirectories: YES attributes: nil error: &err];
238+ if (err) {
239+ return nil ;
128240 }
129241 }
242+
243+ return tempDirectoryURL;
244+ }
245+
246+ - (void ) cleanUpTempFiles : (NSString *)appGroupId {
247+ NSURL *tmpDirectoryURL = [ReactNativeShareExtension tempContainerURL: appGroupId];
248+ if (tmpDirectoryURL == nil ) {
249+ return ;
250+ }
251+
252+ NSFileManager *fileManager = [NSFileManager defaultManager ];
253+ NSError *error;
254+ NSArray *tmpFiles = [fileManager contentsOfDirectoryAtPath: [tmpDirectoryURL path ] error: &error];
255+ if (error) {
256+ return ;
257+ }
258+
259+ for (NSString *file in tmpFiles)
260+ {
261+ error = nil ;
262+ [fileManager removeItemAtPath: [[tmpDirectoryURL URLByAppendingPathComponent: file] path ] error: &error];
263+ }
130264}
131265
132266@end
0 commit comments