stack.menu

Swift + SwiftUI

Native iPhone and iPad apps using Apple's UI and platform APIs.

SwiftUI · Swift concurrency · SwiftData · Xcode

Complete guide (Markdown) · Structured record (JSON)

Original source review: 2026-10-10. Core guidance rechecked: 2026-10-13. Check scope. Dependency combinations and setup need project-specific qualification.

Use cases

Choose it when

Consider another path when

Working practices

Tools by job

UI and state: SwiftUI + Observation; small @MainActor observable presentation models; explicit services

Native controls and platform conventions; keep domain and transport logic out of views

SwiftUI/Observation availability must match the minimum OS; Observation is not persistence or automatic thread safety

Structured local persistence: SwiftData

Framework-integrated models and persistence with a small dependency footprint

Validate migrations, relationship behavior and deployment availability; local persistence does not implement cross-platform sync

Nonsecret preferences: UserDefaults

Framework-integrated models and persistence with a small dependency footprint

Validate migrations, relationship behavior and deployment availability; local persistence does not implement cross-platform sync

Networking and API contract: URLSession + Codable behind an injected API client

Native HTTP client and async APIs; share API schemas and fixtures with Android

Check status codes, cancellation, timeouts and decoding; do not put provider secrets in the client; server owns authorization

Concurrency and background work: Swift structured concurrency, cancellation and actor isolation; server for durable agent runs

Keep rendering responsive and avoid tying durable business work to a view lifetime

async does not mean CPU work runs off the main actor; OS may suspend apps and does not guarantee exact background scheduling

Credentials: Keychain for app credentials

Use platform secret storage rather than preferences or database fields

Define accessibility and logout/revocation behavior; backend service credentials never belong in app binaries

Unit and integration tests: Swift Testing for new domain/API tests; XCTest where needed

Test deterministic state transitions, fixtures, decoding, persistence and failures

Do not mix Swift Testing and XCTest assertions inside a single test; simulator tests do not validate all hardware behavior

UI and accessibility: XCTest/XCUIAutomation; per-screen accessibility audits; VoiceOver and Dynamic Type review

Exercise navigation, permission states, keyboard/focus, screen-reader order and large text

Automated audits cover the current screen and do not establish usability or complete accessibility; hardware checks remain necessary

Build, CI and distribution: Xcode + Swift Package Manager; xcodebuild on pinned macOS/Xcode CI; TestFlight before App Store

One reproducible toolchain, recorded test results, signed archives and beta verification

Mac and compatible SDK/runtime required; signing team, bundle ID, profiles, capabilities, account access and store submission remain explicit handoffs; archive success is not publication

Navigation: SwiftUI NavigationStack

Platform navigation keeps back behavior and destinations in native UI

Use on supported OS versions; keep route identities separate from loaded objects and test deep-link/session restoration. iOS 16+ availability requires a plan for older targets.

Notifications: UserNotifications

Native notifications support reminders with explicit user permission

Request authorization in context, handle denied/revoked permission and validate notification routes. Remote delivery adds APNs/server setup; delivery timing is not a durable job guarantee.

Media selection: PhotosUI PhotosPicker

Native picker lets users choose image/video assets

iOS 16+; load selected representations with cancellation/error handling and bound file size before upload. Selection is not immediate local-file availability.

Build with the stack’s strengths

Architecture and developer workflow.

An installed iOS client persists across backend releases and is suspended or terminated under OS control. Recommendation: SwiftUI views render observable presentation state while repositories own URLSession DTOs and local persistence. Store rollout, API compatibility and local-store migration are separate adoption gates.

Version scope: Documentation reviewed 11 October 2026. SwiftUI Observation and SwiftData are iOS/iPadOS 17+; support older OS targets through compatible alternatives. Pin Xcode/SDK/Swift/package resolution and retain the minimum OS/device matrix. No simulator, archive or store-release check was executed. · Reviewed 2026-10-11

Architecture boundaries

Tools and feedback

Translations to avoid

For an existing application

Keep the contracts while you change the stack.

