Specific steps to set up a plugin for iphone in XCode

I understand that plugins are now supported when building for iphone in Unity.

The unity documentation does not spell out any specifics on how to set up the XCode project for building a plugin for this.

When i go to create a new project in XCode, the only 'Bundle' option is under the Mac OS X section and not iphone OS. if i select that and then change the architecture in the project settings to 'iPhone Device 3.0' and add a code signing identity, i get 139 errors when i try to build.

i would appreciate some help from anyone who has successfully created a plugin for iphone, and the specific steps they had to take to get the project set up correctly.

thanks!

1 Answer

1

You need to add some code to the Xcode project that is built when you Build for iPhone.

It's quite a process to get the workflow for this right, but it's worth doing the homework. I published my project once to a folder named 'Unity-iPhone' in my Unity project folder, copied it out to a new, separate location, and then added the following script to my Unity project's Assets/Editor/ folder as PostprocessBuildPlayer (making sure to give executable permissions to the file):

#!/bin/bash
cp -f ./Unity-iPhone/Data/* ~/Documents/Xcode/my-project/Data/.
cp -f ./Unity-iPhone/Libraries/* ~/Documents/Xcode/my-project/Libraries/.

What this does is copy all the bits that actually change in an Xcode build and leaves the rest of your project intact. I have lost one too many days trusting Unity's own 'Append' feature.

You can then develop happily in Xcode in the ~/Documents/Xcode/my-project project. To get the plugin going, first rename ProjectAppDelegate.m to ProjectAppDelegate.mm. This tells the compiler to process C and C++ code in the file (or something). Then, above the @implementation line near the top, add:

static ProjectAppDelegate delegateObj;

Then, in your applicationDidFinishLaunching method, you set this value:

- (void)applicationDidFinishLaunching:(UIApplication *)application {
    delegateObj = self;
    ...
}

This allows your plugin's C code to talk to your application's Objective-C code.

Then add the following code at the bottom of the App Delegate .mm file. I have included a sample of how to pass structured data back and forth (I struggled with this, should save you some time).

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([delegateObj.someStringProperty UTF8String]);
    	myStruct->intValue = [delegateObj.someNSNumberProperty intValue];

    	printf("-> myStruct->stringValue %s
", myStruct->stringValue);
    	printf("-> myStruct->intValue %s
", myStruct->intValue);
    	NSLog(@"-> complete.");
    }

    void _HaveObjCDoSomething () {
    	NSLog(@"_HaveObjCDoSomething");
    	[delegateObject doSomething];
    	NSLog(@"-> complete.");
    }	
}

Finally, in Unity, in a C# class in Assets/Plugins/, you'd have something like:

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();
    	}
    }
}

Of course, there is more to it than just what you have here, but this is a good kickstart. Have fun :)

If you change your PostprocessBuildPlayer script to the following, it copies files recursively, adding subfolders (such as added video files etc): #!/bin/bash cp -Rf ./Unity\ Player/Data/* ./Xcode\ Project/Data/. cp -Rf ./Unity\ Player/Libraries/* ./Xcode\ Project/Libraries/.

Thanks @enekochan your comment about RegisterMonoModules.cpp helped me!

How does version control work with this setup? Separate repositories for the Unity project and the Xcode project? Any example/template projects on GitHub with everything set up? Just asking. I will be doing some experimentation myself.

A fuller description and updated version of @enekochan's script can be found on his own site at [http://tech.enekochan.com/2012/05/28/using-the-xcode-simulator-with-a-unity-3-native-ios-plug-in/][1]. [1]: http://tech.enekochan.com/2012/05/28/using-the-xcode-simulator-with-a-unity-3-native-ios-plug-in/