TECH TALK: Bridging Unity and the iOS SDK

Unity is a phenomenal game development tool with a seamless iOS deployment pipeline, but its user interface capabilities are mediocre. Our preferred solution is to utilise Unity’s plugin architecture to wrap our games in a native iOS UI that communicates with the Unity game environment.

We’ve been developing iOS applications with Unity since our first App Store release in 2009. Unity ships with an excellent development environment and provides a high quality pipeline into iOS application delivery.

As a product compiled with Mono, direct access to iOS APIs from Unity applications is restricted. In terms of gameplay and 3D performance this is of little consequence as runtime game logic and screen rendering is fully encapsulated in Unity, with API bindings provided for interactive features such as accelerometer data and touch detection. However, it’s our belief that user experience suffers when interfaces are built in Unity rather than with the UIKit framework and the iOS SDK. iOS has a rich heritage of interface conventions and behaviours, and it’s necessary to leverage these native features to produce the kind of sophisticated experience users expect from iOS apps.

Our preferred solution is to build our game interfaces in Xcode utilising native code. This allows us to play to the strengths of each platform – Unity provides the 3D engine, and the UIKit framework enables the seamless interactions and behaviours iPhone users are accustomed to.

Additionally, maintaining a game wrapper written in Objective-C gives us access to the full iOS SDK without being restricted to the native APIs that Unity has re-implemented. When multiple developers are working on a game, developers can contribute significant iOS features and compile the project regardless of whether they have access to or familiarity with the Unity development tool.

A native code bridge

For two-way communication between Objective-C and code running in Unity we make use of Unity’s plugin architecture to create a bridge between the execution environments.

Objective-C to Unity C#

We utilise UnitySendMessage to call methods in Unity from Objective-C. The Unity docs reveal that UnitySendMessage takes three parameters: the name of the target GameObject, the script method to call on that object and the message string to pass to the called method. These parameters must be sent as UTF-8 strings.

Unity to Objective-C

We can expose C functions in Xcode, and then add the same method signature to Unity. This ensures that Unity is aware of the external methods it can call at runtime. The method below logs a message from Unity to the Xcode console.

The work flow in action

NAVY SINK’EM is a multiplayer game that has UI and networking on the iOS side, talking to a Unity game environment. These environments have defined responsibilities corresponding to their unique strengths, and commands are fired between them to facilitate the execution of these responsibilities.

Layering UIKit and Unity

In the gameplay scenario pictured below, a UIView is layered over the Unity OpenGL view. When Unity detects that a ship has been touched, the camera centres the ship on the screen and a command is sent through the native interface to Objective-C. In response, UI elements detailing the ship’s status are prepared and the OS takes over touch detection. When a touch on the ship is detected a second time, this time courtesy of the ship’s coordinates mapped in the UIView screen space, iOS tells Unity to pause rendering and initiates drawing a shooting interface on the screen with Core Graphics.

We chose to draw the shooting interface (represented in this instance by the circular device overlaid on the ship) in a CGLayer with CGPath definitions based on the player’s touches. Drawing this in Unity would be visually less impressive, and would involve a greater code overhead as various effects (e.g. the ocean shader) would need to be independently toggled to free CPU cycles. Furthermore it’s our experience that Unity does not handle continuous touch detection particularly well, as Unity receives touch input asynchronously. This makes responding to multiple touches difficult. We greatly prefer the immediate event based touch input we receive from UIKit.

Once the player releases their shot, the OS sends a normalised vector representing the shot’s velocity and direction to Unity, which takes over calculating and rendering the shot’s trajectory. The results of the shot are shared with the OS.

Saving and recreating state

In any game of sufficient complexity, it’s necessary to save and recreate state across gameplay sessions. In an asynchronous multiplayer game like NAVY SINK’EM, players’ moves are shared over the air. A local copy of the game is updated as the player’s turn is taken, and synced with the server on completion. This reduces network traffic and allows players to take their turns in the absence of a network connection. When a player refreshes their games list, or in response to a remote push notification, the server pushes out updated games to the player’s app to replace the local copies.

iOS sends the current game state to Unity when a game is launched from the player’s list of active games. To comply with the restrictions on the native interface bridge, we serialise the relevant game data structures and pass the whole game through as a JSON string. In return, Unity sends JSON chunks back to iOS with the updated data as moves are made. In the case of NAVY SINK’EM, the OS piggybacks on the methods used to serialise the game object for sending it to the server.

iOS and Unity implement the game data packet independently of one another, according to the needs of each environment and the features of the respective languages.

Handling audio with Unity

Unity takes all the work out of handling audio. As the Unity environment is persistent throughout the life of the application, we can take advantage of this and route the entirety of the app’s audio playback through the bridge to Unity with a simple Objective-C one liner from anywhere in the app.

Performance gotchas

Rendering Unity’s OpenGL view in iOS is an expensive exercise. Native UI performance is highly degraded when used in conjunction with an active Unity scene. To alleviate this we pause Unity and avoid repainting the GL view when complex UI elements are placed on the screen.

Unloading assets from the Unity scene may also prove helpful.

Our iOS/Unity game catalogue

We built the following games (yeah granted, CityGT is not a game as such!) using the techniques discussed in this article. Check them out from the App Store and leave a comment if you’ve got any questions, criticisms or wisdom to impart. We don’t hear a whole lot of buzz about Unity game development for iOS in Australia and we love to hear stories from fellow Unity developers.


Shark Wars (2011 – US App Store only)

CityGT (2009 – AU App Store only)


20 Leggo Place
Richmond, Melbourne
Victoria 3121

Call us

phone: +61 3 9421 3365

General enquiries
Business enquiries

Come get us

Harvest our thought

News from the nest is our monthly compendium of all things Millipede. It features the earnest juice on stuff we’re up to, thinking about and witnessing in our industry. Sweet biscuits indeed. Pop your details below to book your vitamin delivery.


You are necks? *

Looking for a job or a contract gig? We’re flattered by your interest! Millipede hires by word of mouth, personal recommendation and individual reputation. It’s how we sustain our identity and our work environment. This means that unless we’re advertising for positions, we only consider hiring people we already know. If you know us and you’re looking for an opportunity, shoot out a tentacle through your network and hit us up. Formal jobs will be advertised via our social media channels, and naturally we welcome all applicants.

Interested in work experience or an internship? We’re active in our local community and will happily meet with your class group and chat about our industry, however we do not offer work experience or internships — we’re not equipped for this kind of offering. Please understand that we won’t deviate from this rule as we feel rubbish when we have to refuse every time.