Native Plugins 101

Nov 26, 2014


iOS native plugins are actually pretty straight forward, once you get the basics down. The biggest issue people to have is just getting started and bridging Unity3D and iOS.

A common misconception is that you can just wrap Objective-C code in extern "C" and call it from Unity. You can't. But you can call C code! So, we need to create a bridging layer in C which exposes the Objective-C functionality we want.

Unity (C#)

using UnityEngine;
using System.Collections;
using System.Runtime.InteropServices;

public class SamplePlugin : MonoBehaviour {

    #if UNITY_IPHONE
    [DllImport("__Internal")]
    private static extern bool _testMethod();
    #endif

    public static bool TestMethod() {
        #if UNITY_IPHONE
        if (Application.platform == RuntimePlatform.IPhonePlayer) {
            return _testMethod();
        }
        #endif
        return false;
    }

}

Make sure that you wrap your extern calls with #if UNITY_IPHONE blocks! That way you don't get undefined symbol errors if you switch platforms. It's also good practice to prefix your extern methods with an underscore, so that it's clear that you're calling bridging code (from either direction).

Plugin (C + Objective-C)

The bridging phase is the most important. This is the piece that connects Unity/Mono and our Objective-C code. Because Objective-C is just a superset of C we can include this layer in our existing Objective-C files, for simplicity.

TestPlugin.h

#import <Foundation/Foundation.h>

void _testMethod(); // THIS is the method that Unity looks for.

@interface TestPlugin : NSObject
- (BOOL)testMethod; // Unity can't see this one! 
@end

TestPlugin.m

#import "TestPlugin.h"

bool _testMethod() {
    TestPlugin *plugin = [[TestPlugin alloc] init];
    BOOL value = [plugin testMethod];
    [plugin release];
    return value;
}

@implementation TestMethod

- (BOOL)testMethod {
  NSLog(@"Native plugin is working!");
  return YES;
}

@end

You only need to wrap your bridging methods with extern "C" { } only if you're using Objective-C++ (a .mm file). Wrapping them in a standard .m file will cause errors.

You'll see in the implementation of _testMethod() that we're using Objective-C code here. You can use Objective-C in a C function, but it's better to do all of your Objective-C work in an object. Mixing languages gets messy, try to stick to only creating/releasing objects in your C code.

Note that Unity3D iOS builds do not have ARC enabled. You must call release properly. The Memory Management Programming Guide is worth reading. You may also want to create a singleton manager object for your Objective-C code, so that you're not constantly creating and destroying objects.

The End

That's really all there is to it! If you want to get more advanced and start passing data back and forth, check out the Mono Interop docs and the Unity plugin docs.


        

Chris Baltzer