Explore the Template Project

Learning Objectives

After completing this unit, you’ll be able to:

  • Navigate the project structure for Mobile SDK React Native apps.
  • Configure your project for debugging or production.
  • Find the Mobile SDK React Native sample app and open source libraries on GitHub.

Mobile SDK Templates for React Native

When you create an app with forcereact, the script customizes a template with the arguments that you provide. This customized template is the project for your new Mobile SDK app. For React Native, the template defines a native container app that provides direct access to the native operating system. This container provides the bridge between your JavaScript code and the Android or iOS runtime. In some cases, your development needs might require adjustments to this underlying container. Let’s take a quick look at what’s in that app.

iOS Native Container

The iOS native part of the template project is almost identical to that of a native iOS project. Its two classes take care of:

  • * Configuring Mobile SDK for startup
  • * Instantiating and displaying the root view
  • * Initiating and handling login and authentication

Open <output_directory>/ios/FirstReact.xcworkspace in Xcode. If you skim through the workspace, it’s easy to think that you’re looking at a native iOS project. For example, the AppDelegate class includes most of the boilerplate code of a native iOS app. The React-specific code is in the setupReactRootView: and setupRootViewController methods of AppDelegate.

In setupRootViewController, you tell React whether to run your app from a development server (for testing) or from a bundle on the device (for production). This method merely calls setupReactRootView:. Depending on the scenario you’re building, you might be required to uncomment the appropriate setupReactRootView: call and comment out the other. The code comments give helpful details for these setups.
- (void)setupRootViewController {    
    /**
     * Loading JavaScript code - uncomment the one you want.
     *
     * OPTION 1
     * Load from development server. Start the server from the repository root:
     *
     * $ npm start
     *
     * To run on a device, change `localhost` to the IP address of your computer
     * and make sure your computer and iOS device are on the same Wi-Fi network.
     */
    [self setupReactRootView:[NSURL URLWithString:@"http://localhost:8081/index.ios.bundle?platform=ios"]];
    /**
     * OPTION 2
     * Load from pre-bundled file on disk. To re-generate the static bundle,
     * start the server from the repository root:
     *
     * $ npm start
     *
     * Run the curl command and add the output to your main Xcode build target:
     *
     * $ curl http://localhost:8081/index.ios.bundle -o main.jsbundle
     */
    // [self setupReactRootView:[[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]];
}

Notice that you initialize this class with the location (jsCodeLocation) of a bundle that contains your JavaScript code. If you’re running from the development server––in the simulator or on the device––use option 1. To repackage the bundle as a static bundle and install it locally in a production environment, use option 2. In either case, follow the instructions provided in the code comments.

The setupReactRootView: method creates an instance of RCTRootView, a React Native class, and assigns that instance as the app’s root view. RCTRootView acts as the bridge between native UI components and your app’s React Native components.
- (void)setupReactRootView:(NSURL*)jsCodeLocation{
    RCTRootView *rootView = 
        [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
                                    moduleName:@"FirstReact"
                             initialProperties:nil
                                 launchOptions:self.launchOptions];
    UIViewController *rootViewController =
        [[UIViewController alloc] init];
    rootViewController.view = rootView;
    self.window.rootViewController = rootViewController;
}

Android Native Container

Open your FirstReact/android project in Android Studio, then go to the Project view and look under android/app/src/main/java. There, you can find two native Java classes––MainActivity and MainApplication. These classes are similar to their native-only Mobile SDK counterparts.

The MainApplication and MainActivity classes fulfill the same purposes as in Android native projects:

  • Configuring Mobile SDK for startup
  • Instantiating and displaying the startup activity
  • Initiating and handling login and authentication

The primary differences between native and React Native templates for these classes are their base classes. In React Native apps, MainApplication extends Application, as usual, but also implements ReactApplication. MainActivity extends SalesforceReactActivity, which, in turn, extends ReactActivity. ReactApplication and ReactActivity come from the Facebook React Native library. Looking closely at the implementations, you find that most code paths ultimately lead to a React Native object. These objects include React-centric specializations of many Mobile SDK classes, such as SalesforceReactSDKManager.

For example, the MainApplication code creates a ReactNativeHost object and uses it to pass Mobile SDK React Native packages to the React Native framework.
private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
  @Override  public boolean getUseDeveloperSupport() {
     return BuildConfig.DEBUG;
  }
  @Override
  protected List<ReactPackage> getPackages() {
     return Arrays.asList(
           new MainReactPackage(),
           SalesforceReactSDKManager.getInstance().getReactPackage()
     );
  }
};
Digging deeper into the SalesforceReactSDKManager.getReactPackage() code, you find that it returns a new ReactPackage object. This object overrides the createNativeModules() base method to return exactly the list of packages you’d expect:
@Overridepublic List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
   List<NativeModule> modules = new ArrayList<>();  
   modules.add(new SalesforceOauthReactBridge(reactContext));
   modules.add(new SalesforceNetReactBridge(reactContext));
   modules.add(new SmartStoreReactBridge(reactContext));
   modules.add(new SmartSyncReactBridge(reactContext));
   return modules;
   }

