Create a Unity project. Go to “File->Build Settings”, select the iOS platform and then “Player Settings…”. In “Other Settings” on “Identification” configure the “Bundle Identifier” for example with “com.YourCompanyName.YourProductName”
If you don’t do this you would get this error while doing “Build”.
NOTE: If you are going to run the application on the Xcode Simulator you must configure the “SDK Version” to a Simulator version (“iOS Simulator Latest” for example or any other iOS desired version). If you don’t do so (if you select for example “iOS Latest” or any other Device version) you will get hundreds of errors about not defined function for i386 architecture when compiling the Xcode project. If you don’t do this step correctly you will have to do “Build” again in Unity and copy again the “Libraries
” and “Data
” folder to your Xcode project as I will explain later.
Once we have configured both “Bundle Identifier” and “SDK Version” save the scene and the project anywhere in your computer. When we run “Build And Run” a new Xcode project will be created (called “Unity-iPhone”) inside our Unity project folder in a new folder with any name we want, for example “iOS Build”. You HAVE to copy that folder to another place in your computer, for example were your Xcode projects usually are.
Any time we make a change in the Unity and do a “Build” we will be asked to save the Xcode project it creates, but we can overwrite the original (that one created inside the Unity projects folder) with no problems. But we have to copy ALL the files in “Data
” and “Libraries
” from the newly created Xcode project by Unity to our Xcode, the one we copied on the very fist time. A script like this can be run after we do a “Build”:
#!/bin/bash
cp -Rf ./iOS\ Build/Data ~/Documents/Xcode/my-project/
cp -Rf ./iOS\ Build/Libraries ~/Documents/Xcode/my-project/
Any way the best option to gain the same effect is to create a PostprocessBuildPlayer
script (be careful, this file must have execution permissions) inside “Assets/Editor
” (thus we have to create a folder called Editor
inside the “Project” view and copy there the script) with the same content as before so the files will be copied automatically to our Xcode project every time we do a “Build” in Unity. The PostprocessBuildPlayer
scripts can be shell script or Perl script.
Open our Xcode project and change the extension of the “iPhone_target2AppDelegate.m
” file (it can have a different name depending on the Unity version but there is only one “AppDelegate” file so look for a file with “AppDelegate” on its name) to “iPhone_target2AppDelegate.mm
” (2 m’s at the end and not only 1). This will make Xcode compile the file as Objetive-C++.
In that file just above “ ...
” write:
static iPhone_target2AppDelegate *delegateObject;
Inside the “applicationDidFinishLaunching
” method add the following line (in bold here) as the first instruction to execute:
- (void)applicationDidFinishLaunching:(UIApplication *)application {
delegateObject = self;
...
}
At the end of the “AppDelegate” (the “iPhone_target2AppDelegate.mm
” file), but before the
clausule, write down the following code. This will create 2 methods that will be called from Unity. One of the accepts an struct by reference so we can change its values (the methods to obtain the new values are not defined, they are just examples) and the other one just executes a method from the “AppDelegate” you should create.
NSString* CreateNSString (const char* string) {
return [NSString stringWithUTF8String: string ? string : ""];
}
char* MakeStringCopy (const char* string) {
if (string == NULL) return NULL;
char* res = (char*)malloc(strlen(string) + 1);
strcpy(res, string);
return res;
}
struct MyStruct {
char* stringValue;
int intValue;
};
extern "C" {
void _PassStructFromObjCToUnity ( struct MyStruct *myStruct ) {
NSLog(@"_PassStructFromObjCToUnity");
myStruct->stringValue = MakeStringCopy([delegateObject.someStringProperty UTF8String]);
myStruct->intValue = [delegateObject.someNSNumberProperty intValue];
printf("-> myStruct->stringValue %s\n", myStruct->stringValue);
printf("-> myStruct->intValue %i\n", myStruct->intValue);
NSLog(@"-> complete.");
}
void _HaveObjCDoSomething () {
NSLog(@"_HaveObjCDoSomething");
[delegateObject doSomething];
NSLog(@"-> complete.");
}
}
Now we go back to our Unity project and inside a C# class in Assets/Plugins/
we could write something like this that will execute the native code methods defined inside “extern "C"
” clausule.
using UnityEngine;
using System.Runtime.InteropServices;
public struct MyStruct
{
public string stringValue;
public int intValue;
}
public class OBJCPlugin
{
[DllImport ("__Internal")]
private static extern void _PassStructFromObjCToUnity ( ref MyStruct myStruct );
public static MyStruct PassStructFromObjCToUnity ()
{
Debug.Log("OBJCPlugin.PassStructFromObjCToUnity");
MyStruct data = new MyStruct();
if ( Application.platform == RuntimePlatform.IPhonePlayer )
{
_PassStructFromObjCToUnity( ref data );
}
return data;
}
[DllImport ("__Internal")]
private static extern void _HaveObjCDoSomething ();
public static void HaveObjCDoSomething ()
{
Debug.Log("OBJCPlugin.HaveObjCDoSomething");
if ( Application.platform == RuntimePlatform.IPhonePlayer )
{
_HaveObjCDoSomething();
}
}
}
We have just modified the Unity project to call the native code in iOS so we have to do “Build” again and copy the “Libraries
” and “Data
” folders to our Xcode project (but if we created the PostprocessBuildPlayer
script those folders will be copied automatically. This way both the native code in iOS and the calls in C# get together.
If you want to be able to run this Xcode project in the Simulator read the post “Using the Xcode Simulator with a Unity 3 Native iOS Plug-In“.
Sources: http://answers.unity3d.com/questions/10110/specific-steps-to-set-up-a-plugin-for-iphone-in-xc.html
http://www.tinytimgames.com/2010/01/10/the-unityobjective-c-divide/