Communicate with a Unity game embedded in a SwiftUI iOS App

Dino Trnka
Ministry of Programming — Technology
9 min readMar 12, 2021

If you followed my previous post about launching a Unity game from a SwiftUI app, you were able to successfully integrate a Unity game in your SwiftUI project, and load or unload it with the press of a button.

However, our Swifty journey cannot end there, because there isn’t much use of this functionality without actually being able to communicate between the two apps. Maybe you’ll need to send some game or player information from iOS to the Unity side to initialise the game properly. Or you need to send back the player’s score to the native app once you’re done with the game.

All of this is possible with the Unity framework, and with just a little setup you’ll be able to establish two-way communication with no trouble. Let’s see how!

Note: We will continue right where we left off, so if you haven’t already, be sure to follow through to the first part of the article, where all of the projects are set up.

What do we want to accomplish?

In order to implement communication between the iOS app and the Unity game, we will need to make something slightly more complex than a single-button app, but still simple enough not to get distracted with game mechanics.

So, for the iOS →Unity communication, let’s go with this simple idea: Our Unity game will contain a single ball that can change its colour depending on what message we send from the native side. On the native iOS side, we will implement three buttons that are labeled with different colour names: red, green and blue. Each one of these buttons will launch the same Unity game. However, each button will send a different message to the game, so if the user has tapped on the red button, the colour of the ball will be red, the blue button makes the ball blue, and so on.

For the Unity → iOS communication, we will do the opposite. We’ll add a button in the Unity game and keep track of how many times it was pressed by sending a message from Unity to iOS on each button press.

These use cases are very simple and don’t represent a real game, but it is enough for us to take a look at how communication works. Now that we know what to do, let’s get to the fun part!

iOS → Unity communication

Let’s create a new Unity project. In that project, add a Quit Game button and make it just call Application.Unload(), in exactly the same way as we did in the previous post. If you want, you can re-use the previous Unity project, because it already has the Quit button functionality.

Next, add a sphere game object and name it Ball.

Our shiny ball is nothing special at the moment, but it will be once we give it some functionality. So, let’s create a new script called BallBehavior.cs and attach it to our Ball game object. This script needs to have the following code:

Believe it or not, this code is all that is needed from the Unity side to establish iOS → Unity communication. The native app simply needs to call the SetBallColor method by making use of the UnityFramework SDK.

Let’s export this game from Unity now. We will export it in a folder named UnityBallExport.

Remember, each time you export your Unity game, you have to repeat the integration steps:

  • Drag the exported project’s Unity-iPhone.xcodeproj file to your main iOS app’s XCode workspace,
  • (Re)Import the UnityFramework.framework library in XCode,
  • Select the Data folder and check the Target Membership box next to UnityFramework.

Refer to the Connect Unity with iOS section of the previous article for more information on how to do this.

Now it’s time to modify our SwiftyUnity project. Our main goal here is to call the SetBallColor method that we have just implemented on the Unity side from the iOS app. As you recall, we load the Unity game from our ContentView.swift file. We will set the colour of the ball right after we show the Unity game.

An important point to consider is that the game might not be initialised by the time we send the message, which will result in the message getting lost. That’s why we will implement a caching mechanism that will store any pending messages, so they can get sent once the game is fully loaded.

Open the Unity.swift file which is our main entry point to the game. Let’s add a new structure called UnityMessage, which will contain all the data we need for sending messages to Unity.

Now we have a way to send messages to Unity by using the sendMessage method that calls Unity Framework sendMessageToGO method (GO stands for Game Object). As you can see, the app will check if Unity has been initialised and if so, the message is immediately sent. Otherwise, the message is cached so it can be sent later.

We now need to handle the mechanism for sending and cleaning up the cached messages. Taking this into account, your Unity.swift file should look like this:

I added comments in this file so you can keep track of what has been added, compared to the last one. As you can see, sendCachedMessages method makes sure that cached messages (if any) are sent after the game is initialised, so nothing gets lost.

Now let’s get back to our ContentView.swift so we can make use of this feature by creating our red, blue and green buttons. I added the view modifiers just to make the buttons look a bit prettier, so don’t mind them.

