Sitemap
Chandra Welim

Chandra Welim

Senior iOS Engineer in Tokyo | CAT® Certified. Writing about Swift until it makes sense, and Bitcoin/crypto. CAT®: market research, fundamental & technical.

Press enter or click to view image in full size
Photo by Joshua Hoehne on Unsplash

visionOS Development: Build Apps for Apple Vision Pro

Getting started with spatial computing

4 min readMar 12, 2026

--

Vision Pro is Apple’s bet on the future. Spatial computing. Mixed reality. A new platform.

If you’re an iOS developer, you have a head start. visionOS shares foundation with iOS. SwiftUI works. UIKit works (in some cases). Your skills transfer.

But spatial computing is different. 3D interfaces. Depth. Immersion. New interaction paradigms.

Let me show you how to get started.

What visionOS Is

visionOS is a new operating system built for Apple Vision Pro. It’s based on iOS/iPadOS foundations but designed for spatial computing.

Key characteristics:

  • Apps run in windows floating in space
  • Windows can have depth (3D elements)
  • Users interact via eye tracking and hand gestures
  • Apps can create immersive experiences
  • Compatible iPad/iPhone apps run automatically

App Types

1. Window-based apps (most common):

Traditional apps in floating windows. Good starting point.

@main
struct MyApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}

2. Volumetric apps:

3D content that exists in the user’s space.

@main
struct MyApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
.windowStyle(.volumetric)
.defaultSize(width: 0.5, height: 0.5, depth: 0.5, in: .meters)
}
}

3. Immersive experiences:

Full or mixed immersion that replaces or augments reality.

@main
struct MyApp: App {
var body: some Scene {
ImmersiveSpace(id: "immersive") {
ImmersiveView()
}
}
}

Setting Up Your Project

Requirements:

  • Xcode 15+
  • macOS Sonoma+
  • visionOS SDK

Create a new project:

  1. File → New → Project
  2. visionOS → App
  3. Choose window style

Simulator:

Vision Pro simulator works without hardware. Test window layout, basic interactions, and UI.

Get Chandra Welim’s stories in your inbox

Join Medium for free to get updates from this writer.

For true spatial testing, you need actual hardware.

SwiftUI on visionOS

Most SwiftUI code works unchanged:

struct ContentView: View {
var body: some View {
VStack(spacing: 20) {
Text("Hello, visionOS!")
.font(.largeTitle)

Button("Tap Me") {
print("Tapped!")
}
.buttonStyle(.borderedProminent)

Image(systemName: "visionpro")
.font(.system(size: 80))
}
.padding()
}
}

New features for visionOS:

// Glass background effect
VStack {
// Content
}
.glassBackgroundEffect()

// Hover effects
Button("Hover Me") { }
.hoverEffect(.highlight)

// Depth
Image("photo")
.offset(z: 50) // Push toward user

3D Content with RealityKit

RealityKit brings 3D to your app:

import RealityKit
import RealityKitContent

struct ContentView: View {
var body: some View {
RealityView { content in
// Load a 3D model
if let model = try? await Entity.load(named: "Robot", in: realityKitContentBundle) {
content.add(model)
}
}
}
}

Adding interactions:

struct InteractiveModelView: View {
@State private var rotation: Angle = .zero

var body: some View {
RealityView { content in
if let model = try? await Entity.load(named: "Globe", in: realityKitContentBundle) {
model.components.set(InputTargetComponent())
model.components.set(CollisionComponent(shapes: [.generateSphere(radius: 0.1)]))
content.add(model)
}
}
.gesture(
DragGesture()
.targetedToAnyEntity()
.onChanged { value in
// Rotate based on drag
}
)
}
}

Ornaments

Ornaments are UI attached to windows:

struct ContentView: View {
var body: some View {
MainContent()
.ornament(attachmentAnchor: .scene(.bottom)) {
HStack {
Button("Play") { }
Button("Pause") { }
Button("Stop") { }
}
.padding()
.glassBackgroundEffect()
}
}
}

