はじめに
Xcode16よりAppleの公式Formatterであるswiftlang/swift-formatが内蔵されています。
その為追加インストールなしで、ビルド時にswiftファイルの自動整形を行うことができます。
備忘録も兼ね、導入方法についてまとめたいと思います。
手順
手順の概要は以下の通りです。
- プロジェクト設定画面を開く
- Run Scriptを追加する
- スクリプトの編集を行う
- User Script Sandboxingを編集
- .swift-formatの追加
1. プロジェクト設定画面を開く
Xcodeの左のナビゲーターからプロジェクトを選択し(下図①)、
TARGETSのプロジェクト名を選択します。(下図②)
2. Run Scriptを追加する
タブからBuild Phasesを選択し(下図①)、+ボタンを選択し(下図②)、
New Run Script Phaseを選択します。(下図③)
3. スクリプトの編集を行う
Run Scriptに下記スクリプトを記載します。
この時、Based on dependency analysisのチェックを外しておきます。(下図赤枠部)
# フォーマットしたいフォルダを指定
set -e
PROJECT_DIR="${SRCROOT}"
# Xcode内蔵のswift-formatを使用してフォーマットを実行
if xcrun --find swift-format >/dev/null 2>&1; then
xcrun swift-format format \
--in-place \
--recursive \
"$PROJECT_DIR"
xcrun swift-format lint \
--recursive \
"$PROJECT_DIR"
else
echo "warning: swift-format が見つかりません。"
fi
スクリプトの内容
このスクリプトでは、まず以下で swift-format が利用できるか確認しています。
if xcrun --find swift-format >/dev/null 2>&1; then
swift-format が見つかった場合は、以下を実行します。
xcrun swift-format format \
--in-place \
--recursive \
"$PROJECT_DIR"
format は、Swiftファイルを実際にフォーマットするコマンドです。
--in-place を指定することで、フォーマット結果をファイルへ直接反映します。
また、--recursive を指定しているため、指定したディレクトリ以下を再帰的に処理します。
その後、以下を実行します。
xcrun swift-format lint \
--recursive \
"$PROJECT_DIR"
lint は、指定したフォーマットルールに従っているかをチェックするためのコマンドです。
つまり、
format
↓
コードを自動整形
lint
↓
フォーマットルールに違反していないか確認
という流れになります。
4. User Script Sandboxingを編集
PROJECCT > TARGETS > "project file"からBuild SettingsとAllを選択し、
Build OptionsのUser Script SandboxingをNoに変更します。
5. .swift-formatの追加
.swift-formatはJSON形式で記述されるフォーマットの設定ファイルであり、
プロジェクトのルート配置する必要があります。
ProjectRoot/
├── .swift-format ←ここに置く
├── AppName.xcodeproj
├── AppName/
│ └── ContentView.swift
│── AppNameTests/
|── AppNameUITests/
プロジェクトのルートに.swift-formatファイルを作成し、
フォーマットの設定を記述します。
フォーマットの設定に関しては、下記コマンドで確認できるダンプを参考に設定すると良い思います。
# swift-formatをインストール済みの場合
swift-format dump-configuration
# swift-formatをインストールしていない場合
xcrun swift-format dump-configuration
筆者は下記のように設定しました。
{
"fileScopedDeclarationPrivacy": {
"accessLevel": "private"
},
"indentConditionalCompilationBlocks": true,
"indentSwitchCaseLabels": false,
"indentation": {
"spaces": 4
},
"lineBreakAroundMultilineExpressionChainComponents": true,
"lineBreakBeforeControlFlowKeywords": false,
"lineBreakBeforeEachArgument": true,
"lineBreakBeforeEachGenericRequirement": false,
"lineLength": 100,
"maximumBlankLines": 1,
"multiElementCollectionTrailingCommas": true,
"noAssignmentInExpressions": {
"allowedFunctions": [
"XCTAssertNoThrow"
]
},
"prioritizeKeepingFunctionOutputTogether": true,
"respectsExistingLineBreaks": true,
"rules": {
"AllPublicDeclarationsHaveDocumentation": false,
"AlwaysUseLiteralForEmptyCollectionInit": true,
"AlwaysUseLowerCamelCase": true,
"AmbiguousTrailingClosureOverload": true,
"BeginDocumentationCommentWithOneLineSummary": false,
"DoNotUseSemicolons": true,
"DontRepeatTypeInStaticProperties": true,
"FileScopedDeclarationPrivacy": true,
"FullyIndirectEnum": true,
"GroupNumericLiterals": true,
"IdentifiersMustBeASCII": true,
"NeverForceUnwrap": true,
"NeverUseForceTry": true,
"NeverUseImplicitlyUnwrappedOptionals": true,
"NoAccessLevelOnExtensionDeclaration": true,
"NoAssignmentInExpressions": true,
"NoBlockComments": true,
"NoCasesWithOnlyFallthrough": true,
"NoEmptyTrailingClosureParentheses": true,
"NoLabelsInCasePatterns": true,
"NoLeadingUnderscores": true,
"NoParensAroundConditions": true,
"NoPlaygroundLiterals": true,
"NoVoidReturnOnFunctionSignature": true,
"OmitExplicitReturns": false,
"OneCasePerLine": true,
"OneVariableDeclarationPerLine": true,
"OnlyOneTrailingClosureArgument": true,
"OrderedImports": false,
"ReplaceForEachWithForLoop": false,
"ReturnVoidInsteadOfEmptyTuple": true,
"TypeNamesShouldBeCapitalized": true,
"UseEarlyExits": true,
"UseExplicitNilCheckInConditions": true,
"UseLetInEveryBoundCaseVariable": true,
"UseShorthandTypeNames": true,
"UseSingleLinePropertyGetter": true,
"UseSynthesizedInitializer": true
}
}
swift-formatのリンタとフォーマットのルールは、RuleDocumentaion.mdに記載されていますので、こちらを参照するといいと思います。
参考文献