Container

The Electrode Native container is a native library similar to common third-party libraries that Android and iOS developers are familiar with. The container is generated entirely by the Electrode Native platform and packaged as a versioned Android Archive (AAR) library for Android and as a framework for iOS.

Each mobile application has its own custom Electrode Native container.

What's inside a Container ?

The Electrode Native container includes the following:

  • The MiniApps The MiniApps are packaged inside a single JavaScript bundle containing all of the JavaScript code of all the MiniApps.

  • The MiniApps assets the MiniApps assets are the fonts and images that are used by the MiniApps.

  • The MiniApps native dependencies The MiniApps native dependencies are all of the native dependencies that MiniApps directly or indirectly depend on. React Native is one of them but it can also includes third-party native modules (react-native-maps, react-native-vector-icons, react-native-electrode-bridge to name a few) or platform APIs.

  • Additional native dependencies Some native dependencies do not depend directly or indirectly on the MiniApps. These dependencies are primarily native platform API implementations as well as some third-party native modules that are only used from the mobile application side. These additional dependencies are uncommon.

  • Container-specific code An Electrode Native container includes some code that is exposed to mobile application developers to properly integrate a container in their mobile application. This consists mostly of code to initialize the container along with some utility code to access and deal with the MiniApps stored within.

Container publication overview

An Electrode Native container is completely generated by Electrode Native based on the MiniApps and specific native dependencies you want to include in it. The Container should be published in a central, team shared, location. Keeping it local to your machine has its benefits--for example to launch a MiniApp in the Electrode Native runner, or for development and experimentation use, but for the most part you'll want to publish your Electrode Native container.

Electrode Native supports three distinct publishers for the Electrode Native generated containers: Git, Maven and JCenter:

  • The Maven publisher can be used to publish Android containers. Upon generation and compilation of the Electrode Native container, the resulting AAR artifact will be published to a Maven repository of your choice--this can be your own custom Nexus for example.

  • The JCenter publisher can be used to publish Android container. Upon generation and compilation of the Electrode Native container, the resulting AAR artifact will be published to your Bintray repository.

  • The Git publisher can be used for both the Android or iOS container. When you generate a container, the Git publisher will publish the complete generated container project to a Git repository of your choice--probably GitHub. Please note that in the case of an Android Container, the project itself will be published to Git, not the AAR. Only Maven and JCenter publisher will trigger generation and publication of an AAR.

It is possible to use more than one publisher for your container. For example, you can choose to publish your Android containers both to Maven and GitHub.

A Container is versioned for publication. Every time an Electrode Native container is re-generated, its version is updated. When using Maven or JCenter publishers, the version is part of the maven artifact. When using the Git publisher, a Git tag is used to version the Container.

Because Electrode Native containers include some native code, they are primarily used during the development lifecycle of a mobile application version. You cannot use a newly generated Electrode Native container in a mobile application version that has already been released. When you update a Miniapp for an OTA update, a newly generated Electrode Native container is not included because you cannot ship native code through OTA.

Publishing a container

There are two ways to publish a Container using Electrode Native : directly (explicitly) or indirectly (implicitly). Based on your needs and your context, you might need one way or the other (or both).

Explicitly publishing a Container

Electrode Native offers the publish-container command that can be used to publish a container. You don't need a Cauldron to use this command. It is mostly used after generating a container locally through the create-container command, in order to publish the locally generated container to a shared repository (git, maven or jcenter) so that anyone with access to the repository can use the container.

It is possible to run the publish-container command multiple times for the same container, if you wish to publish the container to different repositories.

The use of the create-container / publish-container commands combo, will be mostly useful for development and experimentation with Electrode Native. Internally, we are also using these commands on one of our CI jobs that is in charge of generating development Containers for our native application on a scheduled basis. We use create-container command, providing all MiniApps GitHub repositories to the command (through the --miniapps option), so that it pulls the latest code of all MiniApps and generate a Container based on the latest changes. The CI job then use publish-container to publish the newly generated container to repositories accessed by the native application to retrieve the latest development Container.