Each of these buttons calls the Unity.shared.sendMessage method. The first parameter is the name of our game object in Unity, which is Ball in this case. The second parameter is the name of the method in the script attached to the game object, which is SetBallColor. The third parameter is the message we want to send, which is the colour name. Keep in mind that the message has to be of string type, so if you want to send a different type, you will need to wrap it in a string and unwrap in on the Unity side.

Let’s test this by tapping on the buttons:

It’s working! The ball receives the message and changes its colour. Depending on which button you press, the ball will change colour to red, blue or green. This concludes our iOS → Unity communication.

Unity → iOS communication

Sending messages the other way around is a little trickier, but that won’t stop us! Let’s revisit our goal: We need to keep track of the number of times the button in the Unity game has been pressed by sending a message back to iOS on each button press.

Let’s create a new Unity project and add a single button to the scene. Also, create a new folder inside the Assets folder and name it Plugins.

We’ll leave Unity for just a moment because we need to do some configuration on the iOS side. In the SwiftyUnity project, add a new Objective-C file called NativeCallProxy.

When XCode offers to create an Objective-C bridging header, click on Create Bridging Header button and let XCode configure everything so Objective-C code can be accessed from the project as well.

Important: XCode automatically created a file named NativeCallProxy.m, but we want the extension to be .mm. So, rename the file to NativeCallProxy.mm.

So far, we have 2 important new files in the iOS project: NativeCallProxy.mm and SwiftyUnity-Bridging-Header.h.

Add one more file and name it NativeCallProxy.h. Edit these files so they have the following contents:

We will use the sendMessageToMobileApp method for our Unity → iOS communication. We will make a call by pushing the button we created in our Unity scene, and place a listener for that event on the native side.

Now comes the interesting part: We need to move the NativeCallProxy.h and NativeCallProxy.mm to the Unity project, because we will call them from the UnityFramework! We will leave the SwiftyUnity-Bridging-Header.h in XCode though.

So, let’s move NativeCallProxy.h and NativeCallProxy.mm inside the Plugins folder we created beforehand in the Unity project. We don’t need these in the iOS project, since they will be packed inside the exported Unity project anyway, so feel free to remove them from the SwiftyUnity project.

It’s time to connect everything. Let’s create a C# script called ButtonBehavior.cs and fill it with the following code:

The “magic” is happening inside the ButtonPressed() method. It is calling the NativeAPI.sendMessageToMobileApp() method with a message that will be received on the native iOS side.

All that is left now for the Unity side is to connect this script with the button itself, so add the ButtonBehavior.cs script to our button and connect the ButtonPressed listener.

That’s it! Let’s build this game. We will name it UnityButtonExport and integrate it into our XCode workspace just like we did before. Remember, each time you export a Unity game as an iOS project, you have to:

  • Drag the exported project’s Unity-iPhone.xcodeproj file to your main iOS app’s XCode workspace,
  • (Re)Import the UnityFramework.framework library in XCode,
  • Select the Data folder and check the Target Membership box next to UnityFramework.

Important: This time we’ll need to do one more bit of configuration besides the usual. Remember the NativeCallProxy files from the Unity game Plugins folder? You will see them in the exported Unity-iPhone project now.

You need to select the NativeCallProxy.h inside the Libraries/Plugins folder of the Unity-iPhone project and change UnityFramework’s target membership from Project to Public. Don’t forget this step!

The last step is hooking up a listener from the app to the sendMessageToMobileApp method. Let’s create a sample view model class that will register a listener for our Unity messages. Create a new Swift file, name it ViewModel.swift, and fill it with the following code:

We will also simplify our ContentView.swift, so it only launches the game:

After so much work, it’s finally time to test the communication. Run the iOS project and tap on the “Press Me!” button. The message from Unity should be printed out to your XCode logs.

Remember, you have to use a physical iPhone device (I am using one too in the example gif that follows, I am only streaming it to the screen).

Phew! That was a lot of work. However, you can apply this setup to any kind of communication you need! 🤩 These examples are very simple but with this baseline, you can implement more complex logic such as sending or receiving a message on every frame or even using a custom Bluetooth controller for the game, which would be handled on the iOS side and passed over to Unity.

I hope you enjoyed our little journey. I wish you lots of fun with your Unity adventure! 🤓

--

--

Dino Trnka
Ministry of Programming — Technology

Programmer, gamer, musician and all-around geek. I get hyped easily and love to share it with others.