はじめに

こんにちは!システム開発部のYです。

WWDC2025では、visionOS 26における「近くにいるユーザーとvisionOSの体験を共有する」機能の詳細が発表されました。
本記事では、以下のセッション内容をまとめています。詳細は WWDC セッションをご参照ください。

Share visionOS experiences with nearby people / 近くにいるユーザーとvisionOSの体験を共有

目次

新APIの概要、既存APIの改善点

Learn about nearby sharing

visionOS 26では、ウィンドウ横に共有ボタンが追加され、タップすると共有メニューが開きます。
近くにいるユーザーのリストから選択することで、簡単に共有を開始できます。

共有されたウィンドウは、誰でも動かすことができ、その移動は全員に共有され、ウィンドウは自然にグループ全体の方向を向きます。
また、ウィンドウのサイズ変更の共有や共有環境へのウィンドウの固定も出来ます。

誰かがDigital Crownを長押しして再センタリングすると、ウィンドウはその場にいる全員にとって適切な位置へ再配置されます。

また、誰かがウィンドウ上部の点を指さしたり、ウィンドウの位置に手をかざしたりすると、その人が見えるようにコンテンツがフェードアウトします。

FaceTimeとの統合

近くの人とアプリを共有している際にFaceTimeを起動すれば、離れた場所にいる人を招待して、一緒に遊んだりコラボレーションしたりできます。

ユーザーが空間ペルソナとして参加すると、システムが最適な位置を見つけ、近くのユーザーの隣に並べて配置します。

システムが決定する空間ペルソナの配置位置は、共有されるウィンドウの種類によって変わります。Volumeを共有中に参加した場合は、周囲の隙間を埋めるようにペルソナが配置されます。

iOSやmacOSなど、FaceTimeをサポートする他のプラットフォームからも参加できます。
参加すると共有ウィンドウの横にビデオが表示され、アプリを操作しながら他のユーザーと快適に会話できます。

デフォルトでは、共有ウィンドウは開始時点で閲覧専用です。そのアプリを持っていないユーザーとも共有でき、アプリの導入も不要です。
ただし、インタラクティブな体験を実現するにはSharePlayの導入が必要です。

Build nearby activities

Allow sharing from the share menu

visionOS 26では新しい共有メニューが導入され、アプリの共有がこれまで以上に簡単になりました。
その利点を最大限に活用するには、SwiftUIまたはUIKit APIを用いてGroupActivityを共有メニューに公開する必要があります。
これにより、ユーザーが共有ボタンをタップすると、そのGroupActivityが有効化されてSharePlayを開始できます。

Volumeウィンドウでは、アプリがアクティビティを公開している場合にのみ共有メニューを使用できます。

// Expose an activity with GroupActivities and SwiftUI

import SwiftUI
import GroupActivities

struct BoardGameActivity: GroupActivity, Transferable {
    var metadata: GroupActivityMetadata = {
        // Configure metadata
    }()
}

struct BoardGameApp: App {
    var body: some Scene {
        WindowGroup {
            BoardGameView()
            ShareLink(item: BoardGameActivity(),
                      preview: SharePreview("Play Together"))
                .hidden()
        }
        .windowStyle(.volumetric)
    }
}

ボードゲームで遊べるSharePlay体験を作成する場合、まずBoardGameActivityというシンプルなグループアクティビティを作成します。
次に、volumeにBoardGameViewを指定します。最後に、BoardGameActivityを共有メニューに公開するにはShareLinkを追加する必要があります。

公開したアクティビティが共有メニューから共有されると、自動的に有効化されてGroupSessionが作成されます。これは、GroupActivityでactivate()メソッドを手動で呼び出す場合と同じ動作です。

// Join a GroupSession with GroupActivities

// Sessions are created automatically when the activity is activated
for await session in BoardGameActivity.sessions() {

    // Additional configuration and setup

    // Join SharePlay
    session.join()
}

自動的に作成されたGroupSessionを取得するには、BoardGameActivityのセッションを監視対象にします。
そして、実際にSharePlayを開始するには作成したセッションを構成して参加します。

これでユーザーは共有メニューから直接アプリの共有を開始出来ます。

アプリを共有するためのウィンドウバーはイマーシブ空間には存在しません。
これを解決するには、独自のボタンを用意し、ウィンドウやボリュームを使わずにユーザーが共有を開始できるようにします。

ユーザーがそのボタンを押すと、BoardGameActivityのactivateメソッドを呼び出します。
visionOS26の新機能により、アクティビティでactivateを呼び出すと、FaceTime以外でも共有メニューが自動的に表示されます。

この機能は、ウィンドウとイマーシブ空間のどちらでも利用できます。
近くにいるユーザーを選択するか、新規のFaceTimeセッションを作成するとアプリから直接共有を開始出来ます。