Implicitly publishing a Container

If you are using a Cauldron, then a container will automatically be regenerated for any non released version(s) of your native application whenever you make a change to the content of the container of a given native application version (for example adding/removing/updating one or more MiniApp(s) or native dependency(ies) for this Container). In addition to regenerating a new Container to include the changes, the Cauldron will also trigger the publication of this new generated Container, using any publishers configured in the Cauldron, as the next section details.

Please also note that if your cauldron is properly configured for container publication, you can also use the cauldron regen-container command to trigger a new generation and publication of a container for a given native application version, even if there are no changes to the content of the container (this can be useful in certain scenarios).

Configuring container publication

The container publication configuration is part of the containerGenerator configuration object stored in a Cauldron, so that it can be shared across users of the Cauldron. If you have properly setup your Cauldron, you can use the cauldron add publisher command to add a publisher to a given native application platform. You can use this command multiple times if you wish to add more than one publisher.

The publication configuration object will be stored under in the cauldron document. The following illustrate such a configuration:

"nativeApps":[
   {
      "name":"MyWeatherApp",
      "platforms":[
         {
            "name":"android",
            "config":{
               "containerGenerator":{
                  "containerVersion":"1.0.0",
                  "publishers":[
                     {
                        "name":"git",
                        "url":"git@github.com:user/myweatherapp-android-container.git"
                     },
                     {
                        "name":"maven",
                        "url":"http://user.nexus.repo.com:8081/nexus/content/repositories"
                     },
                     {
                        "name":"jcenter"
                     }
                  ]
               }
            }
         },
         {
            "name":"ios",
            "config":{
               "containerGenerator":{
                  "containerVersion":"1.0.0",
                  "publishers":[
                     {
                        "name":"git",
                        "url":"git@github.com:user/myweatherapp-ios-container.git"
                     }
                  ]
               }
            }
         }
      ]
   }
]

The publishers array contains all the publishers you want to use to publish an Electrode Native container. In example shown above, we want to publish the Electrode Native container of MyWeatherApp Android to three destinations: a GitHub repository, a Maven repository and to JCenter. For the GitHub repository, the code of the Electrode Native container will be published and a Git tag will be used for the version. For the Maven repository, the Electrode Native container will be compiled and the resulting versioned AAR will be published to the Maven and JCenter repositories.

The ern cauldron add publisher command can be used to add a publisher to a Cauldron, rather than adding it manually.

Creating a custom Container publisher

Electrode Native Container publishers are standalone node packages (published to npm) and retrieved dynamically (they are not packaged within Electrode Native itself). In that sense it is quite easy to implement (and eventually distribute) your own Container publisher if needed.

Check our dummy publisher as a reference to get started.

A few things to keep in mind when creating a Container publisher :

  • The package nampe must start by ern-container-publisher-. This is a convention for Electrode Native Container publishers, and is enforced by Electrode Native.

    Using this convention makes it also simpler for the users as Electrode Native will prefix the publisher name with ern-container-publisher- if needed, so they can use a shorthand name. For example, a publisher can be reffered as foo and Electrode Native will look for a package named ern-container-publisher-foo. Please note that if you are publishing a scoped package for a publisher, Electrode Native will not prepend ern-container-publisher-. So if your publisher is @mycompany/ern-container-publisher-foo, it should be reffered to as this ful package name in Electrode Native, there is no shorthand name.

  • You should add a keyword 'ern-container-publisher' in the keywords list of the package.json. This could be used later on to enable Container publishers discovery.

  • All you have to implement is a class with two getters get name (to return the name of the publisher) and get platforms (to return the list of supported platforms of the publisher), as well as the publish(config) method. This class should be the default export of the module.

  • Once your Container publisher is published to npm, it will be immediately available for Electrode Native users. During development, if you need to try out your publisher before publishing it to npm, you can use ern publish-container command by using an absolute local file system path to your Container publisher module. ern publish-container will call either index.js (or index.ts as we support TypeScript) from your module directory root, or src/index.js if no index was found in root. Thus make sure to name your entry point as index.js/index.ts.