The React Native runtime calls this method and other overrides to weave the magic that executes JavaScript methods as native Mobile SDK code.

Configuring Your Build

Looking for where you configure your code to run in debug or release mode? That mechanism is a little different on Android than on iOS. During development and debugging, Android Studio expects to load the JS files directly from the development server. For release mode, it loads the JS files from a bundle on the device. You can override the default build configuration in the project’s android/app/build.gradle file. Follow the embedded instructions in the big comment at the top of the file. For example:

/*** The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets
* and bundleReleaseJsAndAssets).
* These basically call `react-native bundle` with the correct arguments during the Android build
* cycle. By default, bundleDebugJsAndAssets is skipped, as in debug/dev mode we prefer to load the
* bundle directly from the development server. Below you can see all the possible configurations
* and their defaults. If you decide to add a configuration block, make sure to add it before the
* `apply from: "../../node_modules/react-native/react.gradle"` line.
*
* project.ext.react = [
*   // the name of the generated asset file containing your JS bundle
*   bundleAssetName: "index.android.bundle",
*
*   // the entry file for bundle generation
*   entryFile: "index.android.js",
*
*   // whether to bundle JS and assets in debug mode
*   bundleInDebug: false,
*
*   // whether to bundle JS and assets in release mode
*   bundleInRelease: true,
*
*   // whether to bundle JS and assets in another build variant (if configured).
*   // See http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Variants
*   // The configuration property can be in the following formats
*   //         'bundleIn${productFlavor}${buildType}'
*   //         'bundleIn${buildType}'
*   // bundleInFreeDebug: true,
*   // bundleInPaidRelease: true,
*   // bundleInBeta: true,
*
*   // the root of your project, i.e. where "package.json" lives
*   root: "../../",
*
*   // where to put the JS bundle asset in debug mode
*   jsBundleDirDebug: "$buildDir/intermediates/assets/debug",
*
*   // where to put the JS bundle asset in release mode
*   jsBundleDirRelease: "$buildDir/intermediates/assets/release",
*
*   // where to put drawable resources / React Native assets, e.g. the ones you use via
*   // require('./image.png')), in debug mode
*   resourcesDirDebug: "$buildDir/intermediates/res/merged/debug",
*
*   // where to put drawable resources / React Native assets, e.g. the ones you use via
*   // require('./image.png')), in release mode*   resourcesDirRelease: "$buildDir/intermediates/res/merged/release",
*
*   // by default the gradle tasks are skipped if none of the JS files or assets change; this means
*   // that we don't look at files in android/ or ios/ to determine whether the tasks are up to
*   // date; if you have any other folders that you want to ignore for performance reasons (gradle
*   // indexes the entire tree), add them here. Alternatively, if you have JS files in android/
*   // for example, you might want to remove it from here.
*   inputExcludes: ["android/**", "ios/**"],
*
*   // override which node gets called and with what additional arguments
*   nodeExecutableAndArgs: ["node"]
*
*   // supply additional arguments to the packager
*   extraPackagerArgs: []
* ]
*/

Mobile SDK Sample App for React Native

If you’re itching to do some further explorations on your own, check out the SmartSyncExplorerReactNative sample app in its open-source repo on GitHub. This sample demonstrates all the React Native language features you’ve just learned about, as well as some Mobile SDK offline features you may not yet know about:
  • SmartStore––An offline caching mechanism for storing data securely on the device.
  • SmartSync Data Framework––An offline synchronization mechanism for easily merging offline data changes with the source Salesforce records when a device regains its connectivity.
To build and run the SmartSyncExplorerReactNative sample app on your development machine:
  1. At a Terminal window or Windows command prompt, clone the repo:

    git clone https://github.com/forcedotcom/SmartSyncExplorerReactNative.git
  2. Change to the SmartSyncExplorerReactNative/ directory.

    cd SmartSyncExplorerReactNative
  3. Install dependencies:

    • iOS: node ./installios.js
    • Android: node ./installandroid.js
  4. Start the react-native packager:

    • OS X: npm start
    • Windows: npm run-script start-windows
  5. Open the project workspace in your development environment:

    • iOS: In Xcode, open <local_clone_of_repo>/ios/SmartSyncExplorerReactNative.xcworkspace
    • Android: In Android Studio, open <local_clone_of_repo>/android/
  6. Click Run.


To study the native library project that defines the bridges between Mobile SDK and React Native, see the libs/SalesforceReact projects in the SalesforceMobileSDK-iOS and SalesforceMobileSDK-Android GitHub repos.

retargeting