Script.it .Nam Myoho Renghe Kyo.

Script.it
[Unity3D] Writing native plugins: iOS

Following up on the previous post on writing native android plugins. I will now go into the process of creating native iOS plugins for Unity3D.
At the end of this post we will have a .a file as the native library together with some headers used for our implemented classes and an UnityAppController subclass.


iOS native plugins for Unity3D.


To develop native iOS plugins, you will need the following toolset:
- an Apple computer; can be anything thats fast enough.
- xCode code editor.
- an Apple device to compile on (also Apple simulators).
- Objective-C coding knowlegde, as i am not covering the language itself.

Now that we have that covered, lets go into writing the plugin.

1. Open the xCode editor and create a new Project with project type: iOS -> Framework & library -> Cocoa Touch Static Library.
iOS_create_xcode_project

2. Give your plugin a namespace.
iOS_namespace

3. Finish the project creation process.
Your project structure will look like the following:
iOS_project_view
The files in red indicate that those files are not yet valid. For now, don’t worry about those. We will cover them later on.


Let’s dive in some Objective-C code to create our plugin.


Your typical Objective-C class has 2 components, a header ( .h file ) and a body ( .m file ).

We will start with our header file.
The header file is typically viewed as an “interface”, defining parameters and methods ( not method bodies ).

?View Code OBJECTIVE-C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
 
//import the basics.
#import <Foundation/Foundation.h>
 
//start interface.
@interface MyNativePlugin : NSObject  //extend from basic object.
 
//define variables.
@property ( retain, nonatomic ) NSString* dummyString;
 
//define methods.
-(id) init;
-(void) doMyNativePluginStuff: (NSString*) passed_name; 
 
//end interface.
@end

In the above piece of code, we define a Header “MyNativePlugin.h” which has 2 methods “init” and “doMyNativePluginStuff”, the first accepting 0 parameters and the latter accepting 1 string parameter. The Header also defines a class variable called dummyString of type NSString.

Next up is the body file.
The body file typically incorporates the variables and methods defined in the header file.

?View Code OBJECTIVE-C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
 
//import the header file.
#import "MyNativePlugin.h"
 
//start implementation.
@implementation MyNativePlugin
 
//synthesize variables.
@synthesize dummyString;
 
 
//define methods.
-(id) init {
   return self;
}
 
-(void) doMyNativePluginStuff: (NSString*) passed_name {
 
  //Do something with the passed name.
  //Here we just log the passed_name
  NSLog( @"%@", passed_name );
 
}
 
//end implementation.
@end

Now that we have our plugin class ready, the next step is to setup a UnityAppController.


UnityAppController explained.


When Unity3D creates a xCode project, it creates a main entry point for the Objective-C application. This entry point is the UnityAppController.
Like our plugin class this class also consists of 2 parts.
1. UnityAppController.h
2. UnityAppController.mm

Now, our goal is to get our class initialized on start-up AND call our class after some unity event.
We could open the UnityAppController files and handcode our initialisation and implementation code inside of these files. But the downside is that everytime Unity3D builds ( or replaces ) a new xCode project, you will have to do this hand-coding stuff again…. and again…. and again.

We definitely need something more robust and more automated.
What we need is to subclass the UnityAppController to keep all unity generated codes but easily implement our own.
Enter “IMPL_APP_CONTROLLER_SUBCLASS

IMPL_APP_CONTROLLER_SUBCLASS is a method that will take a class as parameter, subclasses it from UnityAppController and make it the new AppControlller being called by Unity3D as entry point.

For this to work we need to make our own UnityAppController implementation with our codes.
This can be fairly simple.
Below a quick 1 file implementation of an AppControlller subclass (header and body in 1 file )

?View Code OBJECTIVE-C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
 
//import the basics.
#import "MyNativePlugin.h"       //our newly made plugin.
#import "UnityAppController.h"   //our link to the base class.
 
 
 
 
//------- start header ----------------
//start interface.
@interface MyNativePluginAppController : UnityAppController  //extend from UnityAppController.
 
@property ( retain, nonatomic ) MyNativePlugin* myNativePlugin;
 
//define methods.
-(void) initNativePlugin;
-(void) doCallNativePlugin:(NSString*) name;
 
//end interface.
@end
 
 
 
 
 
 
//------- start body ---------------
@implementation MyNativePluginAppController
 
//synthesize variables.
@synthesize myNativePlugin;
 
 
// overriding main unity entry point.
 
-(void) startUnity: (UIApplication*) application {         
   [super startUnity: application];  //call the super.
   [self initNativePlugin];          //call method to initialize our plugin class.
}
 
-(void) initNativePlugin {
   myNativePlugin = [[myNativePlugin alloc] init];  //initialize our plugin class.
}
 
-(void) doCallNativePlugin:(NSString*) name {
  [myNativePlugin doMyNativePluginStuff:name ];  //call plugin
}
 
@end
 
//setting this as app controller.
IMPL_APP_CONTROLLER_SUBCLASS( MyNativePluginAppController )

Save this file as MyNativePluginAppController.mm


To recap what we just did.


1. We created a class (header and body) to hold our custom plugin code called MyNativePlugin.