Adding an Electrode Native container to your mobile application

This section describes how to add an Electrode Native container to your Android or iOS mobile application.

Android

A Container library can be added to a mobile Android application project in one of two ways:

  • By adding a dependency on the Electrode Native container AAR (recommended), or

  • By directly adding the Electrode Native container module to the Android project (as a git submodule for example)

You will also need to update your build.gradle files with the following:

  • jcenter repository

We publish the react-native Maven artifact to jcenter. Therefore, you must make sure that jcenter is present in your list of repositories. The repositories are most commonly defined in your top-level project build.gradle.

repositories {
  jcenter()
  //...
}
  • resolution strategy

React Native includes some third-party libraries that might conflict with the versions you are using. For example, you might have issues with jsr305. If that is the case, add the following to your application module build.gradle

configurations.all {
  resolutionStrategy.force 'com.google.code.findbugs:jsr305:3.0.0'
  //...
}

In addition to the above resolution strategy for handling the jsr305 conflict, you might also run into a conflict with OkHttp. React Native depends on a specific version of the very popular networking library, OkHttp. If you are using this library in your application, you might be forced to align your version of OkHttp with the version included with the React Native version that you are using. This is due to the current React Native design.

  • okio linting

You might run into a conflict with the okio third party library which comes with React Native. It is a known issue. To resolve this issue, disable the lint check for InvalidPackage. You can also find solutions by searching for the okio conflict on the web.

lintOptions {
  disable 'InvalidPackage'
  //...
}

Adding the Container as an AAR

If a Maven publisher has been configured in the Electrode Native cauldron, Electrode Native will package and publish the Electrode Native container project as a Maven artifact containing the AAR file--either to a local or remote Maven repository.

If you are implicitly publishing a container from a Cauldron (through a change in container content or the use of cauldron regen-container command), the Maven artifact will have the following data:

  • Group ID : com.walmartlabs.ern

  • Artifact ID : {mobile-app-name}-ern-container

  • Version string : {container-version}

{mobile-app-name} is the name of the mobile application in the cauldron for the Electrode Native container that is being generated. For example, if the application name is walmart, the Electrode Native container artifact ID will be walmart-ern-container.

{container-version} is the version of the generated Electrode Native container. The container version can be in the form: x.y.z where x, y and z are integers. For example 1.2.3 is a valid container version. You can specify a version for a Container or, by default, the current version will be patched-bumped to the new version.

To add a dependency on the Electrode Native container, in your mobile application add the following code to the dependencies object of your application module build.gradle. Be sure to substitute the {mobile-app-name} and {container-version} to represent your application.

dependencies {
  compile('com.walmartlabs.ern:{mobile-app-name}-ern-container:{container-version}')
  //...
}

If you are explicitely publishing a container through the use of publish-container command, the Maven artifact id will be local-ern-container. The group id will remain the same though com.walmartlabs.ern. You can pass options to the command to change the artifact id and group id at your convenience. Please see publish-container documentation for more details. Also, if you use or plan to use a locally published Electrode Native container (to your maven local repository), make sure to declare mavenLocal in the list of repositories. This is located in the top-level project build.gradle.

repositories {
  mavenLocal()
  //...
}

Adding the container as a Git submodule

Alternatively, you can include an Electrode Native container in a mobile application by adding it as an Android module. Although this is not the recommended way to add third-party dependencies (the container being one) to an Android project, it is however possible and this might be the best process if you don't have a remote Maven repository that you can publish the container to.

To add the container library as an Android module, add a GitHub publisher to the cauldron (or use publish-container with the git publisher option). Then, when a new Container version is published, Electrode Native will publish the resulting project to a private or public GitHub repository. It will create a Git tag for each version of a container. You can then add the container Android module to your application project--managed as a Git submodule.

Note Do not edit the code of the container if you use this procedure even though adding the Container directly in your project makes its code editable. The container code should not be modified manually, as any custom modification will be lost the next time the container is generated.

Be sure to include the module in your project settings.gradle, and add a compile project directive to your application module build.gradle.

