SwiftUI Multi-Platform Architecture (iOS, iPadOS, macOS, visionOS)

Published: (January 6, 2026 at 02:32 PM EST)
2 min read
Source: Dev.to

Source: Dev.to

🧠 The Core Principle

Share behavior.
Specialize presentation.

  • Business logic should be 100 % shared.
  • UI composition adapts per platform.

🧱 1. Split by Feature, Not by Platform

Bad structure

iOS/
macOS/
Shared/

Good structure

Features/
    Home/
    Profile/
    Settings/
Platform/
    iOS/
    macOS/
    visionOS/

Each feature contains:

  • ViewModel
  • State
  • Business logic

Platform folders contain:

  • Wrappers
  • Layout adapters
  • Platform‑specific views

🧩 2. Platform Abstraction Layer

Create small adapters:

protocol PlatformMetrics {
    var sidebarWidth: CGFloat { get }
    var toolbarHeight: CGFloat { get }
}

Implement per platform:

struct iOSMetrics: PlatformMetrics {
    let sidebarWidth: CGFloat = 0
    let toolbarHeight: CGFloat = 44
}

struct macOSMetrics: PlatformMetrics {
    let sidebarWidth: CGFloat = 240
    let toolbarHeight: CGFloat = 52
}

Inject the appropriate implementation:

.environment(\.metrics, currentMetrics)

No conditionals are needed inside views.

🧭 3. Navigation Architecture per Platform

PlatformNavigation style
iPhonesingle stack, push‑based
iPadsplit view, column navigation
macOSsidebar + detail, multi‑window
visionOSspatial stacks, scene‑based navigation

Do not force a single navigation model. Instead:

struct RootView: View {
    var body: some View {
        PlatformContainer {
            HomeFeature()
        }
    }
}

PlatformContainer switches implementation based on the current platform.

🪟 4. Window & Scene Management

macOS & visionOS are window‑first platforms. Design explicitly:

WindowGroup {
    RootView()
}

WindowGroup(id: "inspector") {
    InspectorView()
}

On iOS these groups are ignored; on macOS they are essential. The architecture must assume multiple instances.

🧠 5. Input Model Differences

PlatformInput modalities
iOStouch, gestures, swipe
macOSpointer, hover, right‑click, keyboard
visionOSgaze, pinch, spatial focus

Never rely solely on:

.onTapGesture {  }

Always support:

  • Button (including keyboard shortcuts)
  • Focus handling
  • Context menus

⌨️ 6. Keyboard & Command System

macOS & iPad need command definitions:

.commands {
    CommandGroup(replacing: .newItem) {
        Button("New Item") {
            create()
        }
        .keyboardShortcut("N")
    }
}

Expose actions from shared logic:

func create()
func delete()
func refresh()

UI layers wire these actions; the underlying logic stays shared.

📐 7. Layout Strategy

Avoid fixed layouts. Use adaptive stacks, flexible grids, dynamic type, and size classes. Example pattern:

if horizontalSizeClass == .compact {
    CompactLayout()
} else {
    RegularLayout()
}

Isolate this logic in layout containers—not in feature views.

🧪 8. Testing Multi‑Platform Correctness

Test across platforms for:

  • Window creation
  • Deep linking per platform
  • Keyboard navigation & focus behavior
  • Split‑view state
  • Multi‑window restoration

Most multi‑platform bugs are state‑related rather than UI‑related.

❌ 9. Common Anti‑Patterns

Avoid:

  • #if os(iOS) inside feature views
  • Platform checks inside ViewModels
  • Duplicated business logic
  • Navigation hacks that force a single UI everywhere

If a solution feels forced, it probably is.

🧠 10. Mental Model

Think in layers:

Business Logic (shared)
State Management (shared)
Navigation Model (per platform)
Layout System (per platform)
Input Model (per platform)
Presentation (per platform)

Only the bottom three layers change per platform.

🚀 Final Thoughts

Multi‑platform SwiftUI is not a UI problem — it’s an architecture problem. When designed correctly:

  • Features stay reusable
  • Code stays clean
  • Platforms feel native
  • Maintenance stays sane
  • visionOS becomes trivial to support
Back to Blog

Related posts

Read more »

SwiftUI #21: Groups

¿Qué es un Group? Un Group agrupa varias vistas juntas para evitar el límite de sub‑vistas que tiene un Stack máximo 10 y permite aplicar estilos a varias vist...

SwiftUI #20: Prioridades

Introducción En SwiftUI, un Stack divide el espacio de forma equidistante entre sus vistas. Si las vistas no caben, asigna un tamaño fijo a los Image y reduce...