2. We created a main entry point which subclasses UnityAppController and initialises our plugin class on start-up called MyNativePluginAppController.

Let’s continue our journey.


Building the connection with Unity3D.



In order for Unity3D to call our Objective-C, we will need something that is called an “extern” method.
This method defines a method which will be exposed externally and can be called from Unity3D. Because our flow goes from Unity3D to Objective-C, this method will need to be in our Objective-C entry point, thus the MyNativePluginAppController.mm needs to be modified to have such a method.

?View Code OBJECTIVE-C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
 
//import the basics.
#import "MyNativePlugin.h"       //our newly made plugin.
#import "UnityAppController.h"   //our link to the base class.
 
 
 
 
//------- start header ----------------
//start interface.
@interface MyNativePluginAppController : UnityAppController  //extend from UnityAppController.
 
@property ( retain, nonatomic ) MyNativePlugin* myNativePlugin;
 
//define methods.
-(void) initNativePlugin;
-(void) doCallNativePlugin:(NSString*) name;
//end interface.
@end
 
 
 
 
 
 
//------- start body ---------------
static MyNativePluginAppController* delegateObject;   // <--- a static object is defined to be called from "extern" method.
 
@implementation MyNativePluginAppController
 
//synthesize variables.
@synthesize myNativePlugin;
 
 
// overriding main unity entry point.
 
-(void) startUnity: (UIApplication*) application {         
   [super startUnity: application];  //call the super.
   [self initNativePlugin];          //call method to initialize our plugin class.
 
   delegateObject = self;                                       // <--- we assign this instance to the static delegateObject.
}
 
-(void) initNativePlugin {
   myNativePlugin = [[myNativePlugin alloc] init];  //initialize our plugin class.
}
 
-(void) doCallNativePlugin:(NSString*) name {
  [myNativePlugin doMyNativePluginStuff:name ];  //call plugin
}
 
 
extern "C" {                                                             // <-- we define our external method to be in C.
    void CallNativePlugin( const char* name ){
        NSString* justName = [[NSString alloc] initWithUTF8String:name]; // <-- convert from C style to Objective C style.
 
        [delegateObject doCallNativePlugin:justName ];                   // <-- call method to plugin class
    }
}
 
@end
 
//setting this as app controller.
IMPL_APP_CONTROLLER_SUBCLASS( MyNativePluginAppController )

This will conclude the MyNativePluginAppController.mm

To recap what we did.

1. We created our plugin class which will do our native stuff.

2. We created our UnityAppController subclass, which has our custom initialisation methods.

3. We added a “delegateObject”, which is a static variable in the UnityAppController class of type MyNativePluginAppController.
We need this to be static because the “extern” method cannot acces variables defined inside the AppController subclass. This way, we ensure it is always accessible.

4. We assigned the instance of the UnityAppController subclass to the delegateObject in startUnity.

5. We added an “Extern” method which Unity3D can call and will trigger our plugin.

!! It is important to have our newly made UnityAppController subclass saved outside of our xCode project. We will add this later inside the Unity Project.

Next.
We need to compile our library file ( .a ), which we can use inside Unity3D.

First thinks first.
Be sure your xcode project structure looks like this:
iOS_project_view

Now connect an iOS device (preferably a 64 bit device) to the Apple computer.
Select the project in the project view.
Make sure you have the correct architectures selected:
iOS_architectures

For my plugin i am going to support:
- armv7
- arm64

But you can also add:
- armv6
- armv7s

These are the architectures on which your plugin will run. Other architectures will fail loading the plugin as you have not supported them.

Next, select the correct target and set target device to connected device:
iOS_targets
And press “Run” or the Play button.

Your .a file has been made.
You can check this by looking at the project view and seeing that the .a file has no red characters anymore.
iOS_compiled

Right click the .a file and select “Show in finder”, which will take you to the file location.

!! In this post i will not cover the simulator architectures as my blog post is becomming too long.


Congratulations!
You have succesfully compiled a iOS library to be used in mobile architectures.

Now lets go on to the Unity3D implementation part of our native plugin!

Steps to reproduce in Unity3D:
1. Open Unity3D
2. Open your project.
3. Create a “Plugins/iOS” folder inside the project’s “Assets” folder.
4. Place the generated .a file inside the “iOS” folder together with “MyNativePluginAppController.mm” and the “MyNativePlugin.h” files.

Your plugin is now ready to go!

Let’s look at the C# code to call our native plugin.

1
2
3
4
5
6
7
8
9
10
11
12
 
     //connect to the extern method in Objective-C
     [System.Runtime.InteropServices.DllImport("__Internal")]
     extern static public void CallNativePlugin( string filename);
 
 
     //Call the method from Unity3D.
     CallNativePlugin( "this is a string");
 
 
     //Voila! 
     // Native plugin has been called.

Next time we will have a look at calling Unity3D’s build scripts.

Happy Coding!
\o Rackdoll

Creative Commons License
[Unity3D] Writing native plugins: iOS by Script.it, unless otherwise expressly stated, is licensed under a Creative Commons Attribution-Noncommercial-No Derivative Works 3.0 Netherlands License.

Comments are closed.

Categories
Archives
Live Supporters