These are documented options and proposed checks. Inspect the actual source application before adopting a transition.

Browser and installed clients

Inspect the source

Installed versions, minimum OS, URLSession DTOs/errors, deep links, auth expiry and old/offline writes.

During coexistence

Recommendation: Ship additive backend responses and versioned fixtures before the new client; retain old contracts until measured adoption and an explicit support policy permit retirement.

Target design

Recommendation: Separate API DTOs from local models and use capability negotiation for optional features; authorization remains backend-owned.

Rollback

Disable the new server feature and retain both API contracts; already-installed clients require a compatible forward fix when needed.

Benefits and prerequisites

Independent backend/client deployment can preserve supported installations.
Requires: Inventory of real installed versions, API fixtures and release/support owners.
Tradeoffs: Long-lived version support costs maintenance; a new store release cannot force immediate adoption.

Checks before transfer

  • Old and new builds run against the same new backend.
  • Unknown optional fields and absent new fields do not break supported clients.
  • A delayed old-client mutation receives the documented success/conflict/upgrade response.
Data and schema

Inspect the source

Existing on-device store versions, entity meaning, relationships, pending local writes and upgrade paths.

During coexistence

Recommendation: Add compatible fields/stages before removing representations; migration fixtures include every supported historical store and unacknowledged operations.

Target design

Recommendation: Use SwiftData VersionedSchema and SchemaMigrationPlan when OS support permits; preserve the established Core Data/store choice if migration value is unproven.

Rollback

Prefer a compatible forward repair if downgrade is unqualified. Restore a pre-upgrade copy only under a reconciled policy that does not erase later local writes.

Benefits and prerequisites

An explicit schema history makes upgrade behavior inspectable.
Requires: Reviewed stages and fixtures proving values/relationships/pending writes survive.
Tradeoffs: Forward migration does not establish older-binary readability; cross-store conversion is application work.

Checks before transfer

  • Open each supported old store with the new build and compare user data and pending operations.
  • Interrupt migration and verify recovery behavior.
  • Attempt the intended rollback build against the migrated fixture; reject release rollback if unreadable.
Caching and shared state

Inspect the source

Local reads, pending offline changes, tombstones/cursors and server conflict semantics.

During coexistence

Recommendation: Keep the server as shared-domain reconciler; record each local mutation durably with a stable operation ID and schema version while old clients use the same conflict policy.

Target design

Recommendation: Use repository-owned local reads and a persistent operation log when offline writes are required; choose revision checks, merge or user resolution per domain instead of accidental timestamp wins.

Rollback

Stop the new synchronizer, retain pending operations and translate/reconcile them before returning to old logic; never clear the log as a rollback shortcut.

Benefits and prerequisites

Users can retain local work through intermittent connectivity.
Requires: Durable local storage, replay-safe backend, explicit conflict/tombstone protocol.
Tradeoffs: This is a proposed application design; SwiftData does not automatically provide arbitrary backend synchronization.

Checks before transfer

  • Two devices edit the same record offline; the declared policy preserves or exposes both intents.
  • Disconnect after server commit but before acknowledgement; replay does not duplicate the effect.
  • Restart/sign-out does not silently replay account A operations as account B.
Jobs and schedules

Inspect the source

Foreground task cancellation, pending intents and any existing background task identifiers/handlers.

During coexistence

Recommendation: Retain old handler identifiers and persisted payload readers while new code is installed; use one owner per sync operation.

Target design

Recommendation: Choose BGAppRefreshTask/BGProcessingTask for eligible opportunistic device work, foreground tasks for interactive work and backend durable jobs for server completion obligations.

Rollback

Disable new task submission while preserving payload readers/handlers long enough to reconcile pending work; continue foreground sync or a server job where appropriate.

Benefits and prerequisites

Eligible refresh/maintenance can execute while the app is backgrounded.
Requires: Capabilities, registered handlers, durable checkpoints and expiration handling.
Tradeoffs: The OS selects launch time; device scheduling does not satisfy a fixed backend completion SLA.

