One of my favorite things about Unity is how easy it is to port games to multiple platforms. The simplest of games require no work to target a new platform. Literally none. This makes developing in Unity a time saver because there are very small porting costs.
However keeping the game purely within the engine comes with drawbacks. Primarily, little system integration. Many platforms have features outside of the game which you might want to take advantage of. For instance, iOS has twitter sharing built in to the OS. This is a feature Unity does not implement. So how do we use it? Through the use of plugins.
Plugins allow you to call into code written and potentially compiled outside of Unity. This is extremely powerful because it allows you to do pretty much anything the platform you’re targeting can do. Going back to the iOS twitter example, you can call into the native Cocoa Touch APIs.
Let’s try this out by writing a simple plugin to show a dialog to post to twitter. This API is built into iOS versions 5 and newer. I will be assuming you are targeting at least that version. If you are targeting something older you will need to include checks for the iOS version before calling the API. We need two functions. The first will check whether we can post to twitter. The second to do the posting. The social sharing API is documented at https://developer.apple.com/library/ios/documentation/NetworkingInternet/Reference/SLComposeViewController_Class/Reference/Reference.html. It will come in handy.
The mono runtime makes calls into native code using C standards. Any language that follows the C calling convention can be called. Unfortunately Objective-C does not follow this convention. We will have to wrap Objective-C APIs with C code to make it available to Unity. this is done by putting our calls inside C functions and using a C compiler to compile it. Our file will contain calls into C++ so we need to use a .mm
file with Extern "C" {
at the top and }
at the bottom to avoid name mangling issues.
// Check if c++ compiler #if __cplusplus Extern "C" { // Avoid name mangling issues #end if #if __cplusplus } // Close Extern C #endif
Put this file in the Assets/Plugins/iOS/
folder in your project. This is a special folder. When you build for iOS, any native libraries or code in this folder are copied to the Xcode project where they are compiled and linked with the rest of Unity.
Now, in Unity create a new C♯ file. This file will act as the Unity side to the plugin.
// Notice it doesn't extend MonoBehaviour public class TwitterSharing { }
Let’s start by adding a simple call to find out if Twitter is available. SLComposeViewController
contains a class method called isAvailableForServiceType
to check the availability of a given service. The constant for Twitter is SLServiceTypeTwitter
.
// In Social.framework, imports social sharing classes and constants #import <Social/Social.h> #if __cplusplus Extern "C" { #end if bool _isTwitterAvailable() { // Returns true when Twitter is available return [SLComposeViewController isAvailableForServiceType:SLServiceTypeTwitter]; } #if __cplusplus } #endif
When calling native code, mono “marshals” the parameters and return values. Basically it copies stuff. This is needed because C and C♯ have different memory management. Primitive types, integer, floating point, and boolean, are automatically converted to and from their respective types. Because of this we can use a bool in C♯ just as we would in C. Strings work a little differently as we will see later.
using System.Runtime.InteropServices; // For calling into native code public class TwitterSharing { // The plugin is only compiled for the iOS platform, check if Unity is compiling for it. #if UNITY_IPHONE // Specifies which binary file contains the function. // For plugins you write the library is always "__Internal". [DllImport("__Internal")] // Has the same signature as in the C file private extern static bool _isTwitterAvailable(); #endif public bool IsTwitterAvailable() { #if UNITY_IPHONE // Calls into native code return _isTwitterAvailable(); #else // The method needs to always return something. return false; #endif } }
Now you can check whether Twitter is available from game code. Debug.Log(TwitterSharing.IsTwitterAvailable());
will print whether twitter is available when running on iOS. But this is no fun. We want to post to Twitter. Let’s add a function to do just that. But first, let’s cover how mono marshals strings from C♯ to C. When you send a string
as a parameter mono converts it into a null terminated UTF8 character array (char[]
ending with \0
) for native code. The sharing service expects an NSString
so we have to convert it using stringWithUTF8String
.
#import <Social/Social.h> // Contains functions to call into Unity #include "UnityInterface.h" #if __cplusplus Extern "C" { #end if bool _isTwitterAvailable() { return [SLComposeViewController isAvailableForServiceType:SLServiceTypeTwitter]; } void _postToTwitter(char* text) // Make note of the signature { // Create a view for Twitter sharing SLComposeViewController* shareSheet = [SLComposeViewController composeViewControllerForServiceType:SLServiceTypeTwitter]; // Convert characters into NSString. [shareSheet setInitialText:[NSString stringWithUTF8String:text]]; // Get the Unity native view and tell it to show the twitter view. [UnityGetGLViewController() presentViewController:shareSheet animated:YES completion:NULL]; } #if __cplusplus } #endif
Now to make the unity side.
using System.Runtime.InteropServices; public class TwitterSharing { #if UNITY_IPHONE [DllImport("__Internal")] private extern static bool _isTwitterAvailable(); // This specifies which binary file contains the function. // For plugins you write the library is always "__Internal". [DllImport("__Internal")] // Notice here we say the parameter is a string private extern static void _postToTwitter(string text); #endif public bool IsTwitterAvailable() { #if UNITY_IPHONE return _isTwitterAvailable(); #else return false; #endif } public void PostToTwitter(string text) { #if UNITY_IPHONE // Call native function _postToTwitter(text); #endif } }
Now we can do something like the following from within the game.
if (TwitterSharing.IsTwitterAvailable()) { TwitterSharing.PostToTwitter("Tweet!"); }
This will bring up a twitter share dialog for the user to tweet with the text “Tweet!” already filled in. Some things to go over in the future include performing an action when the player successfully tweets and attaching a screenshot/image.
More information is available at http://www.mono-project.com/Interop_with_Native_Libraries.