SharePlay Tutorial — Share custom data between iOS and MacOS
In this tutorial you will extend a small game to learn how you can use SharePlay for your apps.
- Introduction
- Preconditions - Project Setup
- UI Setup + Gameplay - Group Activity API
- Problems & Solutions
- Decoding Error
- Capability Error - Conclusion
Introduction
Recently, iOS 15.1 and the macOS 12.1 Beta were released, which both come with the functionality of SharePlay — a new way to share experiences of your app right inside FaceTime. In general you can use the Group Activities API to easily share video and audio, but also custom data with each other. In this tutorial we will especially focus on the last part and create a small simple tug of war game to showcase the functionality of SharePlay in a fun way. Even though the overall usage of the Group Activities API is well set up and easy to use, it can still happen to get stuck at some points, therefore, we go through the setup step-by-step in this tutorial and sum up possible problems and solutions at the end of this post.

Preconditions
For testing the SharePlay functionality you need two physical devices because FaceTime is needed. Since this a cross-platform tutorial, I recommend the following setup, but in general it should also work if you use one of the following devices twice.
- Mac/Macbook with macOS 12.1+ installed
- iPhone/iPad with iOS 15.1+ installed
Additionally you should also download an initial project which already covers the UI and game logic part for you as we won’t go into detail about that:
Project Setup
This tutorial will be kept as simple as possible, to make it easily understandable — therefore, a generic naming of “SharePlay” will be used for the project, models, etc. — feel free to use a different naming and to extend/adapt the project at any time as you would like to. For copy-paste reasons I recommend to use the same naming as a first step. :)
Let’s start by selecting the project on the left side in the navigator where you should see two targets — SharePlayTutorial (iOS) and SharePlayTutorial (macOS). Add now for both targets the Group Activities capability by selecting each target, clicking on +Capability on the top left corner, searching for Group Activities and selecting it.

UI Setup + Gameplay
Since this tutorial is about the SharePlay functionality rather than any kind of UI or Gameplay implementation, we will not go into detail about this. It basically just adds one button for each player — one on top of the screen and the other one on the bottom of the screen — a visual representation of the players and the rope, a button for starting the SharePlay session and a button to restart the game.
The code contains some magic numbers and could also definitely be improved, but as mentioned above, it’s more about the SharePlay part and it’s doing its job.
Group Activity API
Let’s get to the interesting part — the Group Activity API. First, we have to import the GroupActivity framework to create an activity which conforms to the GroupActivity protocol. The activity defines specific parts of our shared experience like the title which gets presented when a person starts the activity and what kind of shared experience we would like to use. Besides listenTogether and watchTogether, which are used for sharing audio or video, we will use the remaining type generic since we want to share custom data.
Then we have to start the activity which is usually done by some kind of user interaction. Therefore, we will create a function in the viewmodel to activate the activity and replace the placeholder comment in the action of the SharePlay button with calling this new function.
Next, we need a way to check when a new participant joins. The system will always add a new element to the activity’s session sequence, as soon as someone joins, therefore, we will iterate over the sequence with the await keyword to receive each value asynchronously as it becomes available. To avoid blocking code we will do the iteration in a task in the GameView.
Then we have to define the configureGroupSession function and will complete several steps at once in there. First, we create a GroupSessionMessenger from the given session, which is basically responsible for receiving and sending all data. This messenger has to be stored in a variable since later we still need it to send the data. Then we define what should happen when a message, in our case a SharePlayModel, is received. Here we can then just replace our viewmodel’s model with the received model and the UI will update accordingly. It’s important to also define the SharePlayViewModel as MainActor since we do not want to update the UI from a background task. Lastly, we will join the session to begin the delivery of synchronized data to the current device.
When we try to run our app now, we see the error No exact matches in call to instance method ‘messages’ appears. This occurs since our model does not conform to the Codable protocol yet. In general we can send any data which conforms to the Codable protocol, but we should still keep it as small as possible because of performance and data usage reasons, since it’s the data which gets sent between all the participants. Therefore, we just send the players’ y-offset in our model. Lastly, we will define a helper method to send data via the messenger.
Now we can send the model whenever we changed it.
Now we can run the app on both devices and call each other via FaceTime. Then we can start SharePlay via the associated button whereby we can join on the other device via an appearing popup. For some reasons you get already linked to the App Store on the Mac where the app won’t get found and you won’t join the SharePlay session. Therefore, start SharePlay on the Mac and join on the iPhone/iPad. Then you can already start to smash the buttons to win the game!
Problems & Solutions
Decoding Error
In a first attempt of creating a tutorial, where I created first an iOS app and added a Mac target afterwads, I got the following error as soon I tried to send some data:
SharePlayTutorialMac[33577:704067] [Default] messageStream:108 Explanation: Decoding message from data Error: Swift.DecodingError.valueNotFound(Any, Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: “message”, intValue: nil), CodingKeys(stringValue: “t”, intValue: nil)], debugDescription: “Decoder for value of Swift.Array<SharePlayTutorialMac.SharePlayModel>.self not found.”, underlyingError: nil))
It was quite strange since nowhere an object with a value of message or t inside was used as mentioned in the error. It seems like the type was defined in some kind of absolute way as the model’s type was defined as SharePlayTutorialMac.SharePlayModel in the error instead of just SharePlayModel. The type was somehow connected to the target’s name SharePlayTutorialMac.
Solution
My first attempt for solving this issue was to create a custom package, move the type/model there and importing the package where the model is used. This will even work when you build the app locally, but as soon as you upload the app to App Store Connect for example and download it via TestFlight, you will get the same error as above, just with the package name instead of the project name like SharePlayModelPackage.SharePlayModel.
After comparing the project.pbxproj file of a newly created multiplatform app (where it was working out of the box) and a newly created iOS app where the Mac target was added afterwards, I found the issue in the incorrect Product Name value. So it was not because of the target’s name, but instead of the product name referenced as $(TARGET_NAME), which in turn led to a product name of SharePlayTutorialiOS for the iOS target and SharePlayTutorialMac for the Mac target.
So to fix this error you have to use the same product name for both targets! For multiplatform apps this is set by default.
Capability Error
In case you forgot to add the Group Activities capability to your target you will get the following error.
SharePlayTutorial[11806:1823770] [Client] Error requesting initial state: Error Domain=NSCocoaErrorDomain Code=4099 “The connection to service named com.apple.group-activities.conversationmanagerhost was invalidated: failed at lookup with error 159 — Sandbox restriction.” UserInfo={NSDebugDescription=The connection to service named com.apple.group-activities.conversationmanagerhost was invalidated: failed at lookup with error 159 — Sandbox restriction.}
Solution
This can simply be fixed by adding the capability to your specific target.
Conclusion
As you can see it’s quite simple to setup SharePlay with a custom shared experience for your app — it’s a great new way to let users enjoy your app together.
The final project can be downloaded here: Direct or on Github.
PS: Since this my very first Medium post, there is definitely room for improvement, also in the project itself, so feel free to comment any kind of suggestions, critics or improvements — thanks! Other than that, I hope this post could help you to setup SharePlay for your app — happy coding! :)