Skip to content

Add SwiftLint build phase to SwiftUI App template - #65

Merged
jmarek41 merged 5 commits into
feature/state-management-templatesfrom
feature/swiftlint-build-step
Sep 4, 2026
Merged

jmarek41 merged 5 commits into
feature/state-management-templatesfrom
feature/swiftlint-build-step

Conversation

@ssestak

@ssestak ssestak commented May 12, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Adds a SwiftLint Run Script build phase to the SwiftUI App Xcode project template. Generated projects autocorrect on every build and surface remaining warnings in Xcode's issue navigator — no manual install of the phase itself needed after project creation.

Stacked on #64. PR base will retarget to main automatically once #64 merges.

Flow

flowchart LR
    A[Build starts] --> B[Compile Sources]
    B --> C[Link Frameworks]
    C --> D[Copy Resources]
    D --> E[SwiftLint phase]
    E --> F{swiftlint on PATH?}
    F -- yes --> G[swiftlint --fix --quiet]
    G --> H[swiftlint]
    H --> I[Warnings in Xcode]
    F -- no --> J[Build-log warning:<br/>'brew install swiftlint']
Loading

Key Changes

  • Templates/SwiftUI App.xctemplate/TemplateInfo.plist
    • New BuildPhases array on the app target with a single ShellScript phase named SwiftLint.
    • ENABLE_USER_SCRIPT_SANDBOXING = NO added to SharedSettings — Xcode 15+ sandboxes script phases by default, which blocks SwiftLint from reading project files (Sandbox: swiftlint deny file-read-data …).
  • Templates/SwiftUI App.xctemplate/App.swift — new #warning instructing the developer to drag the SwiftLint phase above Compile Sources.
  • Script exports /opt/homebrew/bin for Apple Silicon, runs swiftlint --fix --quiet then swiftlint. Graceful fallback emits a build-log warning when SwiftLint isn't installed.

Known Limitation — Phase Position

Xcode project templates can only append custom phases to a target — there is no documented key (InsertBefore, Position, ReplaceBuildPhases, …) to insert before phases inherited from com.apple.dt.unit.applicationBase. Confirmed empirically (declaring Sources/Frameworks/Resources in the child array creates duplicate empty phases instead of replacing the inherited ones) and via research of public Xcode templates on disk and community projects (surfstudio, GoodRequest) — all of them rely on the same after-Sources position.

Consequence: as generated, the SwiftLint phase runs after Compile Sources, so --fix corrections apply to the next build. The new #warning in App.swift tells the developer to drag the phase above Compile Sources once — the reorder persists in project.pbxproj.

Out of Scope

  • Shipping a default .swiftlint.yml inside the template — left to the developer.
  • Build phase for the Scene file template — file templates can't inject build phases.
  • Programmatic post-creation reordering of project.pbxproj — would require shipping a Ruby/Python script with the template.

Test Plan

  • Generate a project from the SwiftUI App template with SwiftLint installed; confirm the SwiftLint build phase appears on the target (one, not duplicated)
  • Introduce a deliberate style violation; confirm it appears as an Xcode warning
  • Manually drag the SwiftLint phase above Compile Sources, introduce an autocorrectable violation; confirm --fix corrects it on the same build
  • Generate on a machine without SwiftLint; confirm the build succeeds with the install-hint warning
  • Confirm no Sandbox: swiftlint deny … errors in the build log
  • Confirm the #warning about reordering appears in the issue navigator on first build

Šimon Šesták added 3 commits May 12, 2026 14:08
Adds a Run Script build phase that runs swiftlint --fix followed by
swiftlint, so generated projects autocorrect and surface remaining
warnings in Xcode's issue navigator. Falls back to a friendly warning
when SwiftLint is not installed.
Declare the full BuildPhases array so SwiftLint runs first, ensuring
--fix corrects sources before they are compiled. Also disable
ENABLE_USER_SCRIPT_SANDBOXING so SwiftLint can read project files
under Xcode 15+'s default sandbox.
Xcode template inheritance only appends BuildPhases — declaring
Sources/Frameworks/Resources in the child template creates empty
duplicates instead of replacing the inherited ones. Keep only the
SwiftLint phase and surface a #warning telling the developer to drag
it above Compile Sources so --fix corrections apply to the current
build instead of the next one.
@ssestak
ssestak force-pushed the feature/swiftlint-build-step branch from 5fb78e5 to cc2e525 Compare May 12, 2026 13:08
@ssestak
ssestak requested a review from jmarek41 May 12, 2026 13:10
Comment thread Templates/SwiftUI App.xctemplate/TemplateInfo.plist
Both scripts now skip Xcode preview builds, prepend /opt/homebrew/bin
only when present, and report a missing SwiftLint as an error and a
missing SwiftFormat as a warning, matching the production projects.
SwiftFormat is appended before SwiftLint so formatting runs first.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@jmarek41

jmarek41 commented Sep 4, 2026

Copy link
Copy Markdown
Member

Pushed 47ababb applying the review comments so the template matches Futrumi and iRozhlas:

  • Added a SwiftFormat run script phase, appended before SwiftLint so formatting runs first.
  • Both scripts now skip Xcode preview builds (BUILD_DIR contains Previews), prepend /opt/homebrew/bin only when present, and use swiftlint --fix --quiet.
  • Missing SwiftLint is reported as error: (as in the handbook and both projects); missing SwiftFormat as warning:.
  • The #warning in App.swift now asks to drag both phases above Compile Sources, in that order.

Handbook counterpart: futuredapp/Engineering-Handbook#119.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@jmarek41
jmarek41 merged commit d842c04 into feature/state-management-templates Sep 4, 2026
@jmarek41
jmarek41 deleted the feature/swiftlint-build-step branch September 4, 2026 07:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants