Robin Fischer

Game programmer that wants to share and document.

Unity native call reference

TL;DR: A lookup reference for the most common native calls on Android and iOS.

I sometimes implement native plugins for Unity and I constantly catch me asking the same questions. How do I transfer a string to Objective-C++ code again? How do I pass a array into Java code via the JNI bridge? I hope I can document the most cases so I have a look up reference. Naturally I want to share this, so everyone can profit from this. So here is a hopefully helpful reference for iOS and Android. I will ad more entries as soon as I encounter them in other projects. Feel free to comment in the comments section which native calls have you constantly searching the forums for answers?

Android

Bool

using UnityEngine;

namespace Android
{
    public static class BoolMethod
    {
        public static void Call()
        {
#if UNITY_EDITOR || UNITY_ANDROID
            var nativeMethodsObj = new AndroidJavaObject("com.robinryf.nativecallmatrix.BoolMethod");
            nativeMethodsObj.CallStatic("call", true);
#endif
        }
    }
}
package com.robinryf.nativecallmatrix;

public class BoolMethod {
    /*
    Make sure you use 'boolean' and not 'Boolean'. It is something completely different to the
    JNI bridge.
    */
    public static void call(boolean myBoolean)
    {
        Log.i("Received bool: " + myBoolean);
    }
}

DataClass

using UnityEngine;

namespace Android
{
    public static class DataClassMethod
    {
        private class DataClass
        {
            public int dataInt;
            public bool dataBool;

#if UNITY_EDITOR || UNITY_ANDROID
            public AndroidJavaObject ToJavaObject()
            {
                var obj = new AndroidJavaObject("com.robinryf.nativecallmatrix.DataClass");
                obj.Set("dataInt", dataInt);
                obj.Set("dataBool", dataBool);
                return obj;
            }
#endif
            
        }
    
        public static void Call()
        {
#if UNITY_EDITOR || UNITY_ANDROID
            var data = new DataClass();
            data.dataInt = 10;
            data.dataBool = true;
        
            var nativeMethodsObj = new AndroidJavaObject("com.robinryf.nativecallmatrix.DataClassMethod");
            nativeMethodsObj.CallStatic("call", data.ToJavaObject());
#endif
        }
    }
}
package com.robinryf.nativecallmatrix;

public class DataClassMethod {
    public static void call(DataClass dataClass)
    {
        Log.i("Received dataClass: " + dataClass);
    }
}

Float

using UnityEngine;

namespace Android
{
    public static class FloatMethod
    {
        public static void Call()
        {
#if UNITY_EDITOR || UNITY_ANDROID
            var nativeMethodsObj = new AndroidJavaObject("com.robinryf.nativecallmatrix.FloatMethod");
            nativeMethodsObj.CallStatic("call", 2.0f);
#endif
        }
    }
}
package com.robinryf.nativecallmatrix;

public class FloatMethod {
    /*
     Make sure you use 'float' and not 'Float'. It is something completely different to the
     JNI bridge.
     */
    public static void call(float myFloat)
    {
        Log.i("Received float: " + myFloat);
    }
}

IntArray

using UnityEngine;

namespace Android
{
    public static class IntArrayMethod
    {
        public static void Call()
        {
#if UNITY_EDITOR || UNITY_ANDROID
            var nativeMethodsObj = new AndroidJavaObject("com.robinryf.nativecallmatrix.IntArrayMethod");
            nativeMethodsObj.CallStatic("call", new [] { 1, 2, 3 });
#endif
        }
    }
}
package com.robinryf.nativecallmatrix;

public class IntArrayMethod {
    public static void call(int[] intArray)
    {
        Log.i("Received int array with length: " + intArray.length + " ints:");
        for (int i = 0; i < intArray.length; i++) {
            int element = intArray[i];
            Log.i(element + "\n");
        }

    }
}

Int

using UnityEngine;

namespace Android
{
    public static class IntMethod
    {
        public static void Call()
        {
#if UNITY_EDITOR || UNITY_ANDROID
            var nativeMethodsObj = new AndroidJavaObject("com.robinryf.nativecallmatrix.IntMethod");
            nativeMethodsObj.CallStatic("call", 42);
#endif
        }
    }
}
package com.robinryf.nativecallmatrix;

public class IntMethod {
    /*
     Make sure you use 'int' and not 'Integer'. It is something completely different to the
     JNI bridge.
     */
    public static void call(int myInteger)
    {
        Log.i("Received int: " + myInteger);
    }
}

String

using UnityEngine;

namespace Android
{
    public static class StringMethod
    {
        public static void Call()
        {
#if UNITY_EDITOR || UNITY_ANDROID
            var nativeMethodsObj = new AndroidJavaObject("com.robinryf.nativecallmatrix.StringMethod");
            nativeMethodsObj.CallStatic("call", "passedString");
#endif
        }
    }
}
package com.robinryf.nativecallmatrix;

/**
 * Created by Robin on 28.11.2017.
 */

public class StringMethod {
    public static void call(String myString)
    {
        Log.i("Received string: " + myString);
    }

}

iOS

Bool

using System.Runtime.InteropServices;

