🦇

Xcode16に内蔵されたswift-formatの導入方法

に公開

ゴール

Apple公式Formatterであるswiftlang/swift-formatを使用し、ビルド時にswiftファイルが自動整形されていること。

https://github.com/swiftlang/swift-format

本記事では、ゴールを達成するために必要な設定であるRunScript.swift-formatの使い方とそれぞれの雛形を紹介します。

前提条件の確認は、次の記事が大変参考になります。
https://zenn.dev/treastrain/articles/8f461a75731562

RunScript追加手順

  1. Xcodeプロジェクト設定を開きます。
  2. Xcodeのプロジェクト設定画面(TARGETS → 対象Target)へ移動します。
  3. タブからBuild Phasesを選択します。
  4. 左上の「+」ボタンをクリックし、New Run Script Phaseを押下します。

swift-formatを使用してコードを自動整形するRunScript

# フォーマットしたいフォルダを指定
TARGET_DIR="$SRCROOT/YourAppName"

# Xcode内蔵のswift-formatを使用してフォーマットを実行
if xcrun --find swift-format >/dev/null; then
  xcrun swift-format format --in-place --recursive "$TARGET_DIR"
else
  echo "warning: Xcodeにswift-formatが見つかりません。Xcode 16以降を使用してください。"
fi

(オプション)swift-formatのリンター機能を使う場合は、次の内容を追加します。

xcrun swift-format lint --recursive "$TARGET_DIR"

.swift-format中のrules{}に記載した内容が警告を出す基準となります。

.swift-formatの追加手順

.swift-formatは、JSON形式で記述され、プロジェクトのルート(.xcodeprojxcworkspaceがある階層)に配置する必要があります。

階層のイメージ
SwiftProjectRoot/
├── .swift-format    ←ここに置く
├── YourAppName.xcodeproj
├── YourAppName/
│   └── Group/
│       └── ContentView.swift

.swift-formatの雛形

次のコマンドで、swift-formatのダンプを確認できます。

swift-format dump-configuration

次のダンプが確認できるので、こちらを参考に適宜修正すると良いと思います。

.swift-format
{
  "fileScopedDeclarationPrivacy" : {
    "accessLevel" : "private"
  },
  "indentConditionalCompilationBlocks" : true,
  "indentSwitchCaseLabels" : false,
  "indentation" : {
    "spaces" : 2
  },
  "lineBreakAroundMultilineExpressionChainComponents" : false,
  "lineBreakBeforeControlFlowKeywords" : false,
  "lineBreakBeforeEachArgument" : false,
  "lineBreakBeforeEachGenericRequirement" : false,
  "lineBreakBetweenDeclarationAttributes" : false,
  "lineLength" : 100,
  "maximumBlankLines" : 1,
  "multiElementCollectionTrailingCommas" : true,
  "noAssignmentInExpressions" : {
    "allowedFunctions" : [
      "XCTAssertNoThrow"
    ]
  },
  "prioritizeKeepingFunctionOutputTogether" : false,
  "reflowMultilineStringLiterals" : {
    "never" : {

    }
  },
  "respectsExistingLineBreaks" : true,
  "rules" : {
    "AllPublicDeclarationsHaveDocumentation" : false,
    "AlwaysUseLiteralForEmptyCollectionInit" : false,
    "AlwaysUseLowerCamelCase" : true,
    "AmbiguousTrailingClosureOverload" : true,
    "AvoidRetroactiveConformances" : true,
    "BeginDocumentationCommentWithOneLineSummary" : false,
    "DoNotUseSemicolons" : true,
    "DontRepeatTypeInStaticProperties" : true,
    "FileScopedDeclarationPrivacy" : true,
    "FullyIndirectEnum" : true,
    "GroupNumericLiterals" : true,
    "IdentifiersMustBeASCII" : true,
    "NeverForceUnwrap" : false,
    "NeverUseForceTry" : false,
    "NeverUseImplicitlyUnwrappedOptionals" : false,
    "NoAccessLevelOnExtensionDeclaration" : true,
    "NoAssignmentInExpressions" : true,
    "NoBlockComments" : true,
    "NoCasesWithOnlyFallthrough" : true,
    "NoEmptyLinesOpeningClosingBraces" : false,
    "NoEmptyTrailingClosureParentheses" : true,
    "NoLabelsInCasePatterns" : true,
    "NoLeadingUnderscores" : false,
    "NoParensAroundConditions" : true,
    "NoPlaygroundLiterals" : true,
    "NoVoidReturnOnFunctionSignature" : true,
    "OmitExplicitReturns" : false,
    "OneCasePerLine" : true,
    "OneVariableDeclarationPerLine" : true,
    "OnlyOneTrailingClosureArgument" : true,
    "OrderedImports" : true,
    "ReplaceForEachWithForLoop" : true,
    "ReturnVoidInsteadOfEmptyTuple" : true,
    "TypeNamesShouldBeCapitalized" : true,
    "UseEarlyExits" : false,
    "UseExplicitNilCheckInConditions" : true,
    "UseLetInEveryBoundCaseVariable" : true,
    "UseShorthandTypeNames" : true,
    "UseSingleLinePropertyGetter" : true,
    "UseSynthesizedInitializer" : true,
    "UseTripleSlashForDocumentationComments" : true,
    "UseWhereClausesInForLoops" : false,
    "ValidateDocumentationComments" : false
  },
  "spacesAroundRangeFormationOperators" : false,
  "spacesBeforeEndOfLineComments" : 2,
  "tabWidth" : 8,
  "version" : 1
}

インデントはデフォルトで2ですが、Xcodeは通常4なので、"indentation""spaces" : 4に変更しておくと良いと思います。

  "indentation" : {
-    "spaces" : 2
+    "spaces" : 4
  }

swift-formatのリンターとフォーマッターのルールは、RuleDocumentaion.mdに記載されています。

https://github.com/swiftlang/swift-format/blob/main/Documentation/RuleDocumentation.md#L3-L11

ルールの詳細な内容は、実例とともに日本語で解説されている次の記事が大変参考になります。
https://zenn.dev/kyome/articles/a2dad672c0a65c

Discussion