Building a mobile share extension for a React Native app

October 09, 2017 0 Comments

Building a mobile share extension for a React Native app

 

 

I recently started working on building a share extension so I decided to share my work with the rest of the world. In this tutorial, I’m assuming you are building an app in React Native. You could either build a react native extension for both, or a native iOS and a native Android extension. I’ll be talking about building the native iOS and the React Native extension for both.

The only reason I’m going to review the native iOS extension is because it’s more common to want to leverage iOS UI and standard dialogs in the share extension. On the other hand, Android is far less standardized.

Building a share extension in React Native has the benefit of mostly working on both platforms without nearly as much additional overhead. On top of that, you’re working in Javascript, which [to me, a humble web dev] is easier and faster than working with Objective-C or Swift.

When building a share extension, it’s generally expected that you have authentication around your app in some form or fashion. Though since it’s not really required, I’ll gloss over that pretty quickly right now.

The practice I’ve found most effective, is to first fetch oauth credentials when logging in on the app and then store those in an app group bucket for iOS or use Shared Preferences on Android. You then fetch them in a similar fashion when making your request.

First, we need to add a Share Extension project to each build.

Open your project file in Xcode and add a new “target”, select share extension for the type, and name it. You’ll have a ShareViewController object and header file generated within your project. Open your project file and select your share extension target. Open the “Link binaries with libraries” dropdown and hit add. I went ahead and added all of the libraries under “Workspace” except for the Tv-OS.

In your info.plist file, make sure you add the following:

  <key>NSExtension</key>
<dict>
<key>NSExtensionAttributes</key>
<dict>
<key>NSExtensionActivationRule</key>
<dict>
<key>NSExtensionActivationSupportsWebURLWithMaxCount</key>
<integer>1</integer>
<key>NSExtensionActivationSupportsImageWithMaxCount</key>
<integer>10</integer>
<key>NSExtensionActivationSupportsText</key>
<true/>
</dict>
</dict>
<key>NSExtensionMainStoryboard</key>
<string>MainInterface</string>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.share-services</string>
</dict>

This defines the data types that will activate your share extension.

If you’re using app groups to share credentials, this is where you would enable app groups in both the main target app and the share extension app. You’ll need to get on your developer.apple.com account to add the capability to your certificate and create the group.

Right click the java folder of your existing project and select new → package and add your share package (I’d recommend [your existing package].share for the name). You’ll want to make sure that in your AndroidManifest.xml you see your activity listed. Inside of it, you should add an intent filter that looks like this:

<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="image/" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND_MULTIPLE" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="image/
" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>

The first filter opens the view, the second one allows you to receive data. Note the mimeType data field and SEND vs. SENDMULTIPLE.

Now that we have the projects set up, we can dive into the implementation for iOS, then React Native on iOS, and lastly React Native on Android. I won’t go through much React Native code because you can build that however you want.

Let’s start by opening the given ShareViewController.h and having the class inherit from SLComposeServiceViewController. You’ll need to #import <Social/Social.h> and then in your .m file implement the following:

- (BOOL)isContentValid {}
- (void)didSelectPost {}
- (void)loadView {}
  • isContentValid is called to determine if the post button is tappable.
  • didSelectPost is called when the user presses the post button
  • loadView is called once the dialog shows up and is where we load data from the app groups bucket.

The default here is going to have a cancel and a post button at the top and a text dialog and a preview of the content if it isn’t just plaintext.

In our loadView we want to do the following:

Defining didSelectPost should make a POST request to your server to share the data. I’d recommend adding cocoa pods to your app and using the AFNetworking module. That’s a majority of what you need to build out your share extension in objective c.

I’m going to assume that you’ve already built the React Native component. It should be pretty much the same as any other component. Ignore the above changes to the .h and .m files.

Open your ShareViewController.h and make sure the class inherits from UIViewController<RCTBridgeModule>. Then open up the .m file and add the following methods:

Everything is pretty similar to a normal React Native app, except I’ve used share.ios for the bundle URL instead of index.ios, so you should create a share.ios.js file and import your component and register it using the AppRegistry. The data method will fetch the extension data, and the close method will close your extension. Also, don’t forget the RCTEXPORT_MODULE(); call.

In your React Native component, you can now:

import { NativeModules } from 'react-native';
const ShareExtension = NativeModules.ShareExtension;

Then, calling ShareExtension.data and ShareExtension.close will fetch the data and close the extension respectively.

You will also want a method that loads the context like this one here. The extractDataFromContext method referenced in the gist above should be the similar to the loadContext method from the iOS Native section, except it will pass the data into the callback. Here’s the signature:

- (void)extractDataFromContext:(NSExtensionContext )context withCallback:(void(^)(NSString *value, NSString contentType, NSException *exception))callback {}

That should be about everything you need to do to get your react native component showing up as a share extension.

For the react native extension on Android, we add ShareActivity.java , ShareApplication.java , ShareModule.java , and SharePackage.java .

ShareActivity.java

This file just needs a definition for the getComponentName method like such:

You’ll want to replace “MyShareExtension” with the name of your React Native component here.

ShareModule.java

This just defines the name that we export with, and the close method. And here is an example of the processIntent method.

SharePackage.java

Imports the share module and exports it as a React Native module.

ShareApplication.java

This should be the same as any other React Native application. You can copy the base code from a React Native app. The only difference is you should only need to add your SharePackage from above to the list of ReactNative packages imported.

There you have it. A 10,000 feet walkthrough of building a share extension for a React Native app. I don’t claim to be an outstanding mobile dev, and this was one of my first attempts at working with Objective-C in case you haven’t noticed. Let me know if you have issues that I can assist with, or if there are any errors in the information I shared above. Thanks!


Tag cloud