The Unity/Objective-C Divide

I love Unity! The Unity iPhone engine is a fantastic piece of technology, allowing us to quickly develop games without having to worry about a lot of the underlying stuff. However, Unity is essentially an additional layer on top of the iPhone SDK, which means that while the iPhone SDK may advance with new features, the Unity engine is generally a step behind with adding these features in a manner that can be easily accessed through script.

Enter Objective-C: This is the language that iPhone apps are usually written in. Unity provides some support for calling native Objective-C code from Unity script, but only for Advanced licensees and only in one direction. This can be pretty limiting especially if you’re trying to use a lot of the fancy features in iPhone OS 3.0. Sure, you can pass around information using the PlayerPrefs trick. But this has the problem of not being instantaneous, and it forces the app to constantly poll for new commands, which probably isn’t a good idea performance-wise.

What follows is a method for immediately calling Objective-C code from Unity script (for both Basic and Advanced licenses) and also vice versa! That’s right, two-way communication between Objective-C and Unity script that’s instantaneous.

Unity to Objective-C (for Unity 3 and Unity iPhone 1.x Advanced licensees)

First, for the Unity 3 and Unity iPhone 1.x Advanced license users of Unity, you already have a quick and easy way of calling Objective-C code from Unity script. In C#, just do the following:

[System.Runtime.InteropServices.DllImport("__Internal")]
extern static public int AwesomeFunction(int awesomeParameter);

Then, in a C/C++/Objective-C file somewhere in your Unity-built Xcode project, do the following:

int AwesomeFunction(int awesomeParameter)
{
   // My awesome code goes here.
 
  return somethingAwesome;
}

You may have to wrap your function prototypes in

extern "C" { ... }

if you’re using C++/Objective-C++ due to name mangling.

Unity to Objective-C (for Unity iPhone 1.x Basic licensees)

Now for you Basic license users, you’ve been used to sending a command to Objective-C from Unity script like this:

PlayerPrefs.SetString("Commands",
   String.Format("AwesomeCommand|{0}|{1}", awesome1, awesome2));

Or something similar. You’ll still be sending commands this way. However, in Objective-C, you probably have an NSTimer that is constantly polling to see if the “Commands” key in NSUserDefaults has a string in it. You might even have some fancy queueing system set up so that you can call multiple commands in a row without them overwriting each other. The issue, though, is that if you need a result back from one of these commands, you can’t get it instantaneously. You have to set up some kind of polling system in Unity script to wait for a return result to show up in some other PlayerPrefs key. Messy.

But there’s a better way. Let me introduce you to Key-Value Observing. The iPhone SDK provides a method for setting up “observers” for certain things currently going on in the system. It just so happens that one of the things that you can “observe” is NSUserDefaults. That’s our ticket!

Somewhere in your Objective-C code (where you might normally set up the NSTimer for polling the NSUserDefaults), place the following code:

[[NSUserDefaults standardUserDefaults] addObserver:self
   forKeyPath:@"Command" options:0 context:nil];

Then, create a method like the following:

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
   change:(NSDictionary *)change context:(void *)context
{
   // Command parsing code goes here.
}

The observeValueForKeyPath method is essentially the function you had before that parsed the commands from PlayerPrefs/NSUserDefaults. What the addObserver moethod does is set self to be an observer of NSUserDefaults, meaning that every time you set the PlayerPrefs in Unity script, the observeValueForKeyPath method gets called immediately!

What this now allows you to do is to set a result key in Objective-C and immediately access it in Unity script. Here’s an example:

