Building React Native 0.61.5 SDK to be used by native IOS applications (Swift)
I was trying to build a react native SDK to be used by native IOS and Android applications. Android was “Kinda” easy to do, but i faced some issues with IOS and thought sharing what i did may help somebody.
In this article we will build a simple react native SDK, and integrate that SDK into a native IOS application. The goal is to make it easy for the SDK user, we don’t want to bother the SDK users with npm or react native related stuff.
Lest get things started…
Create React Native Project
Either create a new react native project or use an existing one,
react-native init MySdk --version 0.61.5
Add Some dependencies
Naturally, we would need some dependencies for our SDK, dependancies that require linking. For this article we will be using:
yarn add react-native-fast-image
Finally install the pods
npx pod-install ios
Using FastImage
We are not going to do anything fancy on react native side, this is just a proof on concept. Just use the FastImage package that we added to make sure that it is working fine.
App.js
Create a native IOS wrapper for the React Native app
Running the created react native app is easy right? all you have to do is execute react-native run-ios. But running it inside another native app is a different story.
First, create a native IOS wrapper (wraps the react native app), that will contain all the required pods by react native. The wrapper will also configure and start the react app.
The idea is to create a native IOS framework, which will hide all react native complexity inside it. First, go to Xcode and create a new Framework project
In the next screen select a name for the framework and select Swift from the language dropdown. Let us name our framework MySdk-native.
After that open the terminal and cd into MySdk-native and execute:
pod init && pod install
We now have a CocoaPods framework, close Xcode and open generated MySdk-native.xcworkspace.
Next comes that hardest part, so buckle up!
Preparing the dependency pods
Now we have to prepare the pod dependancies for our SDK. These dependencies include all React Native dependencies in addition to any dependency that requires linking (such as react-native-fast-image).
React native
React native pods are local pods, because they are downloaded using npm or yarn. So one option is to embed these dependencies inside the SDK, or even better, upload these pods to a private podspec repo.
The problem is, if we take the podspecs the way they are, upload them to a private podspec repo, the pods will not be installed correctly. To better understand what this means, let us take a look at one of react native’s dependencies, FBLazyVector. Its located at node_modules/react-native/Libraries/FBLazyVector/FBLazyVector.podspec.
As you can see:
— The podspec requires package.json to get the version and license.
— The source is set to https://github.com/facebook/react-native.git
— source_files is set to “**/*.{c,h,m,mm,cpp}”
We have two problems here:
— This podspec will install every c,h,m,mm,cpp file in the react native repo
— It will require a package.json to be located at ../../package.json.
To solve these problems:
— Change the source_files to point to the correct directory, in this case:
s.source_files = “Libraries/FBLazyVector/**/*.{c,h,m,mm,cpp}”
— Make sure that the package path is set to:
package = JSON.parse(File.read(File.join(__dir__, "..", "..", "package.json")))# we will place package json in that path in the next section
This way, when we add FBLazyVector as a dependency to our SDK, only Libraries/FBLazyVector contents will be installed.
React native uses a bunch of other pods, to find all pods used by react native, check the following file:
MySdk/ios/Podfile
The exact changes we made to FBLazyVector podspec above has to be done for all other react native pods!
I have already done this and uploaded the whole project to GitHub
Third party dependencies
For third party dependencies, it is much easier, we can use the pod dependencies as the following:
pod 'RNFastImage', :git => "git@github.com:DylanVann/react-native-fast-image.git", :tag=> 'v8.3.3'
Here, we are using RNFastImage pod by just pointing to its repository. However, the SDK user will have to add this to his/her pods as CocoaPods has no idea where RNFastImage is located. If you want the user to just have a single dependency, add any other third party dependency to your podspec repo.
Creating private Podspec repo
After setting up all react native podspecs, we can create a private podspec repo. The goal is to be able to import react native dependencies as:
pod 'React'
Looking at MySdk/ios/Podfile mentioned above, react native requires the following dependencies:
pod 'FBLazyVector'
pod 'FBReactNativeSpec'
pod 'RCTRequired'
pod 'RCTTypeSafety'
pod 'React'
pod 'React-Core'
pod 'React-CoreModules'
pod 'React-RCTActionSheet'
pod 'React-RCTAnimation'
pod 'React-RCTBlob'
pod 'React-RCTImage'
pod 'React-RCTLinking'
pod 'React-RCTNetwork'
pod 'React-RCTSettings'
pod 'React-RCTText'
pod 'React-RCTVibration'
pod 'React-cxxreact'
pod 'React-jsi'
pod 'React-jsiexecutor'
pod 'React-jsinspector'
pod 'Yoga'
pod 'DoubleConversion'
pod 'glog'
pod 'Folly'
The Podspec repo should be structured as the following:
.
+-- PodSpecRepo
| +-- package.json // this is react native's package.json
| +-- FBLazyVector // pod name
| | +-- 0.63.3 // pod version
| | | +-- FBLazyVector.podspec // the amended podspec
| +-- FBReactNativeSpec
| | +-- 0.63.3
| | | +-- FBReactNativeSpec.podspec
| +-- RCTRequired
| | +-- 0.63.3
| | | +-- RCTRequired.podspec.... the rest of the dependencies
Upload you private repo to GitHub or Bitbucket. You can find the repo created in this article here.
Adding the pods to Podfile
Now we have all the pods needed for the react native app, add the following to MySdk-native Podfile:
Do not forget to add your podspec repo as a source on the top of of the Podfile
After that, cd to MySdk-native then execute:
pod install --repo-update
At this point, we can use react native classes inside our wrapper.
Run React Native inside the wrapper
Now we are all set, we want to start the react native app using the native wrapper that we created. This is explained and well documented in the official react native docs.
First, create a Swift class to be used as an interface for the SDK user, our class will simply contain one function that runs the react native app. That function will create a new RCTRootView and present it inside a controller.
Create a podspec for the SDK
It is important to create a podspec for the SDK so users can easily add it to their apps. Nothing special here, just an ordinary podspec.
The SDK in action
Moment of truth! let us test the SDK.
Create a simple swift app with a single button that will start our SDK. We will call this app MySdk-poc. Cd into the created app and execute:
pod init && pod install
Open the generated MySdk-poc.xcworkspace and add the following to the Podfile:
Then execute pod install again.
Create a button that will start react native on action:
Add the following to the info.plist of the poc:
<key>NSAppTransportSecurity</key>
<dict>
<key>NSExceptionDomains</key>
<dict>
<key>localhost</key>
<dict>
<key>NSTemporaryExceptionAllowsInsecureHTTPLoads</key>
<true/>
</dict>
</dict>
</dict>
This is needed only for debugging, to give the app the ability to connect to the metro bundler.
Start your react native project using yarn start then start the poc app!
Notes
— I used react native 0.61.5, other react native version may require different way of doing stuff.
— You may face some problems while building. For me, a lot of build problems were fixed by cleaning the build folder and building again.
— I am writing another article on how to setup production builds.
Project files
You can find all of the project files used in this article here:
https://github.com/AbdullahAsendar/react-native-ios-sdk
Thats all folks!
Abdullah Asendar