Build PhasesのRunScriptでJsonをVaridateする

ローカルフォルダ内に存在するJsonが破損している場合起動時のクラッシュではなく、そもそもビルドを失敗させたいと思い、RunScriptでSwiftを使ってVaridate試してみました。

.shファイルの追加

Run Scriptの導入はいろんな方が記事を書いてくれていますが、私はXCodeのRun Scriptを試しに触ってみるという記事を参考に入れました。本記事ではRunScriptの作成までの過程は飛ばします。

RunScriptに用意されているエディタは小さいのでScriptファイルを作成しそのファイルに記述を行います。

File > New > File > Other > Shell Script

Run Scriptとのパスを通す

作成したShell ScriptファイルをRunScriptで動かすためにPathを通します。

私の場合はプロジェクト直下にScriptファイルを作成したので下記のようになりました。

./Script.sh

パスが通っているかの確認

作成したShellScriptの中身を下記のように書き換えてビルドしhelloと出力されていれば成功です!

#!/bin/sh

echo "hello"

Shell ScriptでSwiftを使う

Swiftの呼び出し

ここからが本題です。以下のようなコマンドでShell ScriptからSwiftを使用することができます。

echo "print(\" Hello Swift!!!\ ")" | swift 

これだと1行だけしか使えないため下記のように記述することでswift_code()内のブロックでSwiftを使えるようになります。

#!/bin/bash
swift_code() {
cat<<EOF
 
    print("Hello Swift!!!")
EOF
}
echo "$(swift_code)" | swift -

フレームワークを使う

echoの部分に下記のように書き換えることでフレームワークを使うことができるようになります。試していませんがCarthageでいれたライブラリも使えるみたいです。

echo "$(swift_code)" | DEVELOPER_DIR = "$ DEVELOPER_DIR" xcrun --sdk macosx "$ TOOLCHAIN_DIR / usr / bin /" swift -F "$ SRCROOT / Carthage / Build / Mac /"  - 

Jsonのparseを行うRun Script

はじめにBundleを使ってPathを取得しようとしていたのですが全然取れずにハマっていましたが、FileManagerを使ったらすんなり取れました。

#!/bin/sh
swift_code() {
cat<<EOF
    import Foundation
    struct Test: Codable {
        var message: String
    }
    let fileManager = FileManager.default
    let path = fileManager.currentDirectoryPath
    print(path)
    let content = try String(contentsOfFile: "\(path)/RunScriptTest/hoge.json")
    let data = try JSONDecoder().decode(Test.self, from: content.data(using: .utf8)!)
    print(data.message)
EOF
}
# Run the Swift code in swift_code()
echo "$(swift_code)" \
| DEVELOPER_DIR="$DEVELOPER_DIR" \
xcrun --sdk macosx \
"$TOOLCHAIN_DIR/usr/bin/"swift -F "$SRCROOT/Carthage/Build/Mac/" -

プロジェクト内のSwiftファイルを使う

上記のコードでvaridateをすることは可能ですが、Json構造が変更された際RunScriptも修正が必要になってしまう為、既存のSwiftファイルを使用するのがベストだと思います。

また、Varidateを行っている部分も別ファイルに移植してすっきりさせました。

修正後のRunScript

#!/bin/sh

swift_code() {
PARSE_STRUCT=./RunScriptTest/Parse.swift
VARIDATE_JSON=./VaridateJson.swift

cat ${PARSE_STRUCT} ${VARIDATE_JSON} 
}
# Run the Swift code in swift_code()
echo "$(swift_code)" \
| DEVELOPER_DIR="$DEVELOPER_DIR" \
xcrun --sdk macosx \
"$TOOLCHAIN_DIR/usr/bin/"swift -F "$SRCROOT/Carthage/Build/Mac/" -

Parse用の構造体

ScriptのPARSE_STRUCT=./RunScriptTest/Parse.swiftでパスを取得していた部分のコードです

struct Parse: Codable {
    var message: String
}

Varidateを行う部分のコード

ScriptのVARIDATE_JSON=./VaridateJson.swiftでパスを取得している部分のコードです。
SwiftのコードなのでSwiftファイルで作成したのですが、Swiftファイルは記述をすると勝手にビルドが走ってしまいエラーが出てしまう為、そのままではコンパイラがFileManagerなどがわからずエラーになってしまいます。

import Foundation

let fileManager = FileManager.default
let path = fileManager.currentDirectoryPath
print(path)

let content = try String(contentsOfFile: "\(path)/RunScriptTest/hoge.json")
let data = try JSONDecoder().decode(Parse.self, from: content.data(using: .utf8)!)
print(data.message)

正しい対応かはわかりませんが、右端のタブのIdentity and Type > Typeの項目をSwiftからShell Scriptにすることでエラーが出なくなります。


初めから拡張子をShell Scriptのものにして作成すればいい話なのですが、Xcodeのファイルツリー構造を眺めた際、Swiftで記述がされているのにSwiftファイルじゃないと気持ち悪いと感じたのでこのような対応をしました。

Xcodeのファイル構造

遭遇したエラー

本番環境でRunScriptを導入した際ScriptName.sh: Permission deniedというエラーが発生しました。

調べてみるとこのエラーはファイル権限によるものらしく
Unity開発:XCodeでビルド出来ないという記事が参考になりました。

参考文献

XCodeのRun Scriptを試しに触ってみる

Use Swift in your Run Script phase

ヒアドキュメントの変数エスケープ

Unity開発:XCodeでビルド出来ない