namespace Ios
{
    public static class BoolMethod
    {
#if UNITY_EDITOR || UNITY_IOS
        [DllImport ("__Internal")]
        private static extern void _BoolMethod(bool myBool);
#endif
        
        public static void Call()
        {
#if UNITY_EDITOR || UNITY_IOS
            _BoolMethod(true);
#endif
        }
    }
}
#import <Foundation/Foundation.h>


extern "C" void _BoolMethod(BOOL myBool)
{
    NSLog(@"Got Bool: %d", myBool);
}

DataStruct

using System.Runtime.InteropServices;

namespace Ios
{
    public static class DataStructMethod
    {
        [StructLayout(LayoutKind.Sequential)]
        private struct DataStruct
        {
            public int myDataInt;
            public float myDataFloat;
            public float[] myDataFloatArray;
            public float myDataFloatArrayLength;
        }
        
#if UNITY_EDITOR || UNITY_IOS
        [DllImport ("__Internal")]
        private static extern void _DataStructMethod(DataStruct myDataStruct);
#endif
        
        public static void Call()
        {
#if UNITY_EDITOR || UNITY_IOS
            var array = new[] {2.4f, 2.5f, 2.6f, 2.9f, 10.1f, 20.1f};
            var myDataStruct = new DataStruct
            {
                myDataInt = 32,
                myDataFloat = 19.3f,
                myDataFloatArray = array,
                myDataFloatArrayLength = array.Length
            };

            _DataStructMethod(myDataStruct);
#endif
        }
    }
}
#import <Foundation/Foundation.h>


struct DataStruct
{
    int myDataInt;
    float myDataFloat;
    float* myDataFloatArray;
    float myDataFloatArrayLength;
};

extern "C" void _DataStructMethod(DataStruct myDataStruct)
{
    NSNumber* myInt = [NSNumber numberWithInt:myDataStruct.myDataInt];
    NSLog(@"Got data struct with int: %@, float %f", myInt, myDataStruct.myDataFloat);
    
    for (int i = 0; i < myDataStruct.myDataFloatArrayLength; i++) {
        NSNumber* arrayNumber = [NSNumber numberWithFloat:myDataStruct.myDataFloatArray[i]];
        NSLog(@"Got array number: %@", arrayNumber);
    }
}



Float

using System.Runtime.InteropServices;

namespace Ios
{
    public static class FloatMethod
    {
#if UNITY_EDITOR || UNITY_IOS
        [DllImport ("__Internal")]
        private static extern void _FloatMethod(float myFloat);
#endif
        
        public static void Call()
        {
#if UNITY_EDITOR || UNITY_IOS
            _FloatMethod(2.3f);
#endif
        }
    }
}
#import <Foundation/Foundation.h>


extern "C" void _FloatMethod(float myFloat)
{
    NSNumber *myNumber = [NSNumber numberWithFloat: myFloat];
    NSLog(@"Got float number: %@", myNumber);
}

IntArray

using System.Runtime.InteropServices;

namespace Ios
{
    public static class IntArrayMethod
    {
#if UNITY_EDITOR || UNITY_IOS
        [DllImport ("__Internal")]
        private static extern void _IntArrayMethod( int[] myIntArray, int size);
#endif
        
        public static void Call()
        {
#if UNITY_EDITOR || UNITY_IOS
            var array = new[] {2, 4, 9, 10, 11};
            _IntArrayMethod(array, array.Length);
#endif
        }
    }
}
#import <Foundation/Foundation.h>


extern "C" void _IntArrayMethod(int* array, int length)
{
    NSMutableArray* myArray = [NSMutableArray array];
    
    for (int i = 0; i < length; i++) {
        int arrayMember = array[i];
        [myArray addObject:[NSNumber numberWithInt:arrayMember]];
    }
    
    NSLog(@"Got int array with legth: %d and entries:", length);
    
    for (int i = 0; i < [myArray count]; i++) {
        NSNumber* number = [myArray objectAtIndex:i];
        NSLog(@"%@", number);
    }
}

Int

using System.Runtime.InteropServices;

namespace Ios
{
    public static class IntMethod
    {
#if UNITY_EDITOR || UNITY_IOS
        [DllImport ("__Internal")]
        private static extern void _IntMethod(int myInt);
#endif
        
        public static void Call()
        {
#if UNITY_EDITOR || UNITY_IOS
            _IntMethod(42);
#endif
        }
    }
}
#import <Foundation/Foundation.h>


extern "C" void _IntMethod(int myInt)
{
    NSNumber* myNumber = [NSNumber numberWithInt:myInt];
    NSLog(@"Got int number: %@", myNumber);
}

String

using System.Runtime.InteropServices;

namespace Ios
{
    public static class StringMethod
    {
#if UNITY_EDITOR || UNITY_IOS
        [DllImport ("__Internal")]
        private static extern void _StringMethod(string myString);
#endif
        
        public static void Call()
        {
#if UNITY_EDITOR || UNITY_IOS
            _StringMethod("Hello this is managed code.");
#endif
        }
    }
}
#import <Foundation/Foundation.h>


extern "C" void _StringMethod(const char* myString)
{
    NSString* myNsString = [NSString stringWithUTF8String:myString];
    NSLog(@"Got passed string: %@", myNsString);
}