iOS

An Electrode Native container can be added as a dependency to an Xcode project in two ways:

  • Use a dependency manager such as Carthage (Cocoapods will be supported in the future) or,

  • Perform a manual installation

Using Carthage to add a container

To add a container using Carthage:

1) Create a Cartfile if you don't already have one, or open an existing Cartfile. 2) Add the following line to your Cartfile.

$ git "git@github.com:user/myweatherapp-ios-container.git" "v1.0.0"

3) Create a Cartfile.resolved file if you don't have one or open your existing Cartfile.resolved file. 4) Add the following line to your Cartfile.resolved file:

$ git "git@github.com:user/myweatherapp-ios-container.git" "v1.0.0"

5) Install your dependencies using the following command:

$ carthage bootstrap --no-build --platform ios

Manually adding a container

To manually add a container:

  1. Clone the container to <Your-WorkSpace>.

     $ git@github.com:user/myweatherapp-ios-container.git
  2. Open your project in Xcode and right click on Libraries.

  3. Select Add Files to <your project name>. Look for ElectrodeContainer.xcodeproj in the file directory where you cloned the repo above.

Additional Configuration

After installing the dependency, you will need to add additional configurations.

  1. In Xcode, choose <your project name> from the Project Navigator panel.

  2. Click <your project name> under TARGETS.

  3. From the General tab, locate Embedded Binaries and click +

  4. Select ElectrodeContainer.framework and click Add.

  5. In Build Phases, verify that ElectrodeContainer is in Target Dependencies, Link Binary With Libraries, and Embed Frameworks.

Initializing a Container

This section describes how to initialize a container for the Android and iOS platforms.

Android

Before accessing MiniApps that are stored within an Electrode Native container, the container needs to be initialized.

Initialization of a Container should ideally take place during startup of your mobile application. If you are using a class extending Application, you should place the container initialization call in the onCreate method of this class. If you are not using an Application class to initialize all libraries used by your mobile application, you should place the container initialization code wherever appropriate. It's best to have it initialized as soon as possible.

The initialization of a container is done as a single call of the initialize static method of the ElectrodeReactContainer class.

ElectrodeReactContainer.initialize(
    this /* Application instance */,
    new ElectrodeReactContainer.Config().isReactNativeDeveloperSupport(BuildConfig.DEBUG)
    /* Additional plugins configuration here */);

The first parameter to this method is the Application instance. In the sample call above, we use this as the call is made from an Application extending class. The second parameter is the configuration of the container and React Native. In the sample above, we enable React Native developer support. In the sample we make use of BuildConfig.DEBUG to enable developer mode for debug builds only. You can adapt it for your application needs.

The initialize method might also contain additional parameters. Respectively, one parameter per plugin configuration. Not all plugins (APIs or third-party native modules) are configurable, so most of them (>90%) won't add an extra parameter to the initialize method. One configurable plugin is react-native-code-push for example, as you need to pass a deployment key to initialize this plugin, and it also has a debug mode that you can enable or disable.

iOS

Before accessing MiniApps stored within an Electrode Native container, you need to initialize the container. In iOS, we prefix our platform-specific files with Electrode.

Initialization of a Container should ideally take place during startup of your mobile application. Ideally it should take place in your AppDelegate.m in didFinishLaunchingWithOptions: method. Otherwise, you should call the container initialization wherever appropriate. It's best to have it initialized as soon as possible.

Initialization of Container is performed through the static method startWithConfigurations: of ElectrodeReactNative.

    ElectrodeContainerConfig *containerConfig = [[ElectrodeContainerConfig alloc] init];
    containerConfig.debugEnabled = RnDevSupportEnabled;
    [ElectrodeReactNative startWithConfigurations:containerConfig];

The first parameter is an implementation of the ElectrodePluginConfig protocol we provide through ElectrodeContainer that allows you to configure for both ElectrodeContainer and React Native. In the sample above we use RnDevSupportEnabled, a static boolean constant, to decide if developer support should be enabled or not. You can adapt it for your application needs.

