Sample CodeiOS, iPadOS, Mac Catalyst, visionOSReviewed 2026-07-21View on Apple Developer

Connecting iPadOS and visionOS apps over the local network

At a glance

Item Summary
Purpose Build an iPadOS companion app to control your visionOS app.
App architecture Separate iPadOS and visionOS app targets share a protocol-based peer-messaging package; actor-backed client/server transports own discovery and connections while RobotViewModel maps messages into UI state.
Main patterns SwiftUI scene composition, SwiftUI–RealityKit bridge, Observable state owner, Async update consumer, Protocol-based peer transport, Protocol capability boundary
Project style Code-rich sample with 23 scanned Swift file(s) and 2087 Swift line(s); resources and generated assets are excluded from those counts.

Project structure

Source bundle/
├── iPadCompanionApp/RobotConfigurationExperience/RobotConfigurationExperienceApp.swift  # RobotConfigurationExperienceApp
├── iPadCompanionApp/Companion/CompanionApp.swift  # CompanionApp
├── iPadCompanionApp/Packages/PeerToPeerMessaging/Sources/PeerToPeerMessaging/ConnectionTypes.swift  # NetworkEvent, ConnectionEvent, NetworkState
├── iPadCompanionApp/Packages/PeerToPeerMessaging/Sources/PeerToPeerMessaging/Actors/Client.swift  # Client
├── iPadCompanionApp/Companion/Views/ServerContentView.swift  # ServerContentView
├── iPadCompanionApp/SharedContent/Models/RobotViewModel.swift  # RobotViewModel
├── iPadCompanionApp/Packages/PeerToPeerMessaging/Sources/PeerToPeerMessaging/Protocols/PeerMessagingProtocols.swift  # PeerToPeerMessage, PeerMessagingManager
├── iPadCompanionApp/Packages/PeerToPeerMessaging/Sources/PeerToPeerMessaging/TLS/CertificateTrustManager.swift  # CertificateTrustManager
└── iPadCompanionApp/Packages/RealityKitContent/Package.realitycomposerpro/ProjectData/main.json  # authored RealityKit content

Structure observations

  • The runtime boundary is WindowGroup + volumetric window; the pruned tree lists only files that explain lifecycle, state, or framework integration.
  • App code is split into role-named views, models, managers, providers, components, or systems.
  • Authored .reality or Reality Composer Pro content is a real implementation boundary; Swift loads or drives it rather than reproducing its entity graph.

Overall architecture

Reference code

iPadCompanionApp/Companion/CompanionApp.swift:12 — the app or executable entry declares the outer scene lifecycle.

struct CompanionApp: App {
    // ...
}

The diagram is a responsibility flow, not a claim that every adjacent node directly calls the next. It keeps scene ownership, shared state, RealityKit content, and framework-provider work at separate levels.

Ownership and state

Ownership evidence

iPadCompanionApp/Companion/CompanionApp.swift:13 — representative stored state or the nearest verified lifecycle anchor.

@main
struct CompanionApp: App {
    @State private var serverController: PeerMessagingController<Server<NetworkCommand>>
    // ...
}
Owner Object or state Relationship Mutation authority
CompanionApp PeerMessagingController as serverController owns a SwiftUI-managed reference slot Only the declaring scope writes
CompanionApp RobotViewModel as robotViewModel owns a SwiftUI-managed reference slot Only the declaring scope writes
ServerContentView ServerController as serverController receives a shared/non-owning reference The upstream owner controls lifetime; this scope may invoke its mutable API
ServerContentView RobotViewModel as robotViewModel receives a shared/non-owning reference The upstream owner controls lifetime; this scope may invoke its mutable API

Ownership here is deliberately narrow: @Environment and weak references are shared links, initialized @State or stored services are lifecycle ownership, and a RealityView content closure owns additions to its entity graph without making the SwiftUI view a reference-type owner.

Class and protocol design

Type Responsibility Depends on or conforms to
CompanionApp (iPadCompanionApp/Companion/CompanionApp.swift:12) Declares app scenes and top-level dependency lifetime. App
ServerContentView (iPadCompanionApp/Companion/Views/ServerContentView.swift:11) Presents UI and forwards gestures or lifecycle events. View
RobotViewModel (iPadCompanionApp/SharedContent/Models/RobotViewModel.swift:16) Owns UI-facing feature state and commands. Concrete framework collaborators
PeerToPeerMessage (iPadCompanionApp/Packages/PeerToPeerMessaging/Sources/PeerToPeerMessaging/Protocols/PeerMessagingProtocols.swift:13) Declares a replaceable capability or lifecycle contract. Codable, Sendable, Hashable
PeerMessagingManager (iPadCompanionApp/Packages/PeerToPeerMessaging/Sources/PeerToPeerMessaging/Protocols/PeerMessagingProtocols.swift:17) Declares a replaceable capability or lifecycle contract. Concrete framework collaborators
RobotConfigurationExperienceApp (iPadCompanionApp/RobotConfigurationExperience/RobotConfigurationExperienceApp.swift:12) Declares app scenes and top-level dependency lifetime. App
CertificateTrustManager (iPadCompanionApp/Packages/PeerToPeerMessaging/Sources/PeerToPeerMessaging/TLS/CertificateTrustManager.swift:17) Coordinates long-lived framework or cross-view work. Sendable

Verified protocol-oriented seams: NetworkCommandPeerToPeerMessage, RobotEventPeerToPeerMessage, MockNetworkActorPeerMessagingManager. Framework conformances remain adapters, not proof of an app-wide protocol architecture.