Immersive Spaces

For full immersion:

@main
struct MyApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}

ImmersiveSpace(id: "solar-system") {
SolarSystemView()
}
.immersionStyle(selection: .constant(.mixed), in: .mixed, .full)
}
}

struct ContentView: View {
@Environment(\.openImmersiveSpace) var openImmersiveSpace
@Environment(\.dismissImmersiveSpace) var dismissImmersiveSpace

var body: some View {
VStack {
Button("Enter Space") {
Task {
await openImmersiveSpace(id: "solar-system")
}
}

Button("Exit Space") {
Task {
await dismissImmersiveSpace()
}
}
}
}
}

Immersion styles:

  • .mixed — Virtual content blends with real world
  • .full — Completely replaces surroundings
  • .progressive — User controls immersion level

Hand Tracking

visionOS tracks hands without code. Standard gestures work:

  • Tap: Look at element, pinch fingers
  • Drag: Pinch and move
  • Zoom: Two-hand pinch
  • Rotate: Two-hand twist

For custom hand tracking:

import ARKit

class HandTrackingManager {
let session = ARKitSession()
let handTracking = HandTrackingProvider()

func startTracking() async {
do {
try await session.run([handTracking])

for await update in handTracking.anchorUpdates {
let anchor = update.anchor

// Access hand joints
if let thumbTip = anchor.handSkeleton?.joint(.thumbTip) {
let position = thumbTip.anchorFromJointTransform
// Use position
}
}
} catch {
print("Failed to start hand tracking: \(error)")
}
}
}

Eye Tracking

Eye position is used for targeting but privacy-protected:

// You don't get raw eye data
// Instead, use hover effects and look-to-select

Text("Look at me")
.hoverEffect(.highlight) // Highlights when user looks at it

Button("Select Me") { }
// Automatically selected when user looks + pinches

Designing for Vision Pro

Key principles:

  1. Window placement: Windows float in space. Don’t assume position.
  2. Comfortable viewing: Keep content at arm’s length (1–2 meters).
  3. Depth subtlety: Use depth sparingly. Too much causes discomfort.
  4. Eye fatigue: Don’t require constant focusing. Provide visual rest.
  5. Input clarity: Hover effects show what’s selectable.

Sizes and distances:

// Human Interface Guidelines recommendations
// Windows: 1-2 meters away
// Interactive elements: minimum 60pt tap target
// Text: minimum 17pt, preferably larger

Bringing Existing Apps

iPad apps run on Vision Pro in compatibility mode. To optimize:

  1. Add visionOS as deployment target
  2. Test in simulator
  3. Add glass effects, ornaments
  4. Consider volumetric elements
  5. Test interactions
#if os(visionOS)
// visionOS specific code
VStack { }
.glassBackgroundEffect()
#else
// iOS/iPadOS code
VStack { }
#endif

The Bottom Line

visionOS is new territory. It shares DNA with iOS but requires new thinking.

Start with window-based apps. Your SwiftUI skills transfer. Add spatial features gradually. Think about comfort and usability.

The platform is young. Early adopters will shape what spatial computing becomes.

Quick Reference

// Basic window app
WindowGroup {
ContentView()
}

// Volumetric
WindowGroup {
ContentView()
}
.windowStyle(.volumetric)

// Immersive space
ImmersiveSpace(id: "space") {
View()
}

// Glass effect
view.glassBackgroundEffect()

// 3D content
RealityView { content in
let model = try? await Entity.load(named: "Model")
content.add(model)
}

// Open immersive space
await openImmersiveSpace(id: "space")

Further reading:

iOS 26 Programming for Beginners is a clear, project-based path from zero to building apps with Swift 6 and Xcode 26. Mastering Swift 6 is the reference I point people to on concurrency, performance, and modern Swift patterns.

References

--

--