This page describes a way to inject a custom Native Plugin (NPI) into the Android and iOS Mobile Application Framework (MAF) Platforms. The NPI is a standalone/self contained part of an application and gives you a way to incorporate your own functionality into your Apps through the MAF Platforms. NPIs should be written in the native code for the platform.
OS Platform | Language |
---|---|
Android |
|
iOS |
|
NPIs give the developer the power to create custom sections of the Application, utilizing tools such as the Layout Editor/Interface Builder in order to define UI layouts and incorporating different backend APIs that are not currently supported by the platform.
In the app design UI, the designer can enter in arbitrary key/value pairs to associate with the NPI. This will be converted into a JSON file with a single object and key/value pairs for each field, and the JSON data will be provided to the NPI at runtime. All of the values are passed to the NPIs as string JSON data, data conversion will need to be done by the NPI (string to boolean, string to integer, etc.).
The only required key in the configuration JSON is the classname, which is the main entry point into the Native Plugin (Fragment for Android and UIViewController/UINavigationViewController for iOS):
Platform | JSON Key | Classname Example |
---|---|---|
Android | FragmentClassName | com.example.ul.NplFragment |
iOS - Objective C | ViewControllerClassName | ViewControllerClassName |
iOS - Swift | ViewControllerClassName | Module.ViewControllerClassName |
If a single app has multiple NPIs, care should be taken since both NPIs will be linked into the final app. So any shared code should only be defined in one of the NPIs to prevent linker errors.
For the NPI, the following information is needed:
The Android MAF Platform builds towards the following versions of the Android Platform:
Android Build Information | Version |
---|---|
Compile SDK Version | 30 |
Build Tools Version | 31.0.0-rc2 |
Target API Version | 30 |
Minimum SDK Version | 21 |
JDK Version | 1.8 |
Android Platform App builds include the following libraries:
Library Information | Version | Gradle Compile Information |
---|---|---|
Android Support Compat Library | 28.0.0 |
|
Kotlin Standard Library | 1.3.21 | org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.21 |
Kotlin Reflect | 1.3.21 | |
MultiDex | 1.0.3 | com.android.support:multidex:1.0.3 |
RX Java | 0.20.7 | com.netflix.rxjava:rxjava-core:0.20.7 com.netflix.rxjava:rxjava-android:0.20.7 |
RX Permissions | 0.6.1 | com.tbruyelle.rxpermissions:rxpermissions:0.6.1 |
Google Play Services | 16.4.0 | com.google.android.gms:play-services-location:16.4.0 com.google.android.gms:play-services-maps:16.4.0 com.google.android.gms:play-services-ads:16.4.0 |
DiskLRUCache Library | 2.0.2 | com.jakewharton:disklrucache:2.0.2 |
HttpMine | 4.2 | org.apache.httpcomponents:httpmime:4.2 |
Countly Analytics | 18.04 | ly.count.android:sdk:18.04 |
Flurry Analytics | 11.4.0 | com.flurry.android:analytics:11.4.0@aar |
Picasso Library | 2.5.2 | com.squareup.picasso:picasso:2.5.2 com.jakewharton:disklrucache:2.0.2 |
Spring Android | 1.0.1 | org.springframework.android:spring-android-core:1.0.1.RELEASE org.springframework.android:spring-android-rest-template:1.0.1.RELEASE |
Scribe (OAuth Library) | 1.3.7 | org.scribe:scribe:1.3.7 |
GreenRobot EventBus | 2.4.0 | de.greenrobot:eventbus:2.4.0 |
Timber | 4.5.1 | com.jakewharton.timber:timber:4.5.1 |
Firebase Core | 16.0.8 | com.google.firebase:firebase-core:16.0.8 |
Firebase FCM | 17.5.0 | com.google.firebase:firebase-messaging:17.5.0 |
Kotlin Shared Preferences | 2.4.0 | com.chibatching.kotpref:kotpref:2.4.0 |
RxPermissions | 0.6.1 | com.tbruyelle.rxpermissions:rxpermissions:0.6.1@aar |
SLF4J | 1.7.25 | org.slf4j:slf4j-nop:1.7.25 |
HTTPClient | 4.4.1.2 | cz.msebera.android:httpclient:4.4.1.2 |
Sanselan | 0.97-incubator | org.apache.sanselan:sanselan:0.97-incubator |
The NPI must provide any 3rd-party libraries in the deployed AAR file, we currently do not have a way to include links to maven artifacts.
We support the following tools for NPI development:
Tool | Information |
---|---|
Android Studio 4 | Integrated Development Environment |
Roboelectric Unit Test | Unit Testing Framework |
Espresso 2.0 UI Test Tool | Automated UI Testing Tool |
Android MAF Platform will provide a Fragment container for 3rd party NPI User Interface elements. These Fragment containers will allow the Platform the ability to embed the NPIs inside the current UI without changing the underlying code.
The Android NPI should abide by the following:
Android Fragments (from the Android Support Library v4) should be used as a UI Container for NPIs, it represents a portion of the UI in an Android Activity. This will allow the Android MAF Framework to control most of the screen that has the NPI. The Fragment approach will allow the Custom Native Plugin to have its own lifecycle, receive its own input events, and the ability to swap out its own Fragments without the knowledge of the MAF Platform.
The Android MAF Platform will pass in an Android Bundle to the Fragment that includes the
following:
final FragmentTransaction fragmentTransaction = activity.getSupportFragmentManager().beginTransaction(); fragmentTransaction.replace(container_view_id, fragment); if (addToBackStack) { fragmentTransaction.addToBackStack(null); } fragmentTransaction.commit(); |
The Custom Native Plugin should be packaged in an AAR file with the structure of an Android library as described in the Android Build System Documentation
http://tools.android.com/tech-docs/new-build-system/aar-format
The Android MAF platform utilizes a publish/subscribe event bus for communications between the platform components. This event bus can be utilized by the NPI for NPI-to-NPI communication and in the future NPI-to-MAF / MAF-to-NPI communication. For more information about the EventBus, see:
https://github.com/greenrobot/EventBus
With the update to Android 6.0, developers are now required to ask at runtime for dangerous permissions. The MobileSmith Android Platform utilizes the Rx Permissions library to handle the runtime permissions.
https://developer.android.com/training/permissions/requesting.html
https://github.com/tbruyelle/RxPermissions
For Rx Permissions, we do not utilize the lambda functionality, instead we use regular callbacks. The NPI Sample Code includes Rx Permissions functionality.
The following are caveats with Native Plugins on Android:
Currently only one AAR file per Native Plugin AppBlock is supported. If multiple AAR files are needed, the user should create one Native Plugin AppBlock for each AAR file.
Changing the ActionBar Title is currently not supported in the NPI framework. This functionality may be provided at a later date.
Nested views can cause some latency issues with the NPIs inside the MobileSmith platform, since the NPI resides several layers deep. See the following App View Hierarchy:
The iOS MobileSmith platform primarily works by transitioning to various UIViewController subclasses. One might represent a list of data configured in the MobileSmith website, another might have an e-mail feedback form, etc. This gives applications built in the MobileSmith platform a lot of built-in functionality they could utilize, but sometimes an application has needs for some very specific functionality.
NPIs allow app designers to include a custom UIViewController instance in their application that can be selected and navigated to similar to the other internal UIViewController subclasses. Currently the “scope” of an NPI is limited to what can be accomplished by a UIViewController implementation. An NPI cannot modify the Info.plist file of the app, change the application icon, or modify the Xcode linker flags, for example.
There are two primary components that make up an NPI: a ZIP file that contains all third-party code and resources needed by the device, and a configuration JSON file that is setup in the MobileSmith website with configuration information that could be utilized by the NPI code.
For the iOS Platform, there are two ways a developer can build NPIs:
Swift 5.0 NPI support is provided through the use of dynamic frameworks, more information on iOS frameworks can be found at:
The same View Controller methodology that static library NPIs utilize applies to dynamic frameworks, there needs to be an initial UIViewController that the platform will initialize. That view controller can implement the following optional method if it needs some options from the JSON Configuration data:
Method | |
---|---|
public func configure(_json: NSDictionary!) | Optional method for passing in the JSON configuration data. |
The Dynamic Framework should be zipped up for uploading to the platform. If the framework relies on multiple frameworks that are not already included in the platform then those frameworks can be added to the same zip file as the NPI. Those frameworks will be linked and included in the final build. If the framework is already linked to the NPI framework, it does not need to be included in the final zip file.
The zip file has only one required component: a library (.a) file containing the code for the custom UIViewController (or UINavigationController) subclass. In addition it could contain any number of resource bundles or additional library files that are required. If there are multiple items that need to be included in the zip, be sure to select the items in the Finder, right-click and select “Compress # items…” instead of compressing their common parent directory.
The main entry point for an NPI is the main view controller. Whenever the NPI is selected in the MobileSmith application, this is the view controller that will be presented. There are two primary types of NPIs that are supported, depending on their superclass:
The view controller class should implement one of the following initialization methods:
Method | Information |
---|---|
public init() { super.init(nibName: "<Initial view controller>", bundle: <NPI Framework>.frameworkBundle) } | This will be called to initialize the NPI's startup view controller. |
Method | Information |
---|---|
@objc public func configure(_ json: NSDictionary!) | This will be called after view controller initialization to pass in the JSON configuration. |
Method | Information |
---|---|
+ (id)npiWithJSON:(NSDictionary *)json; | This class method would be appropriate if the NPI is loaded from a UIStoryboard or if some additional logic needed to be done before the NPI knew which UIViewController subclass it wanted to return. It is given the configuration JSON data as an NSDictionary. |
- (instancetype)initWithJSON:(NSDictionary *)json; | This instance method is the most commonly used, and assumes that the NPI is either initialized from a XIB file or entirely in code. |
The view controller class could also optionally implement the following method:
Method | Information |
---|---|
+(void) setupWithJSON:(NSDictionary *)json | which will be called on app launch, before the NPI view controller is initialized. This could be useful for downloading additional data that the NPI needs before being displayed. |
This is optional, and multiple bundles could be provided. All resources (including xib and storyboard files) should be included in this bundle. When creating a new bundle target in Xcode, you need to create a Mac OSX bundle target and then manually change the platform from OSX to iOS. If you are having trouble loading PNG files from the bundle, try removing the COMBINE_HIDPI_IMAGES setting for the bundle target.
Although the bundle target will initially have source code associated with it, the bundles provided for use as a MobileSmith NPI should not have ANY code. You can verify this by examining the bundle and making sure that there is not an executable file in the bundle with the same name as the bundle (e.g., Foo.bundle/Foo).
If the app designer wants to link an NPI in a flyout menu, then there are a few extra considerations:
The NPI should be a UIViewController-based NPI. It will automatically be added in a UINavigationController that has the flyout toggle button (i.e., a button with a “hamburger” icon).
There are some cases where the NPI coder does not want to be embedded into a MobileSmith UINavigationController. If this is the case, then there is some additional work on the part of the NPI to interact with the application flyout menu:
MobileSmith utilizes Apple’s UIAppearance protocol for much of its look-and-feel. This automatically sets default colors for some UI elements (e.g., navigation bar background color and text). If no colors are set for these properties in the NPI, then they will automatically get the colors specified for the application on the MobileSmith website. These properties include:
The iOS NPI should abide by the following:
Below is a listing of system frameworks that we currently link against. If a native plugin requires a system framework that is not listed here, then we will need to include that framework in our project before we can support that custom appblock:
Framework |
---|
AVFoundation.framework |
AdSupport.framework |
AssetsLibrary.framework |
CFNetwork.framework |
CoreData.framework |
CoreGraphics.framework |
CoreLocation.framework |
CoreMedia.framework |
CoreVideo.framework |
EventKit.framework |
EventKitUI.framework |
Foundation.framework |
ImageIO.framework |
MapKit.framework |
MediaPlayer.framework |
MessageUI.framework |
MobileCoreServices.framework |
PBXFrameworksBuildPhase |
QuartzCore.framework |
SystemConfiguration.framework |
UIKit.framework |
lbiconv.dyllb |
llbxml2.dyllb |
llbz.dyllb |
In addition, we also use the following static 3rd-party libraries. If an NPI needs to use one of these, MobileSmith should be contacted to make sure we are using the same version of that library. If they attempt to include it as part of their uploaded resources.zip, it could result in a build failure:
Library |
---|
llbFlurryAnalytics.a |
llbGoogleAnalytics.a |
llbGoogleConversionTracking.a |
llbzbar.a |
All Frameworks need to disable bitcode since the main platform does not have bitcode enabled.
One potential issue in an NPI that could cause an app store submission to fail is the existence of extended file resources. These resources cannot always be represented in a zip file, and will sometimes be extracted into a __MACOS directory with individual files for the extended resources. This causes an issue with code signing, since those are seen as separate files that were added after code signing, causing app verifications to fail. To prevent this issue, the contents of an NPI zip file should not contain any extended resources.
To view extended resources, the xattr tool can be used. Before zipping up the NPI, if you navigate in the Terminal to the directory with the static library and/or bundles, execute the following command to recursively list all extended resources for all folders and files in that directory:
find . –exec xattr –l “{}” \; |
One of the most common attributes that are accidentally added to files is the com.apple. quarantine attribute. This is typically added to files that are downloaded from an e-mail client or browser and indicate to the Mac operating system that the zip file may contain executable code that has not been approved by the downloader to execute. If you ever download an application and get a pop-up from Apple asking if you are sure you want to launch an application that was downloaded from the internet, that is the work of the com.apple.quarantine attribute. To recursively remove that attribute from all files/folders in the directory, execute the following command in the Terminal:
find . –exec xattr –d com.apple.quarantine “{}” \; |
Note that this could show a lot of warning messages when it attempts to remove that attribute from files that do not actually have the attribute, but that is to be expected. After running that command for each unique extended attribute that was found (if any other than com.apple.quarantine are found), run the
find . –exec xattr –l “{}” \; |
command again to ensure that all extended attributes are removed, and then zip up the NPI for use in the MobileSmith application.