Checks before transfer

  • Expire a task midway; it checkpoints/cancels and marks completion correctly.
  • Suspend/terminate/relaunch and resume without a duplicated business effect.
  • Offline, battery and permission constraints show honest pending state.
Files and objects

Inspect the source

Existing upload/download IDs, authorized object keys, file hashes, task identifiers and completion acknowledgements.

During coexistence

Recommendation: Keep server upload endpoints/object ownership compatible; persist mapping from background task to logical operation and retain file-backed upload payloads.

Target design

Recommendation: Use an identified background URLSession for eligible HTTP(S) transfer; recreate it after system termination and move completed temporary downloads before delegate return.

Rollback

Stop creating new transfers, inspect/cancel or finish existing tasks and reconcile server objects; keep session callbacks until all retained tasks are accounted for.

Benefits and prerequisites

Transfers can continue during suspension without holding the app process.
Requires: Delegate lifecycle, stable session identifier, protected files and authorized retry/acknowledgement contract.
Tradeoffs: Background uploads require files; scheduling is not exact and transfer completion differs from domain acceptance.

Checks before transfer

  • Suspend during upload/download and restore the same session after system termination.
  • Verify file integrity and durable location after completion.
  • Retry an ambiguous response without producing duplicate owned objects.
Delivery and recovery

Inspect the source

Store release ownership, supported devices, adoption, crash reports and persisted-data compatibility.

During coexistence

Recommendation: Verify TestFlight install-over-existing fixtures; retain server compatibility through phased adoption and include manual downloads in risk assessment.

Target design

Recommendation: Use phased release with explicit crash/data-loss/compatibility stop criteria; assess adoption before retiring old API behavior.

Rollback

Pause further distribution and disable affected server capabilities if safe; ship a compatible repair rather than assuming an installed-version/store downgrade.

Benefits and prerequisites

Gradual automatic-update distribution can limit initial exposure.
Requires: Signed reviewed build, live telemetry and a compatible recovery release.
Tradeoffs: Apple's seven-day phases cover eligible automatic updates; anyone can manually download, and pausing does not reverse installs.

Checks before transfer

  • Verify real install/update preserves store, credentials and deep links.
  • Track old/new build support and migration failures separately.
  • Pause/recovery procedure leaves already-updated clients functional.

Read the migration workflow →

Setup

  1. Start in a source checkout in a local development workspace. Record minimum iOS/iPadOS versions, bundle ID, required capabilities, API schema and signing owner.
  2. Install a compatible stable Xcode and required simulator runtimes; finish Apple license/platform setup through the accountable operator. Record xcodebuild -version and selected developer directory.
  3. Create an iOS App in Xcode using Swift and SwiftUI, with unit and UI test targets. Share the scheme. Pin package resolution; use only dependencies justified by a requirement.
  4. Separate views, observable presentation state, repositories and API DTOs. Add migration fixtures and versioned network fixtures; use a fake API for tests.
  5. Run local simulator checks and CI using an actual destination ID. Then verify representative physical devices, offline/reconnect, permissions, VoiceOver and large text.
  6. Have the account owner establish team/bundle ID/capabilities and signing access. Produce a signed archive, verify a TestFlight install, then follow the authorized release workflow. Retain result bundles and release evidence.

Verification

Run these in the configured application; they are instructions, not recorded passes.

xcodebuild -version
xcode-select -p
xcodebuild -list -project App.xcodeproj
xcodebuild -showdestinations -project App.xcodeproj -scheme App
xcodebuild -resolvePackageDependencies -project App.xcodeproj -scheme App
xcodebuild test -project App.xcodeproj -scheme App -destination "platform=iOS Simulator,id=<installed-simulator-UUID>" -resultBundlePath <new-result-path>.xcresult
xcodebuild archive -project App.xcodeproj -scheme App -destination "generic/platform=iOS" -archivePath <new-archive-path>.xcarchive
xcodebuild -exportArchive -archivePath <archive-path>.xcarchive -exportPath <export-path> -exportOptionsPlist <reviewed-export-options>.plist

Open questions

Sources