Access control

Symbol Access Verified effect Likely rationale
public private(set) var connectionState: NetworkState = .cancelled (iPadCompanionApp/Packages/PeerToPeerMessaging/Sources/PeerToPeerMessaging/PeerMessagingController.swift:24) private(set) The getter keeps its wider visibility, while mutation stays inside the declaring type and its permitted same-file extensions. Inference: Protect a state invariant while allowing observation.
@State private var serverController: PeerMessagingController<Server<Net... (iPadCompanionApp/Companion/CompanionApp.swift:13) private Use is restricted to the declaration and same-file extensions permitted by Swift. Inference: Hide implementation details and lifecycle-sensitive state.
public var incomingMessages: AsyncStream<Manager.Message> { (iPadCompanionApp/Packages/PeerToPeerMessaging/Sources/PeerToPeerMessaging/PeerMessagingController.swift:37) public The declaration is available to importing modules, subject to its containing type’s visibility. Inference: Export the type across the package-module boundary.

No reviewed declaration uses fileprivate, open; unmodified Swift declarations are internal.

Reference code

iPadCompanionApp/Packages/PeerToPeerMessaging/Sources/PeerToPeerMessaging/PeerMessagingController.swift:24 — representative visibility boundary.

    public private(set) var connectionState: NetworkState = .cancelled
    public private(set) var errorMessage: String?

    // Tasks for monitoring state.
    @ObservationIgnored private var monitorTask: Task<Void, Never>?
    @ObservationIgnored private var networkTask: Task<Void, Error>?

Logic ownership and placement

Logic Owning type or file Placement rationale
Scene declaration and dependency lifetime CompanionApp The App/entry boundary determines window, volume, and immersive-space lifetime.
Presentation, attachments, and gestures ServerContentView SwiftUI view code forwards user intent and RealityView lifecycle events.
Shared feature state and commands RobotViewModel A role-named owner prevents sibling views from duplicating transitions.

Design patterns

Pattern Source evidence Purpose or tradeoff
SwiftUI scene composition iPadCompanionApp/Companion/CompanionApp.swift:12 Keeps windows, volumes, and immersive-space lifecycle visible at the app boundary.
SwiftUI–RealityKit bridge iPadCompanionApp/SharedContent/Views/RobotView.swift:19 Builds and updates a RealityKit entity graph from SwiftUI lifecycle closures.
Observable state owner iPadCompanionApp/SharedContent/Models/RobotViewModel.swift:16 Shares feature state across multiple views without moving framework resources into view values.
Async update consumer iPadCompanionApp/Packages/PeerToPeerMessaging/Sources/PeerToPeerMessaging/PeerMessagingController.swift:55 Consumes provider or event streams in cancellable structured tasks.
Protocol-based peer transport iPadCompanionApp/Packages/PeerToPeerMessaging/Sources/PeerToPeerMessaging/PeerMessagingController.swift:17 Lets actor-backed client, server, and mock transports share one message contract while the controller publishes UI state.
Protocol capability boundary iPadCompanionApp/Packages/PeerToPeerMessaging/Sources/PeerToPeerMessaging/Protocols/PeerMessagingProtocols.swift:13 Defines a local capability used by multiple concrete types; this is the verified protocol-oriented seam.

Naming conventions

  • Role suffixes are evidence, not decoration: App: CompanionApp, RobotConfigurationExperienceApp; ViewModel: RobotViewModel; Manager: CertificateTrustManager, PeerMessagingManager; Client: Client.
  • Protocols: PeerToPeerMessage, PeerMessagingManager.
  • Commands use verb-led methods: toggleWindowPresentation, start, createConnection, openStream, handleBrowserStateUpdates, invalidate, send, receiveMessages.
  • Files generally match their primary type; Views, Models, Managers, Providers, Components, Systems, and Packages folders describe architectural roles where present.

Architecture takeaways

  • Treat CompanionApp as the owner of scene declarations, not as the owner of every RealityKit entity created later.
  • Keep view-local interaction in SwiftUI, but move provider sessions, playback resources, shared game state, or transport state into a stable owner when their lifetime exceeds one render pass.

Source map

Source file Relevant symbols
iPadCompanionApp/RobotConfigurationExperience/RobotConfigurationExperienceApp.swift:12 RobotConfigurationExperienceApp
iPadCompanionApp/Companion/CompanionApp.swift:12 CompanionApp
iPadCompanionApp/Packages/PeerToPeerMessaging/Sources/PeerToPeerMessaging/ConnectionTypes.swift:19 NetworkEvent, ConnectionEvent, NetworkState, NetworkServiceConstants, TLSError
iPadCompanionApp/Packages/PeerToPeerMessaging/Sources/PeerToPeerMessaging/Actors/Client.swift:14 Client
iPadCompanionApp/Companion/Views/ServerContentView.swift:11 ServerContentView
iPadCompanionApp/SharedContent/Models/RobotViewModel.swift:16 RobotViewModel
iPadCompanionApp/Packages/PeerToPeerMessaging/Sources/PeerToPeerMessaging/Protocols/PeerMessagingProtocols.swift:13 PeerToPeerMessage, PeerMessagingManager
iPadCompanionApp/Packages/PeerToPeerMessaging/Sources/PeerToPeerMessaging/TLS/CertificateTrustManager.swift:17 CertificateTrustManager