// Objective-C code
if([[[NSUserDefaults standardUserDefaults] getObjectForKey:@"Command"]
   isEqualToString:@"DoSomething")
{
   // Return a result by setting the key here.
   [[NSUserDefaults standardUserDefaults] setInteger:1 forKey:@"Result"];
}
// Unity C# code
int CallDoSomething()
{
   PlayerPrefs.SetString("Command", "DoSomething");
   // Before, doing something like this would be very bad!
   return PlayerPrefs.GetInt("Result");
}

As you can see, you can get immediate results from the Objective-C code! Hooray!

Objective-C to Unity (for everyone)

NOTE: Unity provides a C function called UnitySendMessage that you can use to send messages back to your scripts. However, it’s not nearly as robust as this method since it waits until the next frame to execute and you can’t call static methods since you have to supply the function with a GameObject name. However, if you don’t need the extra functionality, I recommend you use that instead.

Where all licensees meet is in the need for having Objective-C code call back to Unity script. There are many instances where you need to capture an event in Objective-C and then pass this off immediately to your Unity scripts so that you can update your game. While using NSUserDefaults to send information back to PlayerPrefs is certainly doable, a polling system would need to be set up in Unity script and it would never be instantaneous.

To get this working, you need to know a little bit about the underlying Unity frameworks. Unity uses a framework called Mono, which is an open-source cross-platform implementation of .NET. When you use something in the System namespace or String.Format, you’re actually using .NET libraries, and not something Unity-specific. While the actual invocation of the Mono runtime isn’t visible from inside the Xcode project, that doesn’t mean we can’t still have access to the Mono functions.

You can do amazing things with C. You can also do terrible things with C. That’s why it’s preferred by so many programmers, as data and memory in C is completely malleable. You can do almost anything with it. Even though we don’t have access to the Mono header files in the Unity Xcode project, by doing some handy Internet searches, we can get the function prototypes of all of the Mono functions we’ll need to get what we want.

In addition, many Mono functions require or return special MonoTypes. This would be a problem in C#, but in C a void pointer cures all! For our purposes, here are all of the MonoTypes that we need:

typedef void* MonoDomain;
typedef void* MonoAssembly;
typedef void* MonoImage;
typedef void* MonoClass;
typedef void* MonoObject;
typedef void* MonoMethodDesc;
typedef void* MonoMethod;
typedef int gboolean;

To be honest, you don’t even really have to set up the typedefs, but it makes the function prototypes look cleaner and more readable. Remember, since pointers simply point to memory, they don’t really need a type.

Now for the function prototypes that we’ll need. The following is the bare minimum of functions needed to accomplish calling Unity script code from Objective-C. (Remember: You may need to wrap these in extern “C” { … } if you’re using C++/Objective-C++ due to name mangling.)

MonoDomain *mono_domain_get();
MonoAssembly *mono_domain_assembly_open(MonoDomain *domain, const char *assemblyName);
MonoImage *mono_assembly_get_image(MonoAssembly *assembly);
MonoMethodDesc *mono_method_desc_new(const char *methodString, gboolean useNamespace);
MonoMethodDesc *mono_method_desc_free(MonoMethodDesc *desc);
MonoMethod *mono_method_desc_search_in_image(MonoMethodDesc *methodDesc, MonoImage *image);
MonoObject *mono_runtime_invoke(MonoMethod *method, void *obj, void **params, MonoObject **exc);

Here’s where we need to explain things: We call Unity script functions by using the mono_runtime_invoke function. However, before we can call this, we need to get the MonoMethod (essentially, the pointer to the Unity script function we want to call) from the assembly file that was compiled by Unity when you hit “Build & Run”. So how do we get that?

First things first, we need to get the domain where all of the Unity scripts are running. In the vaguest of terms, a domain is kind of a box where scripts are run. So we need to grab the Mono domain first:

MonoDomain *domain = mono_domain_get();

Simple enough. We now have the location where all of the Unity scripts are being run. Now to get the MonoMethod, we first need to get a reference to the assembly that contains the function. In your Xcode project, if you look in the Libraries group, you’ll see a bunch of ‘dll.s’ files. These files get compiled by Xcode on Build into DLL files that get placed into your app’s Data folder. If you don’t believe me, take your built Unity app, Show Package Contents on it, and then browse around.

Knowing the folder structure of the built application is actually important to this process, as we’re going to be directly referencing these DLLs for the next step. We need to get the assembly itself so we can read through it and find our functions. Here’s how to do that:

NSString *assemblyPath = [[[NSBundle mainBundle] bundlePath]
   stringByAppendingPathComponent:@"Data/Assembly - CSharp.dll"]
MonoAssembly *assembly = mono_domain_assembly_open(domain, path.UTF8String);
MonoImage *image = mono_assembly_get_image(assembly);

You’ll notice we put in the path of the built DLL file from the application’s main bundle. Note that the assembly you’re looking for might be different for the class method you’re trying to access. To find out the correct assembly, highlight the script in Unity and go into Debug mode in the Inspector. The Assembly Identifier should tell you the name of the assembly. We then get the assembly from our domain. The MonoImage is something we’ll need for the next step.

We have our assembly now, and we can start accessing the functions inside it. To do this, we need to tell Mono what functions we’re looking for. (For ease of use, I’m only going to be looking for static methods in classes without a namespace. If you want to access specific objects or call non-static methods, I suggest you read up on the official documentation on Embedding Mono.)

MonoMethodDesc *desc = mono_method_desc_new("AwesomeClass:AwesomeFunction()", FALSE);
MonoMethod *method = mono_method_desc_search_in_image(desc, image);
mono_method_desc_free(desc);

Notice that the MonoMethodDesc (literally, MonoMethod Description) is created by giving mono_method_desc_new a string that contains the method that we want to call. Remember to put the class name before the function and follow it with a colon, not a period. If you want to access a method with parameters, do something like this:

"AwesomeClass:AwesomerFunction(int,int,int)"

Formatting is very important. There shouldn’t be any spaces between the method parameters. After getting the description, we then search the assembly image for the method. Doing all of this searching could impact performance in your app, so it’s recommended that you do all of this in an initialization function and store the pointers to the various MonoMethods you want to use. After we have the method pointer, we free the MonoMethodDesc that was created.

Now you have your MonoMethod! And you say you want to call it! We can do that very easily now:

mono_runtime_invoke(method, NULL, NULL, NULL);

See? Very simple. If your method had some parameters in it, you need to pass the arguments as a void pointer array to the third parameter of mono_runtime_invoke, like so:

int param1 = 1;
int param2 = 27;
void *args[] = { &param1, &param2 };
 
mono_runtime_invoke(method, NULL, args, NULL);

We are essentially passing the arguments by their address via way of an array.

And that’s it! There are a few caveats to using this method of invoking Unity script methods: Firstly, it’s slow. It would not be recommended to call these many times per frame or even once every frame. It’s primarily good for callbacks that occur every now and then (like for GameKit connection results, etc). Secondly, you’re putting a lot of trust in the Mono and Unity developers to not change things around too drastically. If you upgrade Unity and your code suddenly stops working, you’ll need to downgrade to the previous version of Unity or muck around until you can find out what was changed.

Conclusion

Finding all of this out was a long weekend project for me, and it has definitely been worth it. Functionality that I simply didn’t think was possible without official Unity support is now immediately available, and I am now able to bounce back and forth between Unity script and Objective-C to get access to whatever I need. There are performance issues to consider, but on the whole the flexibility outweighs the potential performance pitfalls. It just requires you to properly plan your app with these things in mind.

Some may read this article and think that the Advanced license is obsolete with these methods. However, I don’t think that’s true. The simple fact that Advanced users can use namespaces like System.Runtime.InteropServices and System.Xml are worth it alone, especially when you consider that you can’t send something as complex as a struct or class through a PlayerPrefs string (I guess you could, but it wouldn’t be very pretty). This is really just a stopgap measure to allow Basic license users access to some of the native functionality that isn’t available in Unity yet. The splash screen requirement alone was enough for us to upgrade!

In addition, using the Objective-C to Unity callback functionality is not for the faint of heart, and certainly not for those who aren’t well-versed in the art of programming. Just be sure to design your games around what you can do, not around what you wish you could do.

I hope you’ve found this both educational and useful and that you’ll consider contributing your own findings to the Unity community.

Tags: ,

  • http://www.zenfrogs.com Brandon

    Awesome post! Seriously this is really cool. I’m kind of a noob to the Unity world (seeing as I’ve only done 3-4 projects in Unity) and this is exactly what I needed. You seriously just saved me from running a timer and killing my performance by checking strings every 10th of a second. This is a perfect situation and you just got yourself another reader

  • http://www.tinytimgames.com Jerrod Putman

    Thanks! I’m really glad you found it helpful. If you need anymore specific examples, you can look in the Unity plugin for OpenFeint (which I helped write). It uses this method as well, and it uses a few more tricks that what I was able to put in this article (like sending strings back from Objective-C to Unity).

  • http://www.cometsandcraters.com imparare

    Hey Jerrod, just posted on the Unity forums to say thanks.

  • Dakota

    Thank you for this. It works great!!

  • nickdep

    Awesome thnx man!!!

  • http://tapmeinc.tumblr.com/ Justin

    Jerrod, thanks a lot for this post.

    I realize its maybe a year after the fact, but perhaps you can advise me on something. I’m working with another dev on his unity game and we are trying to make use of a couple of plugins from Prime31 and a plugin I’m working on using script that links Unity and ObjC through the methods outlined in your article.

    The problem I’m coming across is that if I put my plugin’s script in the “Plugins/” directory (or the “Standard Assets/” or “Pro Standard Assets/” directories) along with the Prime31 scripts, I’ll be able to make calls from Unity to ObjC just fine but when I go to make a call back by searching for a MonoMethod*, I get null returned. If I move my script out of the “Plugins/” directory, the MonoMethod*s are found properly, but then I cannot call the scripts from Javascript (which most of the other scripts were composed in). If I don’t include the other plugins, my script will work fine even in the “Plugins/” directory.

    Have you ever observed any behavior like this? I can’t figure out why its happening. I’ve tried combinations of using different namespaces and making intermediate calls to “mono_class_from_name()” and “mono_method_desc_search_in_class()” to no avail. Thanks in advance, and thanks again for the post — it helped me understand much more clearly what needs to happen.

  • http://www.tinytimgames.com Jerrod Putman

    @Justin: This is just my best guess, but make sure you’re loading the correct assembly in your Objective-C code. My code here mentions loading “Assembly – CSharp.dll”, but it may need to be “Assembly – CSharp-first-pass” or something similar.

    To figure out the proper assembly name, click on the script you’re trying to access (in Unity). In the Inspector, make sure Debug view is on, and you should be able to see the assembly that the script is compiled into.

    Again, that’s just my best guess, so I’m not certain that’s the problem you’re having.

  • http://www.gamieon.com Gamieon

    Just wanted to thank you for this write-up. I used your examples to get Game Center integration and In-App purchasing to work with my Unity app!

  • http://tapmeinc.tumblr.com/ Justin

    You are the smartest and coolest, moreover you were totally right(est). I wish I had checked back here sooner… it could have saved me a lot of headache. Better late than never I suppose. Thanks much.

  • http://www.baby-ninja.com Michael

    Hello i try to call Objective-C from Unity like this:

    extern “C” {

    int AwesomeFunction(int awesomeParameter)
    {
    // My awesome code goes here.

    printf(“awesomeParameter= “, awesomeParameter);
    //console output is only awesomeParameter=

    //how to call a function here like this [self myFunction]; ????
    //[self myFunction];

    return nil;
    }

    }

    The parameter is not recieving when i print it to the console like above?
    And another question, how can i call a Objective-C Function inside?

    Thanks

  • http://www.baby-ninja.com Michael

    OK the parameter recieves like this…sorry

    printf (“awesomeParameter: %i”, awesomeParameter);

  • Jai

    Hi,

    thanks for the post.

    How would I pass an NSString to a C# method that interprets it as a string?

    i.e.

    NSString* myParm = @”Hello World”;

    AwesomeClass:AwesomeFunction(string)

    thanks

    • brian chasalow

      to pass an NSString to a C# method that interprets it as a string,

      first you need to define this in your declarations (near your typedefs) MonoString *mono_string_new(MonoDomain *domain, const char *text);

      if you want to pass one or even multiple strings from obj-c to C#, make sure you declare the function name like this:

      myMethodDesc = mono_method_desc_new(“AwesomeClass:AwesomeFunction”, FALSE);
      myMethod = mono_method_desc_search_in_image(myMethodDesc, monoImage);
      mono_method_desc_free(myMethodDesc);

      and just leave out the bit where you might think to put (string, string, string) after AwesomeFunction.

      then to call your unity function from obj-c,

      const char* param1 = [myNSStringVariable UTF8String];
      MonoString* myString = mono_string_new(domain, param1);
      void *args[] = { myString };
      mono_runtime_invoke(myMethod, NULL, args, NULL);

      • Bruno Mikoski

        And how about to send a string from Unity (C#) to Objective-C? I’m trying to implement the Testflightapp SDK in one of my games, but i can’t find how send a string as parameter to a objective C function.

        • Jerrod Putman

          Set up the method in C# as normal:

          [DllImport("__Internal")]
          public static extern void CoolFunction(string coolString);

          And on the native code side create a function like so:

          void CoolFunction(const char *coolString)
          {
          NSString *nsCoolString = [NSString stringWithUTF8String:coolString];

          // Cool function code goes here.
          }

          Hope that helps.

          • Bruno Mikoski

            Thanks! Work like a charm!

  • Pingback: Ansca Mobile Blog » Archive » Corona SDK turns you into a FAST programmer!

  • kiro

    hi Jerrod
    first of all, thank u so much, that’s a really cool & useful post.
    I’m totally new to Unity/Objective c/Iphone/Mac, so yeah, it’s kind of tough for me and there’s 2 much 4 me to process.
    anyway, I made a very simple project using unity, then built the xcode project,
    inside the viewController, I put the definitions and function prototypes

    thing is, whenever I build the project, I get this error

    “mono_domain_get()”, referenced from:

    I think I’m missing a reference to a header or maybe a library, but I don’t know :)
    thnx in advance

    • brian chasalow

      you’re probably missing libmono.0.dylib – do a show package contents from your built app, find that file, and drag that file into your xcode project to link against it.

  • Claire

    Thank you so much, it was really useful for our team.

    I followed it to access Unity Script from XCode.

    But, I have encountered some problem that “Bad call to mono_mutex_lock” and “Assertion at domain” when I run Unity-iPhone-simulator.

    MonoDomain *domain = mono_domain_get(); // this code make such problem.

    I added libmono.2.0.1.dylib and Mono.Framework.

    I don’t know what I made mistake. I hope you can give me some advices and appreciate in advance!

    • Claire

      I found the way to solve it. ! :)

      • Ranza

        Hey Claire, how did you fix it?
        I’m having a similar problem.

  • Ranza

    I’ve added libmono.0.dylib , but I’m still geting an error
    “Undefined symbols for architecture x86_64:
    “_mono_domain_get”, referenced from:
    __canMakePayments in StoreKitBinding.o
    ld: symbol(s) not found for architecture x86_64″
    I’ll add that I’m trying to make a Mac bundle for Unity, reusing some prime31 iOS plugins.
    Anyone knows how to solve it?

    • rwar

      Seems there is an issue with Unity 3.4 and MonoDomain *domain = mono_domain_get(); Not sure how to fix it

  • Jerrod Putman

    There seems to be an issues with mono_get_domain on 3.4, but I’m not certain how to work around it. There are some discussions on the beta list about this, so I’ll update if I found out anything.

    • Yohann

      Hi Jerrod,
      Great post!
      I started implementing your solution for the advanced unity user using iOS 4.x. And everything worked fine.
      Now, upgrading to iOS5, I get this error:

      Undefined symbols for architecture armv7:
      “_AwesomeFunction”, referenced from:
      __Z19RegisterMonoModulesv in RegisterMonoModules.o
      ld: symbol(s) not found for architecture armv7
      clang: error: linker command failed with exit code 1 (use -v to see invocation)

      Have you encountered it as well? It’s giving me a headache, the code hasn’t changed. It’s got to be some settings somewhere that needs to be updated. Isn’t it?

      • Yohann (again)

        Solved the issue. It just happens that iOS5 compiles with LLVM compiler which is pickier on code than the previous one (GCC or GDB can’t recall) which you have to be more careful abut what you write, double check file extensions, headefrs, types, etc…

  • Daniel

    This tutorial is great!! I’m using Unity 3.4 and Xcode 3.2.6, and still works!! thank you!

  • Quzening

    Thank you so much Jerrod, I can see that this tutorial is really great. I wonder if this would also work for Unity Mac Standalone. One of Unity developer says that Unity does not allow callbacks from C (http://answers.unity3d.com/questions/201598/interop-wrapper-causes-unity-crash.html) but I am dying to callback from C to my C# script!