The startWithConfigurations: method might also take additional parameters such as the implementation of ElectrodePluginConfig for additional plugins, depending on your plugin dependencies. Specifically, one parameter per plugin configuration. Note Not all plugins (APIs or third party native modules) are configurable, so most of them (>90%) won't add an extra parameter to the initialize method. One configurable plugin is react-native-code-push for example, as you need to pass a deployment key to initialize this plugin, and it also has a debug mode that you can enable or disable.

To learn more about configurable plugins, see url

Launching a MiniApp

Android

When a Container is generated, it will create one Activity for each MiniApp included in the container. For example, if you have a MiniApp named Hello, the container will create an Activity-extending class named HelloActivity. It will also be declared in the AndroidManifest.xml file of the container so that you can launch it from your mobile application without extra setup.

All of these activities are stored in the com.walmartlabs.ern.container.miniapps namespace.

To launch a MiniApp, all you have to do then, is start its corresponding Activity. You can also pass initial properties to a MiniApp, which will be provided to the JavaScript MiniApp as properties in the componentWillMount React lifecycle. This might be useful if the MiniApp needs data when first launched.

  • Call the following static method of the ElectrodeMiniAppActivity class.

    The first parameter is the Intent instance that you will pass to startActivity, while the second parameter is a Bundle instance containing the data to provide to the MiniApp as key:value pairs.

public static void addInitialProps(@NonNull Intent intent, @NonNull Bundle bundle)

The generated Activities are very basic, and might not fulfill more advanced needs. If you need to use your own Activity subclass to host a MiniApp, you can directly extend the ElectrodeMiniAppActivity class and override the methods to your needs.

Note Be sure to override the following method and return the String corresponding to the MiniApp name hosted by this Activity--using the previous example, we would return "Hello".

protected String getMiniAppName()

If you cannot extend your own Activity from this one (you might already have a deep inheritance chain and Java does not support multiple inheritance) but roll your own, or host the MiniApp in a Fragment instead, then you'll need to use ElectrodeMiniAppActivity as a template to roll your own class.

iOS

When a Container is generated, it provides one UIViewController for each MiniApp included in the Container. For example, if you have a MiniApp named Hello, the container will create a UIViewController that contains the Hello miniapp--and it's the same UIViewController that you are already familiar with.

To launch a MiniApp, all you have to do then, is

  • Present its corresponding UIViewController by calling [[ElectrodeReactNative sharedInstance] miniAppWithName:@"<your-mini-app-name>" properties:nil]

  • You can also pass initial properties to a MiniApp, which will be provided to the JavaScript MiniApp as properties in the componentWillMount React lifecycle. This might be useful if the MiniApp needs data when first launched. To do this, pass an NSDictionary to the parameter property.

The generated UIViewController is basic and might not fulfill your advanced needs. If you would like to use your own subclass of the UIViewController, you must override viewDidLoad: in your UIViewController as shown below:

- (void)viewDidLoad {
    [super viewDidLoad];    
    UIViewController *viewController =
    [[ElectrodeReactNative sharedInstance] miniAppWithName:@"<YourMiniAppName>" properties:nil];
    viewController.view.frame = [UIScreen mainScreen].bounds;
    [self.view addSubview:viewController.view];
}

- ern create-container Create (generate) a Container locally without publishing it. This command is rarely used in workflows as it does not allow remote publication of the generated container. This command is primarily used for specific development and experimentation purposes.

- ern cauldron (add/del/update> dependencies and - ern cauldron <add/del/update> miniapps Given that a container contains MiniApps and native dependencies, every time you need to add, delete, or update MiniApps or native dependencies to your container, the container is regenerated and published (with a new version). This means every time you make use of an ern cauldron subcommand used to add, delete, or update MiniApps or native dependencies for a given non-released mobile application version, behind the scene a new container version is automatically re-generated and published.

- ern run-android and - ern run-ios These two commands generate a local container that includes your MiniApp along with its native dependencies and the container is used by the Electrode Native runner application project to launch your MiniApp.

Last updated