Setup Apple App

This documentation was generated with the assistance of AI. Please report any inaccuracies.

Generate a complete, buildable native Apple application scaffold — a project-generator manifest (XcodeGen’s project.yml, or Tuist’s Project.swift + Tuist.swift) declaring one application target <AppName>-<Platform> per selected platform (iOS/iPadOS, macOS, watchOS, visionOS), a UI-framework-free Packages/Core local SwiftPM package (Models/, Services/, plus ViewModels/ for MVVM or Controllers/ + a passive-view protocol for MVC) whose Swift Testing suite covers 100% of its lines, the per-platform UI layer (SwiftUI everywhere, or programmatic UIKit for the iOS/iPadOS target), per-platform Swift Testing unit-test and XCUITest UI-test targets, lint/format config, a .ui-quality.yml, an optional gated fastlane/Fastfile, and sonar-project.properties — from the templates in the skill’s reference/ files. It announces whether it is scaffolding from scratch or updating an existing app, and warns and asks Update / Keep as is / Replace / Stop for each of its files that already exists.

Purpose

iru-setup-apple-app bootstraps a new native Apple application from a house template: one XcodeGen or Tuist manifest per repository, one application target per selected platform with SWIFT_STRICT_CONCURRENCY = complete, the app’s MARKETING_VERSION/CURRENT_PROJECT_VERSION and a pinned unit-test host, a shared Packages/Core package (Foundation + Observation only) that holds all testable logic outside any UI framework, and per-platform Swift Testing/XCUITest targets driven by a --uitesting launch argument and accessibility identifiers on every control. Following the catalog’s UI contract (see Building user interfaces), the UI tests also capture the sample screen in every left-to-right/right-to-left × light/dark cell into .ui-screenshots/ through a shared Tests/Support/UIScreenshots.swift helper, and run performAccessibilityAudit() at the level configured in .ui-quality.yml. With ui: uikit the iOS/iPadOS target becomes a programmatic UIKit app — AppDelegate + UIWindowSceneDelegate with a scene manifest (mandatory with the 27 SDK), a UINavigationController root and a code-laid-out UIViewController, no storyboards — backed by an MVC controller core (recommended) or an MVVM view model rendered through UIKit Observation tracking (UIObservationTrackingEnabled added below iOS 26); watchOS, visionOS and macOS targets always stay SwiftUI. visionOS is a native SwiftUI target, not "Designed for iPad"; tvOS, Mac Catalyst and compatibility destinations are not supported. The resulting layout is the scaffold contract the Swift coding and gate skills, iru-explore and Setup Swift GitHub Workflows rely on. It never commits the generated .xcodeproj/.xcworkspace — those are always generator output, regenerated on demand by xcodegen generate/tuist generate and gitignored by the companion Setup Swift Gitignore. It is the Swift/Apple equivalent of Setup Android App and Setup React Native App for the other app-shaped stacks in this catalog; use Setup macOS App for a macOS-only (SwiftUI or AppKit) desktop app and Setup Swift Library for a pure SwiftPM library with no app target.

Purpose group

Plugin

Shipped by the iru-swift plugin of the ai-catalog marketplace (install with /plugin install iru-swift@ai-catalog; see Plugins).

Invocation

/iru-setup-apple-app

Inputs

Argument Required Description Default

args

No

Pre-resolved parameters passed by an orchestrating skill (e.g. Setup Swift Repository) as newline-separated key: value lines covering every field below, parsed in Step 0 so this skill doesn’t ask about any field already resolved this way.

None — if absent or unrecognized, every field is asked interactively.

app-name

Yes (unless supplied via args)

Display name, e.g. My App, written to CFBundleDisplayName and slugified to derive the bundle id and directory names.

None

bundle-id-prefix

Yes (unless supplied via args)

Reverse-DNS prefix, e.g. com.example. The app target’s bundle id is <bundle-id-prefix>.<app-slug>; the watchOS companion (when both an iOS/iPadOS and a watchOS target are selected) is <bundle-id-prefix>.<app-slug>.watchkitapp.

None

platforms

Yes (unless supplied via args)

Multi-select subset of ios, ipados, macos, watchos, visionos. ipados alone or alongside ios widens the one iOS target’s TARGETED_DEVICE_FAMILY rather than adding a second target (and, with UIKit, enables multiple scenes); macos/watchos/visionos each produce their own SwiftUI target (visionos is a native visionOS app). watchos without ios/ipados is a standalone watch app. tvos is rejected as not supported.

None

ui

No

swiftui or uikit — applies only to the iOS/iPadOS target (programmatic UIKit with a scene delegate, no storyboards); ignored when neither ios nor ipados is selected.

