3
0

変更したファイルだけにSwift Formatしたい

Last updated at Posted at 2023-11-02

問題

iOS/macプロジェクトでswift formatを使っているが、プロジェクトの全ファイルではなく変更したファイルだけをformat掛けたい。

Xcodeのビルドスクリプトに入れていることが多くて、プロジェクトが大きくなった時に全ファイルにかけるとビルド時間に影響し、開発の効率が落ちる。だから、差分のファイルだけに掛けたい。ソースバージョン管理(Gitとか)をしているなら対応はできる!

追記: SwiftUIを使っている場合は Previewのために内部的にビルドが走ることがよくある。その場合は このanswerの様に if [ "${ENABLE_PREVIEWS}" = "YES" ]; then ... 制御し、lintのみとした方がいい。そうしないと整形処理で新しく生成されるファイルと自分が編集しているファイルの中身が違っていてXcodeが頻繁にびっくりする。

アプローチ

git statusコマンドで取得するファイル一覧をswift formatに渡すことができる。そうすることで 余計に整形処理を実行せずに済む!

Apple製のSwift-Formatの場合

空白で区切ってファイル一覧を作成しパラメータとして渡すことが期待されている。変更ファイルが0個の場合はコマンドとしておかしいので 0個の場合は何もしないようにする

実装:

# 変更されたSwiftファイルを取得(1行で空白区切り)
changedPaths=$(git status -s . | awk '
    /^D/ {next} 
    !/\.swift$/ {next}
    /^R|^C/ {print $4; next}
    {print $2}
    ' | xargs -n1)


if [ -n "$changedPaths" ]; then
    # 整形外科通院
    echo "Format and Lint Swift Sources: $changedPaths"
    swift-format format --in-place --configuration my-config-file.json $changedPaths
    swift-format lint --configuration my-config-file.json $changedPaths
else
    echo "Format and Lint Swift Sources: No Swift files have changed."
fi

ちなみに、全部掛けたかったら 以下で良い

# 全部整形外科通院
echo "Lint and Format Swift Sources: doing all files"
swift-format format --in-place --configuration my-config-file.json --recursive .
swift-format lint --configuration my-config-file.json --recursive .

Nicklockwood製のSwiftFormatの場合

環境変数でファイル一覧を渡す必要がある。SCRIPT_INPUT_FILE_COUNTSCRIPT_INPUT_FILE_0, SCRIPT_INPUT_FILE_1, ... shell scriptではよくあるパターン

実装:

# 変更されたSwiftファイルを取得
changedPaths=$(git status -s . | awk '
    /^D/ {next} 
    !/\.swift$/ {next}
    /^R|^C/ {print $4; next}
    {print $2}
    ' | xargs -n1)
counter=0
for file in $changedPaths; do
  export SCRIPT_INPUT_FILE_$counter="$SRCROOT/$file"
  counter=$((counter + 1))
done
export SCRIPT_INPUT_FILE_COUNT=$counter
# 整形外科通院
swiftformat --config my-config-file.yml --scriptinput

Realm製のSwiftLintの場合

ドキュメント上、他と同様に環境変数として渡せると書かれているが何故かlintの方が効かないので ファイルリストとして渡す

実装:

# 変更されたSwiftファイルを取得(1行で空白区切り)
changedPaths=$(git status -s . | awk '
    /^D/ {next} 
    !/\.swift$/ {next}
    /^R|^C/ {print $4; next}
    {print $2}
    ' | xargs -n1)
# 整形外科通院
# auto correct
swiftlint $changedPaths --fix --format
# lint
swiftlint $changedPaths --config my-config-file.yml

あのawkコマンドの詳細を気になるあなたへ

まず、git status -s は以下のような出力をする

M path/to/modified/file.js
M path/to/modified/file.swift
D path/to/deleted/file.swift
R path/to/old/file.swift -> path/to/new/file.swift
?? path/to/non/tracked/file.swift

この出力をawkのパターンマッチの力でパース!

  • /^D/ {next} 削除済のファイルを整形出来ないので Dで始まるものをスキップし、次の行へ
  • !/\.swift$/ {next} .swiftで終わる行をマッチしないものをスキップし、次の行へ
  • /^R|^C/ {print $4; next} 名前変更されたRや競合されたCで始まる場合は 4番目のトークンをプリントし、次の行へ
  • {print $2} 上記の条件全て一致しなかったので2番目のトークンをプリント

以上!

誰かに役に立つといい

See you later! 🐊

3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0