Skip to content

Working in React Native 0.62.2 (iOS) #209

@ortonomy

Description

@ortonomy

Really appreciate the work that @jvandenaardweg went to over in issue #182 to try and help everybody out, but I thought I'd open this here to share a solution that doesn't need to modify build settings or fork any repos. I would suggest just writing your own share extension and using some skeleton code from this issue. You can still follow most of the install steps, but without importing this lib, and a few additional steps.

  1. Create your iOS share extension (in current install steps)

In X-Code, having opened up your project

File > New > Target -> ShareExtension

  1. In your podfile, set your target to use all of the react-native libs
# right below use_native_modules!

# for share extension
  target 'name_of_your_share' do
    use_native_modules!
    inherit! :complete
  end
  1. Replace the default code with an extension of UIViewController and implement viewDidLoad to load react native

MyShareExtension.h

#import <UIKit/UIKit.h>
#import <React/RCTBridgeModule.h>

@interface MyShareExtension : UIViewController<RCTBridgeModule>
@end

MyShareExtension.m

#import "MyShareExtension.h"
#import <React/RCTRootView.h>
#import <React/RCTBundleURLProvider.h>

NSExtensionContext* extensionContext;

@implementation MyShareExtension {
}

RCT_EXPORT_MODULE();

- (void) viewDidLoad {
  [super viewDidLoad];


  extensionContext = self.extensionContext; // global for later call to async promise
  
  // set up react native instance
  NSURL *jsCodeLocation;
  
  jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];
  RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL: jsCodeLocation
                                               moduleName: @"your_target_react_native_app" // AppRegistry.registerComponent("your_target_react_native_app", App)
                                               initialProperties: nil
                                                   launchOptions: nil];
  
  UIViewController *rootViewController = [UIViewController alloc];
  rootViewController.view = rootView;
  [self addChildViewController: rootViewController];
  
  rootViewController.view.frame = self.view.bounds;
  rootViewController.view.translatesAutoresizingMaskIntoConstraints = false;
  [[self view] addSubview:rootViewController.view];
  NSArray* constraints = [NSArray arrayWithObjects:
                          [rootViewController.view.leftAnchor constraintEqualToAnchor: self.view.leftAnchor],
                          [rootViewController.view.rightAnchor constraintEqualToAnchor: self.view.rightAnchor],
                          [rootViewController.view.topAnchor constraintEqualToAnchor: self.view.topAnchor],
                          [rootViewController.view.bottomAnchor constraintEqualToAnchor: self.view.bottomAnchor], nil
                        ];
  [NSLayoutConstraint activateConstraints:constraints];
  
  [self didMoveToParentViewController: self];
  
}
  1. Pretty much copy the content from this lib and expose methods to react native to fetch the parameters capturing from the share extension
// top of file
#import <MobileCoreServices/MobileCoreServices.h>



#define URL_IDENTIFIER (NSString *)kUTTypeURL
#define IMAGE_IDENTIFIER (NSString *)kUTTypeImage

// ... more code


RCT_REMAP_METHOD(getParameters,
                 resolver:(RCTPromiseResolveBlock)resolve
                 rejecter:(RCTPromiseRejectBlock)reject)
{
    [self extractData: extensionContext withCallback:^(NSDictionary* result, NSException* err) {
        if(err) {
            reject(@"error", err.description, nil);
        } else {
            resolve(result);
        }
    }];
}

- (void) extractData: (NSExtensionContext *)context withCallback:(void(^)(NSDictionary* result, NSException *exception))callback {
  @try {

    
    // get items shared
    NSExtensionItem *item = [context.inputItems firstObject];
    __block NSItemProvider *provider = item.attachments.firstObject;
    
    if ([provider hasItemConformingToTypeIdentifier:IMAGE_IDENTIFIER]){
      [provider loadItemForTypeIdentifier:IMAGE_IDENTIFIER options:nil completionHandler:^(id<NSSecureCoding> item, NSError *error) {
        NSURL *url = (NSURL *)item;
        NSDictionary *result = @{@"data": [url absoluteString], @"extension": [[[url absoluteString] pathExtension] lowercaseString], @"type": @"image"};
        if(callback) {
            callback(result, nil);
        }
      }];
      return;
    }
    
    if([provider hasItemConformingToTypeIdentifier:URL_IDENTIFIER]) {
      [provider loadItemForTypeIdentifier:URL_IDENTIFIER options:nil completionHandler:^(id<NSSecureCoding> item, NSError *error) {
        NSURL *url = (NSURL *)item;
        NSDictionary *result = @{@"data": [url absoluteString], @"type": @"url"};
        if(callback) {
            callback(result, nil);
        }
      }];
      
      return;
      
    }
    
    if(callback) {
      callback(nil, [NSException exceptionWithName:@"Error" reason:@"couldn't find provider" userInfo:nil]);
    }
  }
  @catch (NSException *exception) {
  }
}

Conclusion

You still need to follow the install steps to restrict the types that your app can receive (The plist) and ensure the transport settings let you connect to the react native bundler server, but these were the most critical steps to getting my share extension working.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions