2025 Audio & VideoSpatial Computing
WWDC25 · 23 min · Audio & Video / Spatial Computing
Share visionOS experiences with nearby people
Learn how to create shared experiences for people wearing Vision Pro in the same room. We’ll show you how to integrate SharePlay and leverage ARKit in your app, introduce the updated window sharing flows for nearby and FaceTime participants, and cover new API designed for seamless collaboration. Discover best practices to make your collaborative features stand out, easily discoverable, and engaging for people together in the same space.
Watch at developer.apple.com ↗Chapters
- 0:00 — Introduction
- 0:56 — Learn about nearby sharing
- 4:21 — Build nearby activities
- 5:35 — Allow sharing from the share menu
- 9:15 — Enhance for nearby participants
- 10:37 — Place content relative to people
- 13:20 — Coordinate shared media playback
- 15:38 — Support multiple windows
- 16:50 — Share anchored content
Code shown on screen · 9 snippets
Expose an activity with GroupActivities and SwiftUI
// Expose an activity with GroupActivities and SwiftUI
import SwiftUI
import GroupActivities
struct BoardGameActivity: GroupActivity, Transferable {
var metadata: GroupActivityMetadata = {
var metadata = GroupActivityMetadata()
metadata.title = "Play Together"
return metadata
}()
}
struct BoardGameApp: App {
var body: some Scene {
WindowGroup {
BoardGameView()
ShareLink(item: BoardGameActivity(), preview: SharePreview("Play Together"))
.hidden()
}
.windowStyle(.volumetric)
}
}
struct BoardGameView: View {
var body: some View {
// Board game content
}
} Join a GroupSession with GroupActivities
// Join a GroupSession with GroupActivities
func observeSessions() async {
// Sessions are created automatically when the activity is activated
for await session in BoardGameActivity.sessions() {
// Additional configuration and setup
// Join SharePlay
session.join()
}
} Join and configure a GroupSession with GroupActivities
// Join a GroupSession with GroupActivities
func observeSessions() async {
// Sessions are created automatically when the activity is activated
for await session in BoardGameActivity.sessions() {
// Additional configuration and setup
guard let systemCoordinator = await session.systemCoordinator else { continue }
systemCoordinator.configuration.supportsGroupImmersiveSpace = true
// Join SharePlay
session.join()
}
} Check for nearby participants with GroupActivities
// Check for nearby participants with GroupActivities
func observeParticipants(session: GroupSession<BoardGameActivity>) async {
for await activeParticipants in session.$activeParticipants.values {
let nearbyParticipants = activeParticipants.filter {
$0.isNearbyWithLocalParticipant && $0 != session.localParticipant
}
}
} Observe local participant pose with GroupActivities
// Observe local participant pose with GroupActivities
func observeLocalParticipantState(session: GroupSession<BoardGameActivity>) async {
guard let systemCoordinator = await session.systemCoordinator else { return }
for await localParticipantState in systemCoordinator.localParticipantStates {
let localParticipantPose = localParticipantState.pose
// Place presented content relative to the local participant pose
}
} Associate a specific window with GroupActivities and SwiftUI
// Associate a specific window with GroupActivities and SwiftUI
import SwiftUI
import GroupActivities
struct BoardGameApp: App {
var body: some Scene {
WindowGroup {
BoardGameView()
ShareLink(item: BoardGameActivity(), preview: SharePreview("Play Together"))
.hidden()
}
.windowStyle(.volumetric)
WindowGroup(id: "InstructionalVideo") {
InstructionalVideoView()
.groupActivityAssociation(.primary("InstructionalVideo"))
}
}
}
struct BoardGameView: View {
var body: some View {
// Board game content
}
}
struct InstructionalVideoView: View {
var body: some View {
// Video content
}
} Create a world anchor with ARKit
// Create a world anchor with ARKit
import ARKit
class AnchorController {
func setUp(session: ARKitSession, provider: WorldTrackingProvider) async throws {
try await session.run([provider])
}
func createAnchor(at transform: simd_float4x4, provider: WorldTrackingProvider) async throws {
let anchor = WorldAnchor(originFromAnchorTransform: transform)
try await provider.addAnchor(anchor)
}
func observeWorldTracking(provider: WorldTrackingProvider) async {
for await update in provider.anchorUpdates {
switch update.event {
case .added, .updated, .removed:
// Add, update, or remove furniture
break
}
}
}
} Observe sharing availability with ARKit
// Observe sharing availability with ARKit
func observeSharingAvailability(provider: WorldTrackingProvider) async {
for await sharingAvailability in provider.worldAnchorSharingAvailability {
if sharingAvailability == .available {
// Store availability to check when creating a new shared world anchor
}
}
} Create a shared world anchor with ARKit
// Create a shared world anchor with ARKit
import ARKit
class SharedAnchorController {
func setUp(session: ARKitSession, provider: WorldTrackingProvider) async throws {
try await session.run([provider])
}
func createAnchor(at transform: simd_float4x4, provider: WorldTrackingProvider) async throws {
let anchor = WorldAnchor(originFromAnchorTransform: transform,
sharedWithNearbyParticipants: true)
try await provider.addAnchor(anchor)
}
func observeWorldTracking(provider: WorldTrackingProvider) async {
for await update in provider.anchorUpdates {
switch update.event {
case .added, .updated, .removed:
// Add, update, or remove furniture. Updates with shared anchors from others!
let anchorIdentifier = update.anchor.id
}
}
}
} Resources
Related sessions
-
25 min -
25 min -
36 min -
38 min -
14 min -
26 min