さらに効果的な解決策は、非イマーシブモードの提供を継続することです。

この方法では、非イマーシブウィンドウの共有メニューから共有を開始し、全員が参加した後にイマーシブモードへ移行します。

// Join a GroupSession with GroupActivities

// 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()
}

実際にSharePlayを開始するにはGroupSessionに参加する必要があります。
さらに、セッションからsystemCoordinatorを取得し、supportsGroupImmersiveSpaceをtrueに設定します。
これにより、イマーシブ空間が全員に同じ場所で共有されます。

Enhance for nearby participants

// 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
        }
    }
}

セッションの参加者情報を取得するには、GroupSessionのアクティブな参加者パブリッシャーを監視します。
その参加者情報から新しいisNearbyWithLocalParticipantプロパティを確認できます。
このプロパティは、参加者が近くにいる場合にtrueを返します。

Place content relative to people

Immersive空間における参加者の位置情報は、ParticipantStateの新しいPoseプロパティから取得できます。

// 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
    }
}

localParticipantに関する情報を取得するには、セッションのsystemCoordinatorにアクセスし、localParticipantStateの非同期シーケンスを監視します。
localParticipantStateを取得すると、その状態の新しいposeプロパティを読み取ることができ、ローカル参加者のImmersiveSpaceシーンに対する相対位置を把握できます。

poseプロパティはリアルタイムで追跡されるものではなく、再センタリングが行われた時などの重要なイベント後に更新されます。

Coordinate shared media playback

visionOS 26ではAVPlayerがアップグレードされ、AVPlaybackCoordinatorの新機能とあわせて、同じ空間にいるユーザー間でオーディオとビデオの再生を正確に同期・調整できるようになりました。

Support multiple windows

visionOS 26の新しいSharePlay APIでは、ビューモディファイアを使ってSharePlayに関連付けたいWindowGroupをいつでも正確に選択できます。
これにより、複数のウィンドウを使用するアプリをこれまで以上に柔軟かつ高度に制御できるようになります。

// 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"))
        }
    }
}

新しい.groupActivityAssociationビューモディファイアをWindowGroupのビューに追加します。
groupActivityAssociationにprimaryを渡すと、そのウィンドウは開いている間共有されるよう設定されていることをシステムに伝えられます。

Share anchored content

visionOS 26ではARKitもアップデートされ、共有ワールドアンカーを使って同じ物理的な場所に共有コンテンツをアンカーできるようになりました。

共有コンテンツを特定の物理的な場所に配置するには、共有アプリで新しいAPIを使い、アンカーを近くの参加者と共有されるものとしてマークする必要があります。
SharePlayセッション中にこのAPIを利用すると、近くの参加者とのみ自動的に共有されるワールドアンカーを作成できます。
共有アンカーはSharePlayがアクティブな間にのみ作成でき、通常のワールドアンカーとは異なり、共有終了後は保持されません。

// 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
        }
    }
}

誰かが部屋に家具を追加した場合、その家具を近くにいる参加者全員に表示させたいとします。

そのためには、まず共有ワールドアンカーを作成する前に、それが使用可能かどうかを確認する必要があります。
WorldTrackingProviderの新しいWorldAnchorSharingAvailabilityを監視することで、利用可能になった時点で近くの人とアクティブに共有されていることが分かり、共有ワールドアンカーを作成できます。

// Create a shared world anchor with ARKit

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
        }
    }
}

WorldAnchorを作成する際は、新しいsharedWithNearbyParticipantsをtrueに設定して渡します。
すると、近くにいる参加者全員のWorldTrackingProviderで、anchorUpdatesシーケンスから新しい共有アンカーが受け取れます。
この識別子を読み込み、同期することで、アプリは各デバイスのアンカーに関連づけられたコンテンツを認識できるようになります。

まとめ

visionOS 26で追加された「近くにいるユーザーと体験を共有する」機能についてまとめました。

SharePlayの強化により、近くにいるユーザーであればFaceTimeを使わずに物理的な場所を共有でき、周囲の人々とつながり協力するための新しい強力な方法が提供されます。
一方で、遠く離れた相手とコンテンツを共有する場合は、従来どおりFaceTimeによるSharePlayが必要です。
サポートされているプラットフォームでは、近くの人と遠くの人の両方に対応できるようエクスペリエンスを設計することが重要です。
共有機能は、近くの参加者と遠隔地の参加者をともにサポートする必要がある点を忘れないでください。
この記事が少しでも参考になれば幸いです。詳細はWWDCのセッションをご参照ください。

参考



ギャップロを運営しているアップフロンティア株式会社では、一緒に働いてくれる仲間を随時、募集しています。 興味がある!一緒に働いてみたい!という方は下記よりご応募お待ちしております。
採用情報をみる