swiftui (asked when ios/ipados is selected)

architecture

No

mvvm or mvc. ui: swiftui is always mvvm (never asked; mvc rejected); ui: uikit offers mvc (recommended) or mvvm. Shapes Packages/Core — Controllers/ for MVC, ViewModels/ for MVVM and every SwiftUI target, so UIKit MVC plus SwiftUI platforms gets both.

mvvm for SwiftUI; mvc recommended for UIKit

generator

No

xcodegen or tuist — asked with a stated recommendation when unset: XcodeGen for a single-target app, Tuist for a project expected to grow multiple internal SwiftPM packages.

Asked when unset

min-deployment-target

No

The iOS deployment target (e.g. 26.0); other platforms get their equivalent (N.0 from 26 on; 18.0 maps to macOS 15 / watchOS 11 / visionOS 2). Never below 18.0.

26.0, lowered only if no 26+ simulator runtime is installed (Step 4)

distribution

No

none/internal/store — gates which Fastlane lanes (test only, +beta, or +beta+release) get written.

Asked when unset

signing

No

fastlane-match/api-key/none — gates the Fastfile’s signing setup and which CI secrets Step 8 flags.

Asked when unset (defaults toward none when distribution: none, confirmed with the user)

license

Yes (unless supplied via args)

Resolved via AskUserQuestion: MIT License, Apache License 2.0, No license, or Other (asked for a display name and URL directly).

None — user must choose

developer-name / developer-email / organization-url

Yes (unless supplied via args)

Recorded in Step 8’s report and each Info.plist’s NSHumanReadableCopyright.

None

open-source

No

Whether the repository is open source; drives the recommended default for sonar.

Asked directly (yes/no) when not resolved via args

sonar

No

Whether to wire up a SonarQube/SonarCloud scan (cloud/self-hosted/none), plus sonar-organization/sonar-project-key/sonar-host-url when not none; gates sonar-project.properties.

cloud only when open-source is yes and visibility is public (or unknown); otherwise asked with none recommended (SonarCloud needs a paid plan for private projects), offering self-hosted SonarQube second. With ci: no, none is recommended and a chosen scan only gets the build-side sonar-project.properties.

visibility

No

public, private or internal (behaves like private). Only used to key the sonar recommendation; never asked by this skill, it only arrives via args.

Unset (treated as unknown)

ci

No

yes/no — whether GitHub Actions workflows will exist to send Sonar reports. With no, the skill says plainly that nothing will send analysis automatically; if Sonar is still chosen, Step 8 notes that no CI step sends reports.

Unset

accessibility

No

enforce, report, off or none — the automated accessibility level of the UI tests, written to .ui-quality.yml: enforce fails the UI tests on any performAccessibilityAudit() issue, report attaches and lists issues without failing, off skips the audit test, and none opts out entirely (writes accessibility: none, installs no accessibility tooling and omits the accessibility attributes from the templates); a prior .ui-quality.yml value is never silently flipped in mode: existing.

enforce (asked when unset)

on-existing

No

ask, keep, update or replace — answers Step 1’s per-file question for every existing owned file at once (the warning and file list are still printed).

ask

mode

No

new or existing. existing marks an update rather than a from-scratch run but no longer means a silent gap-fill: Step 1 still warns and asks per existing file. The files actually on disk decide whether each is created or updated. Hand-written app/test source is never overwritten either way.

new

Outputs

  • project.yml (XcodeGen) or Project.swift + Tuist.swift (Tuist), per generator, with one target/scheme triple per selected platform.

  • Packages/Core/Package.swift (with swift-docc-plugin), a DocC catalog, Models/ (Greeting), Services/ (GreetingProviding, GreetingService, GreetingError), ViewModels/GreetingViewModel (MVVM / any SwiftUI target) and/or Controllers/GreetingController + GreetingView protocol (MVC), and Tests/CoreTests at 100% line coverage.

  • Per selected platform, Targets/<Platform>/Sources — SwiftUI <AppName>App.swift + ContentView.swift, or for the iOS target with ui: uikit AppDelegate.swift, SceneDelegate.swift, GreetingViewController.swift and GreetingLayout.swift — plus LaunchOptions.swift; Tests/ (Swift Testing: LaunchOptionsTests and <AppName>Tests, or GreetingViewControllerTests/AppShellTests for UIKit); and UITests/<AppName>UITests.swift (XCUITest).

  • .swiftlint.yml, .swift-format and .ui-quality.yml (UI contract settings: accessibility level, directions, themes) at the repository root.

  • Per platform test target, Tests/Support/UIScreenshots.swift (the UI contract capture helper, also compiled into the UI-test target); the UI test writes the sample screen’s captures to .ui-screenshots/screen/<ScreenType>/default--<ltr|rtl>--<light|dark>--<platform-variant>.png (gitignored) and attaches accessibility-audit issues to the .xcresult.

  • fastlane/Fastfile, with only the lanes distribution/signing back — never a lane this skill can’t support. The platform :ios lanes (test, build, beta, release) are written only when an iOS/iPadOS target was selected.

  • sonar-project.properties, only when sonar != none — its SwiftLint input is sonar.externalIssuesReportPaths=swiftlint-sonar.json, the report the workflows skill’s lint job produces.

  • No .xcodeproj/.xcworkspace — those are xcodegen generate/tuist generate output, never written here.

  • A report of every resolved input, the mode announced up front, per file whether it was created, updated, replaced or kept as is (with each mismatch listed for a kept file, and how visibility/ci shaped the Sonar recommendation), the Step 7 verification results, the scaffold contract table (targets, schemes, destinations, versions, architecture markers, test/coverage/lint commands, UI-test hooks, UIKit specifics), and explicit warnings (review before build/commit, run iru-setup-swift-gitignore, visionOS runtime needed for its tests, signing/Sonar credentials still needed).

