iOS Domain Delivery
Overview
iOS Domain Delivery is the specialist method for Swift and Apple-platform changes that must respect view ownership, structured concurrency, persistence, privacy permissions, background execution limits, accessibility, tests, App Store release constraints, and rollback. It supports SwiftUI-first codebases while recognizing UIKit, Combine, Core Data, SwiftData, Keychain, and extension surfaces that appear in mature apps.
When to Use
Use for SwiftUI or UIKit features, view models, services/repositories, async/await or Combine pipelines, navigation, persistence, permissions, app or scene lifecycle, background tasks, widgets/intents, accessibility, unit/UI tests, release risk, or rollback planning.
Expert Operating Standard
Follow <resolved-supervibe-plugin-root>/docs/references/skill-expert-operating-standard.md: read local source
first, preserve evidence, keep scope narrow, verify before completion claims,
and reduce confidence when simulator/device, accessibility, persistence,
permission, or rollback evidence is missing.
Step 0 - Read source of truth
- Read the user request,
AGENTS.md, active task item, owned write set, and prior iOS decisions in.supervibe/memory/. - Inspect
Package.swiftor Xcode project conventions, app targets, schemes, Info.plist privacy keys, entitlements, persistence containers, app lifecycle, routing, dependency injection, localized resources, and nearest XCTest or XCUITest files. - Search existing patterns for
@Observable,ObservableObject,@MainActor,.task, cancellation, Combine bridges, navigation, Core Data/SwiftData, Keychain, permission prompts, localization, and accessibility modifiers. - Use CodeGraph for unfamiliar modules and CodeGraph or symbol search before changing public views, view models, protocols, route identifiers, entity models, persistence schemas, or App Intent surfaces.
When not to use
- Do not use when the task is generic planning, product shaping, design, or release governance and no iOS implementation or review boundary exists.
- Do not use when another stack, database, security, deployment, or API owner has the primary decision; hand off to that specialist and keep the iOS part scoped.
- Do not use to justify dependency swaps, broad rewrites, heavy test runs during graph execution, or mixed old-plan scope without explicit approval.
Decision tree
Rendering UI? -> SwiftUI View or UIKit controller; no direct repository logic.
Screen state? -> @MainActor view model or local @State; own async tasks and cancellation.
Existing stream pipeline? -> keep Combine if publisher-native; bridge intentionally to async sequence.
One-shot request? -> async/await with typed throws or Result; no fire-and-forget Task.
Navigation? -> typed route/path/coordinator contract; preserve deep link/back behavior.
Persistence? -> Core Data/SwiftData/GRDB/local file with migration and rollback story.
Permission needed? -> purpose string, user-intent prompt, denied/restricted/settings state.
Background work? -> BGTask, URLSession background, push, or silent refresh with OS budget.
UIKit interop? -> wrapper owns delegate lifecycle, main-thread UI updates, and teardown.
Accessibility affected? -> VoiceOver, Dynamic Type, contrast, rotor/action, 44pt target.
Procedure
Define the slice: target, minimum OS, entry view/controller, state owner, persistence or service boundary, permission need, verification command, and rollback path.
Map layers. Views render state and send intents; view models own screen state and structured tasks; services/repositories hide I/O; persistence models and migrations are isolated; UIKit adapters stay at platform seams.
Control concurrency. Prefer
async/awaitfor request/response work, actors for shared mutable state,.taskor stored cancellableTaskhandles for lifecycle,@MainActorfor UI-facing models, and explicit cancellation tests.Use Combine deliberately. Keep it for publisher-native sources such as notifications or reactive stores; bridge at one seam with
.valuesor an adapter; do not intermix Combine and async/await throughout one feature.Design navigation contracts. Preserve existing coordinator/path style, keep route arguments value-typed and minimal, handle deep links, restore state, and avoid stringly-typed route branching.
Handle persistence. Choose the repository's current persistence tool, model optionality honestly, migrate schema safely, keep file and app group paths platform-correct, and never force unwrap decoded or persisted user data.
Implement permissions as product states. Include usage description keys, rationale copy, denied/restricted/limited paths, settings recovery, and graceful degradation. Do not prompt on launch unless the platform requires it.
Respect background limits. Use BGTaskScheduler, background URLSession, notifications, or app lifecycle hooks according to the job; make work idempotent, bounded, cancelable, observable, and recoverable after launch.
Add accessibility from the first diff. Labels, hints, values, traits, Dynamic Type up to accessibility sizes, focus order, contrast, and 44pt targets are part of acceptance, not polish.
Write tests first when behavior changes. Cover view model state, async success/failure/cancellation, Combine emissions, persistence migration, permission denial, navigation, accessibility semantics, and UI flow where risk justifies XCUITest.
Verify with scoped
xcodebuild, Swift Package, lint, format, and UI test commands when policy allows. When this worker is told not to run tests, record the exact deferred commands and source evidence only.Repair loop: localize failures to view, view model, service, persistence, permission, background, or navigation boundary; patch the smallest cause; rerun the same focused command when allowed.
Read the source artifact, owned file paths, graph/task scope, and current project convention; record the evidence path, command, receipt, or runtime state that proves the starting point.
If required source, owner, dependency, runtime boundary, or approval is missing, stop and return BLOCKED with the missing field, impacted artifact, and next action instead of guessing.
After edits or reviewer findings, repair the smallest changed slice, rerun the same scoped command, and record command, exit code, pass/fail status, artifact path, confidence, and remaining blocker before completion.
Worked example
Add a SwiftUI medication reminder screen:
MedicationReminderViewrenders state from a@MainActor @Observableview model and never calls notification APIs directly.- The view model validates time, stores reminders through a repository, and schedules notifications through a protocol-defined service.
- Permission state covers not determined, denied, provisional, authorized, and settings recovery.
- Persistence migration adds a nullable
timezoneIdentifier, backfills on first write, and documents rollback. - Tests cover validation failure, permission denial, successful scheduling, cancellation, migration, dynamic type rendering, and XCUITest smoke for the create/edit flow.
Good and bad delivery paths
Good delivery path: deliver through SwiftUI view state, MainActor view model, repository, notification service protocol, permission state machine, persistence migration, and settings recovery. Runtime-specific tests include XCTest validation and permission branches, repository and migration coverage, notification scheduling/cancellation fakes, dynamic type and accessibility checks, and XCUITest smoke for changed flows. Rollback removes or flags the route, cancels scheduled notifications, and keeps nullable migration/backfill compatible. Failure boundaries are denied or provisional permission, timezone conversion, duplicate scheduling, persistence failure, notification unavailable, settings recovery, and migration mismatch.
Bad unsafe path: schedule notifications directly from SwiftUI, assume authorization, write a required migration without downgrade thinking, and verify only in preview. That path has no runtime-specific tests for the changed stack surface, no concrete rollback beyond hope or manual cleanup, and weak failure boundaries for denied or provisional permission, timezone conversion, duplicate scheduling, persistence failure, notification unavailable, settings recovery, and migration mismatch.
Anti-example or Common rationalizations
- "This Task is short, so cancellation does not matter" fails when users leave the view, app goes background, or a stale response overwrites newer state.
- "The value is always non-nil here" fails when network, persistence, localization, deep links, or restored state supplies unexpected input.
- "Combine is already in the app, so use it everywhere" fails when a simple request becomes a pipeline with hidden cancellation and error behavior.
- "We can prompt permissions during onboarding" fails if the user has not expressed intent and denial leaves no usable feature path.
Common rationalizations
- "It is just iOS, so the generic implementation pattern is enough" fails because lifecycle, runtime, data, and deployment constraints differ by stack.
- "A broad suite will prove it faster" fails in graph execution; use the declared scoped command and reserve broad validators for the final release gate.
- "We can clean up the architecture while here" fails unless that cleanup is in the accepted graph scope, has rollback, and has its own verification path.
Red flags
Task {}without stored handle,.task, cancellation path, or actor context.- UI-facing model not
@MainActorwhile mutating observable state. - Force unwraps,
try!, or implicitly unwrapped optionals on external data. - View model imports SwiftUI types unnecessarily or performs navigation by hard-coded strings.
- Missing Info.plist privacy string, entitlement explanation, or denied path.
- Fixed-height text layouts that truncate at accessibility Dynamic Type sizes.
- Persistence schema changes without migration, seed compatibility, or rollback.
Checklist
- View, view model, service/repository, persistence, and platform seams are separated.
- Concurrency has owner, actor/main-thread rule, cancellation, and error model.
- Navigation, permissions, background work, and persistence each name rollback or compatibility behavior when touched.
- Accessibility covers VoiceOver, Dynamic Type, contrast, traits, values, and hit targets.
- XCTest/XCUITest or deferred final-gate commands cover success, failure, cancellation, permission, and migration paths.
Failure modes
- The iOS specialist applies a generic pattern and misses framework-owned lifecycle, typing, permission, migration, cache, or deployment behavior.
- The worker mixes a new graph task with stale plan scope and creates a larger review surface than the MVP flow needs.
- The task closes with prose only: no source evidence, no scoped command or final-gate deferral, no rollback, and no next action for blockers.
Output contract
status: PASS, BLOCKED, PARTIAL, or DEFERRED.slice: SwiftUI, UIKit, view model, service, persistence, permission, background, navigation, accessibility, or release behavior changed.boundaries: files touched by layer and why each belongs there.concurrency: async/await, Combine, actor, cancellation, and main-thread decisions.persistencePermissionsBackground: schema/data, privacy, background budget, and rollback evidence.accessibilityAndRelease: Dynamic Type, VoiceOver, device/simulator, phased release, and rollback notes.tests: focused commands run with exit code or explicit deferral reason.confidence: score with caps from missing UI, device, migration, permission, accessibility, or release evidence.
Guard rails
- DO NOT: use force unwraps or
try!for user, network, persistence, deep link, or permission data. - DO NOT: introduce a new architecture, router, persistence framework, or dependency container when the repo already has one.
- DO NOT: expand entitlements, background modes, or privacy permissions without product reason, user flow, and rollback.
- ALWAYS: keep UI mutations on the main actor and long-running work cancellable.
- ALWAYS: localize user-facing strings and verify accessibility at large content sizes when UI changes.
Verification
Unit: XCTest for view models, services, repositories, actor behavior, async/await cancellation, and Combine emissions.
UI: XCUITest or ViewInspector/snapshot tests where local project conventions support them.
Persistence: migration tests or seed compatibility checks for Core Data, SwiftData, GRDB, file storage, or Keychain wrapper changes.
Accessibility: Accessibility Inspector, VoiceOver traversal, Dynamic Type at accessibility sizes, and contrast checks for changed surfaces.
Release: scoped
xcodebuild test,swift test,swiftlint,swift-format, build/archive dry run, phased rollout, feature flag, or rollback notes when policy allows.If any scoped check fails or new evidence appears, repair the smallest changed slice, rerun the same scoped command, and record command, exit code, pass/fail status, blockers, and final-gate deferrals before claiming completion.
Supporting references
- examples/delivery.md - worked iOS implementation example, anti-example, and verification fixture.
- domain-packs/ios.md - iOS practice pack with review matrix, rollback prompts, and MVP guard rails.
- evals/regression.json - Use when calibrating ios-domain-delivery trigger boundaries, happy-path/failure-path coverage, boundary rollback behavior, or resource-tree regressions.
Related
supervibe:source-driven-developmentsupervibe:tddsupervibe:test-strategysupervibe:verificationsupervibe:code-reviewsupervibe:stacks/ios:ios-developersupervibe:stacks/android:android-developersupervibe:stacks/flutter:flutter-developer
Supporting references
Resource tree hardening
references/practice-pack.md- Read when ios-domain-delivery needs deeper load rules, local evidence anchors, gotchas, or a final checklist.scripts/self-check.mjs- Run with--checkbefore claiming the ios-domain-delivery resource tree is complete; add--jsonwhen machine-readable evidence is needed.evals/regression.json- Use when tuning ios-domain-delivery trigger boundaries or checking should-trigger and should-not-trigger prompts.examples/workflow.md- Load when a concrete ios-domain-delivery workflow example or anti-example would clarify the next action.templates/output-contract.md- Use when emitting agent-output so status, evidence, blockers, confidence, and nextAction stay consistent.
Source: vTRKA/supervibe — distributed by TomeVault.