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