Execution flow

flowchart TD A[Start /iru-setup-apple-app] --> B{"Step 0: args supplied?"} B -- yes --> C[Parse key: value pairs from args] B -- no --> D[Treat every field as unset] C --> E["Step 1: Survey repository for owned files\n(manifest, Packages/Core, lint/format,\n.ui-quality.yml, Fastfile, sonar, .xcodeproj)"] D --> E E --> F{"What already exists?"} F -- nothing --> F1["Announce: scaffolding\nfrom scratch"] F -- "only .xcodeproj" --> G3{"Adopt XcodeGen/Tuist\nor stop?"} F -- "macOS-app layout" --> G5{"Suggest iru-setup-macos-app;\ncontinue on explicit yes?"} G5 -- no --> Z1 G5 -- yes --> F2 G3 -- stop --> Z1[Report untouched; end] G3 -- adopt --> F2 F -- "owned files exist" --> F2["Announce: updating an\nexisting app; list files"] F2 --> G{"on-existing set to\nkeep, update or replace?"} G -- yes --> G1["Apply it to every\nexisting file"] G -- no --> G2{"AskUserQuestion per file:\nUpdate / Keep as is /\nReplace / Stop"} G2 -- Stop --> Z1 G2 -- other --> G4["Record per-file decision;\nread existing manifest as\ndefaults if replaced"] F1 --> H G1 --> H G4 --> H["Step 2: Collect inputs\n(app-name, bundle-id-prefix,\nplatforms incl. visionOS,\nui swiftui/uikit, architecture\nmvvm/mvc, generator,\nmin-deployment-target,\ndeveloper info, license,\nopen-source, sonar,\ndistribution, signing,\naccessibility)"] H --> I["Step 3: Infer repository info\n(owner/repo/host)"] I --> J["Step 4: Look up XcodeGen/Tuist/\nSwiftLint/Fastlane versions"] J --> K["Step 5: Resolve reference templates\n(manifest, Packages/Core per\narchitecture, SwiftUI or UIKit\ntargets, tests, UI-test capture\nhelper, swiftlint, .ui-quality.yml,\nFastfile, sonar)"] K --> L["Step 6: Apply each file's decision:\ncreate / update (diff first) /\nkeep as is / replace;\nnever overwrite hand-written source"] L --> M["Step 7: Verify via iru-gate-runner\n(swift test Core at 100% lines,\nxcodegen generate, xcodebuild test\nper platform, swiftlint, swift format;\nmissing runtime = unverified)"] M --> N["Step 8: Report mode, per-file action,\nscaffold contract, mismatches\nand warnings"]

Dependencies

Invokes

None — iru-setup-apple-app is a leaf skill; it does not call any other skill in this catalog. Verification in Step 7 runs through the iru-gate-runner agent, not another skill.

Invoked by

  • Setup Swift Repository — runs iru-setup-apple-app for the app flavor, passing the shared bootstrap inputs (including platforms, ui and architecture) through args so this skill’s own Step 2 questions are skipped.

  • iru-gate-runner — Step 7 runs swift test --enable-code-coverage
    llvm-cov report against Packages/Core, xcodegen generate (or tuist generate --no-open), xcodebuild test per platform scheme (serialized behind .simulator-test.lock), swiftlint lint --strict and swift format lint --strict through this agent so the caller gets a compact pass/fail summary. Missing tools, non-macOS hosts or a missing simulator runtime (typically visionOS, which falls back to build-for-testing) are reported as unverified.

Source

SKILL.md on GitHub — the file this page was generated from.