A SharePlay package for visionOS that enables real-time synchronization of 3D content and game state across multiple devices.
- Real-time 3D entity synchronization
- Player management and seat assignment
- Type-safe message handling system
- Support for custom message types
- Built specifically for visionOS
- visionOS 1.0+
- Xcode 15.0+
- Swift 5.9+
Add the following to your Package.swift file:
dependencies: [
.package(url: "https://github.com/yourusername/DicyaninSharePlay.git", from: "1.0.0")
]Or add it directly in Xcode:
- File > Add Packages...
- Enter the repository URL
- Select the version rule
- Click Add Package
IMPORTANT: You must add the SharePlay entitlement to your Xcode project:
- Select your project in Xcode
- Select your target
- Go to "Signing & Capabilities"
- Click "+" and add "SharePlay"
- Ensure your provisioning profile includes SharePlay entitlement
import DicyaninSharePlay
import RealityKit
struct ContentView: View {
var body: some View {
Button("Start SharePlay") {
SharePlayManager.shared.startSharePlay()
}
}
}struct ImmersiveView: View {
@State private var content: Entity?
var body: some View {
RealityView { content in
// Your 3D content setup
self.content = content
registerMessageHandlers()
} update: { content in
// Update your 3D content
}
}
private func registerMessageHandlers() {
// Register transform handler
let transformHandler = EntityTransformHandler { message in
guard let entity = content?.entities.first(where: { $0.id.description == message.entityId }) else { return }
entity.position = message.position
entity.orientation = message.rotation
entity.scale = message.scale
}
MessageHandlerRegistry.shared.register(transformHandler)
// Register state handler
let stateHandler = EntityStateHandler { message in
if message.isActive {
Task {
if let newEntity = try? await Entity(named: message.modelName, in: realityKitContentBundle) {
content?.add(newEntity)
}
}
} else {
if let entity = content?.entities.first(where: { $0.id.description == message.entityId }) {
content?.remove(entity)
}
}
}
MessageHandlerRegistry.shared.register(stateHandler)
}
}struct ContentView: View {
var body: some View {
VStack {
// SharePlay status and controls
DicyaninSharePlayStatusView()
.frame(maxWidth: 300)
// Player name editor
PlayerNameEditor()
.frame(maxWidth: 300)
// Active players list
PlayerListView()
.frame(maxWidth: 300)
}
}
}A view that displays the current SharePlay session status and provides controls to start/stop the session.
- Shows active/inactive status with appropriate icons
- Provides prominent "Start SharePlay" button when inactive
- Shows destructive "Leave SharePlay" button when active
- Automatically updates when session state changes
A view that allows users to set and edit their player name.
- Shows current player name or "Set your name" prompt
- Provides inline editing with validation
- Automatically syncs name changes across SharePlay session
A view that displays all active players in the SharePlay session.
- Shows player names with ready status
- Indicates local player with "(You)" label
- Updates automatically when players join/leave
- Uses modern translucent design
Represents a player in the SharePlay session with properties:
name: Player's display nameid: Unique identifierscore: Current scoreisActive: Whether player is activeisReady: Player's ready statusisVisionDevice: Whether player is on visionOSplayerSeat: Assigned seat number
For syncing 3D entity transformations:
entityId: Target entity identifierposition: SIMD3 positionrotation: simd_quatf rotationscale: SIMD3 scale
For syncing entity creation/deletion:
entityId: Target entity identifierisActive: Whether entity should be activemodelName: Name of the model to load
-
Session Management
- Always check session state before sending messages
- Handle session cleanup properly when leaving
- Use appropriate error handling for session operations
-
Player Management
- Update player state through PlayerManager
- Handle player disconnections gracefully
- Validate player data before sending
-
Message Handling
- Use appropriate message types for different data
- Handle message delivery failures
- Consider message ordering for critical updates
-
UI Updates
- Use ObservableObject for state management
- Update UI on the main thread
- Handle edge cases (no players, disconnected, etc.)
Copyright © 2025 Dicyanin Labs. All rights reserved.
Hunter Harris
- Add the SharePlay entitlement to your app's target
- Import the package in your app
- Use the provided views and managers
import DicyaninSharePlay
struct ContentView: View {
var body: some View {
DicyaninSharePlayStatusView()
}
}import DicyaninSharePlay
struct ContentView: View {
var body: some View {
PlayerListView()
}
}You can create and register your own SharePlay message types. Here's how:
- Create a message type that conforms to
SharePlayMessage:
struct CustomGameMessage: Codable, Sendable, Identifiable, Equatable, SharePlayMessage {
public var windowId: String = ""
public var messageId: String = UUID().uuidString
public let id: UUID
public let customData: String
public static func == (lhs: CustomGameMessage, rhs: CustomGameMessage) -> Bool {
lhs.id == rhs.id
}
}- Register your message type in your app's initialization:
// In your app's initialization code (e.g., App.swift)
@main
struct YourApp: App {
init() {
// Register custom message types
MessageRegistry.shared.register(CustomGameMessage.self, typeIdentifier: "customGameMessage")
}
var body: some Scene {
WindowGroup {
ContentView()
}
}
}- Use your custom message type:
// Sending a custom message
let message = CustomGameMessage(
windowId: "main",
messageId: UUID().uuidString,
id: UUID(),
customData: "Hello SharePlay!"
)
SharePlayManager.sendMessage(message: message)The package includes several built-in message types:
Player: Player informationPlayerReadyMessage: Player ready statusGame_StartMessage: Game start notificationGame_SendHeartMessage: Heart synchronizationEntityTransformMessage: Entity transform updatesEntityStateMessage: Entity state updates