# Swift + SwiftUI

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

SwiftUI · Swift concurrency · SwiftData · Xcode

[Complete guide (Markdown)](/stacks/swift-swiftui-ios/guide.md) · [Structured record (JSON)](/stacks/swift-swiftui-ios/record.json)

Original source review: 2026-10-10. Core guidance rechecked: 2026-10-13. [Check scope](/research-freshness.json). Dependency combinations and setup need project-specific qualification.

## Use cases

- iPhone and iPad products

- Native Apple integrations

- Accessible mobile clients for a shared service

## Choose it when

- Apple-native UI and platform capabilities are requirements

- A Mac/Xcode build environment and Apple distribution ownership are available

## Consider another path when

- The requirement is a single shared UI across iOS and Android

- There is no supported macOS/Xcode build environment

## Working practices

- Keep presentation state small and actor-isolated; inject service and API boundaries for tests.

- Use structured concurrency with cancellation and store credentials in Keychain.

- Treat offline storage and synchronization as separate decisions, with explicit conflict handling.

- Check Dynamic Type, VoiceOver and real-device behavior; a simulator pass does not establish background or distribution behavior.

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

- [https://developer.apple.com/swiftui/](https://developer.apple.com/swiftui/)

- [https://developer.apple.com/documentation/swiftui/model-data](https://developer.apple.com/documentation/swiftui/model-data)

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

- [https://developer.apple.com/documentation/swiftdata/modelcontext](https://developer.apple.com/documentation/swiftdata/modelcontext)

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

- [https://developer.apple.com/documentation/swiftdata/modelcontext](https://developer.apple.com/documentation/swiftdata/modelcontext)

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

- [https://developer.apple.com/documentation/foundation/urlsession](https://developer.apple.com/documentation/foundation/urlsession)

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

- [https://developer.apple.com/documentation/foundation/urlsession](https://developer.apple.com/documentation/foundation/urlsession)

- [https://developer.apple.com/documentation/backgroundtasks](https://developer.apple.com/documentation/backgroundtasks)

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

- [https://developer.apple.com/documentation/security/keychain-services](https://developer.apple.com/documentation/security/keychain-services)

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

- [https://developer.apple.com/documentation/xcode/testing](https://developer.apple.com/documentation/xcode/testing)

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

- [https://developer.apple.com/documentation/xcode/testing](https://developer.apple.com/documentation/xcode/testing)

- [https://developer.apple.com/documentation/accessibility/performing-accessibility-audits-for-your-app](https://developer.apple.com/documentation/accessibility/performing-accessibility-audits-for-your-app)

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

- [https://developer.apple.com/xcode-cloud/](https://developer.apple.com/xcode-cloud/)

- [https://developer.apple.com/library/archive/technotes/tn2339/_index.html](https://developer.apple.com/library/archive/technotes/tn2339/_index.html)

### 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.

- [https://developer.apple.com/documentation/swiftui/navigationstack](https://developer.apple.com/documentation/swiftui/navigationstack)

### 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.

- [https://developer.apple.com/documentation/usernotifications/asking-permission-to-use-notifications](https://developer.apple.com/documentation/usernotifications/asking-permission-to-use-notifications)

### 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.

- [https://developer.apple.com/documentation/photosui/photospicker](https://developer.apple.com/documentation/photosui/photospicker)

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

- Use SwiftUI state/Observation according to minimum OS availability; keep view rendering separate from asynchronous repositories and business decisions. Observation is UI dependency tracking, not disk persistence.

- Recommendation: version API DTOs separately from SwiftData/Core Data entities; preserve old installed-client contracts and authorize all privileged operations on the backend.

- Use a versioned local schema and reviewed migration plan for SwiftData. Recommendation: store unacknowledged user intents durably before claiming an offline write succeeded; reconcile conflicts with an explicit server policy.

- Separate foreground structured concurrency, OS-scheduled refresh/processing, background URLSession transfers and server durable jobs. Each has different completion and cancellation semantics.

- Recommendation: Keychain stores credential material; isolate account-local data and clear/lock the appropriate state on account switch. Keep backend provider secrets out of the app bundle.

### Tools and feedback

- Start in the external Xcode source checkout. Read README, minimum OS, shared scheme, signing owner, capabilities, API fixtures and every persisted schema version.

- Record xcodebuild -version and xcode-select -p; use xcodebuild -list -project App.xcodeproj and xcodebuild -showdestinations -project App.xcodeproj -scheme App with the actual names. Resolve packages with xcodebuild -resolvePackageDependencies -project App.xcodeproj -scheme App. Check current flags with the pinned xcodebuild -help; the linked command-line FAQ is archived.

- After choosing an installed simulator UUID, run xcodebuild test -project App.xcodeproj -scheme App -destination 'platform=iOS Simulator,id=<UUID>' -resultBundlePath <new-path>.xcresult. Placeholders require substitution; simulator results do not establish device background behavior.

- Recommendation: use Instruments/network diagnostics, physical-device background checks, upgrade fixtures, VoiceOver and large text. Produce a signed archive/TestFlight update under the established release workflow; record exact old/new client builds and compatible backend revisions.

### Translations to avoid

- Assuming users update when the backend deploys strands old installed clients and offline requests.

- Assuming an older executable can read a migrated store confuses binary rollback with data rollback.

- Keeping pending offline writes only in view state loses work after suspension or termination.

- Using BGAppRefreshTask as an exact timer or unlimited durable worker ignores OS scheduling and expiration.

- Using phased release as install reversal overlooks manual downloads and devices already running the new version.

[developer.apple.com →](https://developer.apple.com/documentation/swiftui/managing-model-data-in-your-app)
[developer.apple.com →](https://developer.apple.com/documentation/swiftdata/schemamigrationplan)
[developer.apple.com →](https://developer.apple.com/documentation/swiftdata/versionedschema)
[developer.apple.com →](https://developer.apple.com/documentation/foundation/urlsession)
[developer.apple.com →](https://developer.apple.com/documentation/backgroundtasks)
[developer.apple.com →](https://developer.apple.com/documentation/backgroundtasks/choosing-background-strategies-for-your-app)
[developer.apple.com →](https://developer.apple.com/documentation/foundation/downloading-files-in-the-background)
[developer.apple.com →](https://developer.apple.com/documentation/security/keychain-services)
[developer.apple.com →](https://developer.apple.com/help/app-store-connect/update-your-app/release-a-version-update-in-phases/)
[developer.apple.com →](https://developer.apple.com/documentation/xcode/testing)
[developer.apple.com →](https://developer.apple.com/library/archive/technotes/tn2339/_index.html)

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.

[developer.apple.com →](https://developer.apple.com/documentation/foundation/urlsession)
[developer.apple.com →](https://developer.apple.com/help/app-store-connect/update-your-app/release-a-version-update-in-phases/)

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.

[developer.apple.com →](https://developer.apple.com/documentation/swiftdata/schemamigrationplan)
[developer.apple.com →](https://developer.apple.com/documentation/swiftdata/versionedschema)

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.

[developer.apple.com →](https://developer.apple.com/documentation/swiftdata/schemamigrationplan)
[developer.apple.com →](https://developer.apple.com/documentation/foundation/urlsession)

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.

[developer.apple.com →](https://developer.apple.com/documentation/backgroundtasks)
[developer.apple.com →](https://developer.apple.com/documentation/backgroundtasks/choosing-background-strategies-for-your-app)

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.

[developer.apple.com →](https://developer.apple.com/documentation/foundation/downloading-files-in-the-background)

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.

[developer.apple.com →](https://developer.apple.com/help/app-store-connect/update-your-app/release-a-version-update-in-phases/)

[Read the migration workflow →](/rewrite/guide.md)

## Setup

- Start in a source checkout in a local development workspace. Record minimum iOS/iPadOS versions, bundle ID, required capabilities, API schema and signing owner.

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

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

- Separate views, observable presentation state, repositories and API DTOs. Add migration fixtures and versioned network fixtures; use a fake API for tests.

- Run local simulator checks and CI using an actual destination ID. Then verify representative physical devices, offline/reconnect, permissions, VoiceOver and large text.

- 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

- What minimum OS versions, device matrix and Apple capabilities must be supported?

- Which offline/sync/conflict rules and API contract apply?

- Do notifications/media selection require permission-denied and degraded flows?

- Who owns bundle identity, signing and beta/release distribution?

## Sources

- [SwiftUI](https://developer.apple.com/swiftui/)

- [SwiftUI model data](https://developer.apple.com/documentation/swiftui/model-data)

- [SwiftData ModelContext](https://developer.apple.com/documentation/swiftdata/modelcontext)

- [URLSession](https://developer.apple.com/documentation/foundation/urlsession)

- [Testing with Xcode](https://developer.apple.com/documentation/xcode/testing)

- [Performing accessibility audits](https://developer.apple.com/documentation/accessibility/performing-accessibility-audits-for-your-app)

- [Xcode Cloud](https://developer.apple.com/xcode-cloud/)

- [Xcode command-line FAQ (archived; check local help)](https://developer.apple.com/library/archive/technotes/tn2339/_index.html)

- [Keychain services](https://developer.apple.com/documentation/security/keychain-services)

- [Background Tasks](https://developer.apple.com/documentation/backgroundtasks)

- [SwiftUI NavigationStack](https://developer.apple.com/documentation/swiftui/navigationstack)

- [Apple notification permission](https://developer.apple.com/documentation/usernotifications/asking-permission-to-use-notifications)

- [PhotosPicker](https://developer.apple.com/documentation/photosui/photospicker)

---

Canonical page: https://stack.menu/stacks/swift-swiftui-ios/
