Using JNI in Swift to put an app into the Android Play Store

Java Code:

package com.flowkey.plugins; // also sets the namespace for JNI// We use Cordova, but this works from any class (or activity)
public class OurSwiftPlugin extends CordovaPlugin {
// Explicitly load all the required frameworks in order.
// Android 5.0+ finds most dependencies by itself and
// doesn't need as much hand-holding. We support 4.4+:
static {
System.loadLibrary("c++_shared");
System.loadLibrary("scudata"); // icu libs...
System.loadLibrary("scuuc"); // see part 2 of this series
System.loadLibrary("scui18n"); // -------------------------
System.loadLibrary("swiftCore");
System.loadLibrary("swiftGlibc");
System.loadLibrary("swiftJNI");
// Contains the Swift code we want to call:
System.loadLibrary("ourSwiftFramework");
}
private native int addInts(int x, int y); @Override
public void pluginInitialize() {
System.out.println(addInts(12, 22)); // prints 34
}
}

Swift code (compiled into libourSwiftFramework.so):

@_silgen_name("Java_com_flowkey_plugins_OurSwiftPlugin_addInts")
public func callYourSwiftFunctionWhateverYouWantInternally(env: UnsafeMutablePointer<JNIEnv>, jobj: jobject, x: jint, y: jint) -> jint {
return x + y // jint is a typealias of Int32, so this just works
}
Code completion is one of those things you don’t miss until its gone.

Java Code:

package com.flowkey.plugins; // see first example, abovepublic class OurSwiftPlugin extends CordovaPlugin {
static {
// ... and the other dependencies here (see example above)
System.loadLibrary("ourSwiftFramework");
}
private native void pushIntOntoSwiftArray(int n); @Override
public void pluginInitialize() {
pushIntOntoSwiftArray(2);
pushIntOntoSwiftArray(5);
pushIntOntoSwiftArray(7);
pushIntOntoSwiftArray(12);
pushIntOntoSwiftArray(8);
}
// Swift will call this when its array contains five elements
private void onArrayFull(int arraySum) {
System.out.println(arraySum); // prints 34
}
}

Swift code (compiled into libourSwiftFramework.so):

var intArray: [jint] = []@_silgen_name("Java_com_flowkey_plugins_OurSwiftPlugin_pushIntOntoSwiftArray")
public func pushInt(env: UnsafeMutablePointer<JNIEnv>, jobj: jobject, n: jint) {

intArray.append(n)

if intArray.count >= 5 {
let callbackResult = intArray.reduce(jint(0), combine: +)
callOnArrayFull(env, jobj: jobj, value: callbackResult)
intArray.removeAll()
}
}
private func callOnArrayFull(env: UnsafeMutablePointer<JNIEnv>, jobj: jobject, value: jint) {
let jni = env.memory.memory // env is a pointer to a pointer!

let methodName = "onArrayFull"
let methodSignature = "(I)V" // one Int argument, returns Void
let javaClass = jni.GetObjectClass(env, jobj)
let methodID = jni.GetMethodID(env, javaClass, methodName, methodSignature)

let valueAsJValue = jvalue(i: value)
var methodArgs: [jvalue] = [valueAsJValue]
jni.CallVoidMethodA(env, jobj, methodID, &methodArgs)
}

--

--

Love podcasts or audiobooks? Learn on the go with our new app.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store