The Easiest Way to Test SharePlay on visionOS Apps
tldr; Here is SharePlayMock, a package you can integrate within 10 minutes to enable testing SharePlay using simulators without requiring access to a second device. Here is the tutorial app repo. Feel free to join this Vision Pro developer discord server for questions and discussions.
Testing SharePlay code on visionOS apps has been very painful for all visionOS developers. Right now, the only way to test and debug a visionOS app SharePlay feature is to ask a friend who has a Vision Pro and make a FaceTime call with them. This can be extremely painful if we want to do iterative debugging.
Here, I want to introduce an API that can make your life way easier — SharePlayMock.
What is SharePlayMock and how does it work?
SharePlayMock is an extension API of Apple’s GroupActivities API. The goal of SharePlayMock is to make testing SharePlay feature of visionOS apps easier.
Without SharePlayMock, the only way to test a visionOS app’s SharePlay feature is to ask a friend who has a Vision Pro to test with you, because you need to be in a FaceTime call to enable SharePlay.
However, SharePlay, at the essence, is just a message channel that helps you send messages to and receive messages from others in the same SharePlay session.
Therefore, SharePlayMock, when enabled, establishes message channels using WebSocket among all participants, so that FaceTime call is NOT required anymore for testing. When SharePlayMock is disabled, the messages go through the official real SharePlay.
With SharePlayMock, developers can test their multiplayer logics themselves by running an app instance on the simulator and another app instance on the Vision Pro device.
What does SharePlayMock help with
SharePlayMock helps you test your multiplayer logics (such as syncing the sphere size in the tutorial app, or syncing card positions in a card game).
However it does NOT help with testing the logic of positioning participants. You need to update to visionOS 2.0 to test the positioning logic.
SharePlayMock Tutorial
Below is a tutorial showing you how to use SharePlayMock in the tutorial visionOS app.
The tutorial app has a sphere rendered and a button below the sphere which you can click to enlarge the sphere and click again to shrink to original size.
When SharePlay is enabled, all users in the SharePlay session should see the sphere in the same size.
The goal is to test this SharePlay feature using a simulator and a Vision Pro device, without a second person’s help or a second Vision Pro device.
Here is the link to the github repo containing this tutorial’s code.
1. Download SharePlay Mock server jar file
First, open up a terminal and brew install java.
brew install openjdk@17
Second, download SharePlayMock Server jar file.
https://github.com/Pixeland-Tech/SharePlayMock/releases/tag/placeholder-tag-0.1.0
2. Git Clone and Import SharePlayMock Package
Git clone this repo
https://github.com/Pixeland-Tech/SharePlayMock
Open your visionOS project in Xcode.
Go to File -> Add Package Dependencies… -> Search or Enter Package URL -> Enter “https://github.com/Pixeland-Tech/SharePlayMock”-> Add Package
Under “Add to Target”, select your project, then click “Add Package”
3. Modify code
First, find your local IP address. It is needed because SharePlayMock will spin up a local WebSocket server when enabled.
To find out your local IP, go to System Settings -> Wi-Fi -> “Details” button of your connected wifi -> IP address
Second, enable SharePlayMock when your app initializes. For example, in our tutorial app, the code looks like below. Remember to replace the IP address with your own IP address.
struct SharePlayTutorialApp: App {
init() {
SharePlayMockManager.enable(webSocketUrl: "ws://192.168.1.69:8080/endpoint")
// SharePlayMockManager.enable(webSocketUrl: "ws://<your_local_ip_address>:8080/endpoint")
}
var body: some Scene {
WindowGroup {
ContentView(viewModel: ContentViewModel())
}.windowStyle(.volumetric)
}
}
Note: You need to manually enable/disable Mock. To disable Mock, simply comment out this line of code. When Mock is enabled, all SharePlay messages will go through the WebSocket. When Mock is disabled, all SharePlay messages will go through the official SharePlay.
IMPORTANT: Comment out this line of code before publishing your app!! This line should only be added during testing. Otherwise, your app will be using WebSocket instead of the actual SharePlay in production.
Third, replace all the APIs below.
GroupActivity
->GroupActivityMock
For example, in the tutorial app, the code looks like below.
import Foundation
import GroupActivities
import UIKit
import SharePlayMock
class PlayTogetherGroupActivity: GroupActivityMock {
typealias ActivityType = PlayTogetherGroupActivity.Activity
private(set) var groupActivity: Activity
init() {
self.groupActivity = Activity()
}
struct Activity: GroupActivity {
// Define a unique activity identifier for system to reference
static let activityIdentifier = "com.spatialdevs.SharePlayTutorial.PlayTogether"
var metadata: GroupActivityMetadata {
var metadata = GroupActivityMetadata()
metadata.title = "Spatial Devs SharePlay Tutorial"
metadata.subtitle = "Let's play together!"
metadata.previewImage = UIImage(named: "birdicon")?.cgImage
metadata.type = .generic
return metadata
}
}
}
2. GroupSession
-> GroupSessionMock
var sharePlaySession: GroupSessionMock<PlayTogetherGroupActivity>?
3. GroupSessionMessenger
-> GroupSessionMessengerMock
var sharePlayMessenger: GroupSessionMessengerMock?
4. GroupStateObserver
-> GroupStateObserverMock
The tutorial app does not use GroupStateObserver
, but if your app uses it, you should replace it with GroupStateObserverMock
.
@ObservedObject var groupStateObserver = GroupStateObserverMock()
5. Participant
-> ParticipantMock
The tutorial app does not use Participant
, but if your app uses it, you should replace it with ParticipantMock
like below.
func sendMessage<T: Codable>(_ message: T, to predicate: @escaping (ParticipantMock) -> Bool, inSequence: Bool = true) {
if let participants = self.groupSession?.activeParticipants.filter(predicate),
participants.isEmpty == false {
self.sendMessage(message, to: Participants.only(participants), inSequence: inSequence)
}
}
4. Test SharePlay
First, open a terminal and run the command below to spin up your WebSocket server.
java -jar /<your_path_to_the_jar_file>/shareplay-mock-server-0.1.0.jar
Second, run your app on a Simulator in Xcode.
Third, connect Vision Pro to Xcode. Select target as your Vision Pro device and run your app on your Vision Pro while keeping the simulator running (in Xcode, you can run multiple instances at the same time on different targets).
Fourth, in your app, turn on SharePlay using the UI/feature you built for your app.
For example, in the tutorial app, I built a SharePlay button that can be used to start/end a SharePlay session. I click this button to start a SharePlay session that includes both my Vision Pro device and the Vision Pro simulator.
Finally, the app on the simulator and the app on your device should now be connected and you can test and debug your code.
For example, in the tutorial app, if my multiplayer logics are implemented correctly, when I enlarge it in the simulator, both the simulator and the device should both see the enlarged sphere, and vice versa.
Outro
Hope you find both SharePlayMock and this article helpful. Again, here is the link to SharePlayMock. And here is tutorial app repo. Feel free to join this Vision Pro developer discord server for questions and discussions.