FastlaneがSwiftで書けるようになった〜
これはSwiftアドベントカレンダーの17日目の記事です。
Swiftの方はプラットフォームに依存しないエントリーを書くべきかと思いましたが、
FastlaneのSwift対応がタイムリーだったのでこっちにしました。
元はSwiftでTCPソケット通信を書こうと思ってたので、年末にでも。それでは本題へ
今回は下記のプロジェクトを元に紹介していきます。
https://github.com/matsuokah/fastlane-swift-samplegithub.com
Fastfileの設定ファイルやその周辺がSwiftで書けるようになりました
fastlaneがもともとRubyなのは周知の事実ですが、rubyの実装をSwiftから叩く実装が2.69.0から入りました。
Swift対応の実装方針としてはブリッジ、フック、そしてlane定義に分かれています。
ブリッジ: Rubyのコマンドをコールするラッパーを自動生成
フック: ブリッジファイルをコールしたり、エントリーポイントとなるコード
lane定義: コマンド(ブリッジ部分)を叩いたり、ビルドの設定や環境変数を定義している箇所
そしてこれらが、xcodeプロジェクトで管理できるようになっています。
今回の一番のポイントは「慣れたエディタで慣れた言語をつかえる」。これだけで効率化のイメージが湧きますよね
早速使ってみる
fastlane init swift
でfastlane関連のSwiftのファイルが生成されます。
To edit your new Fastfile.swif type: open ./fastlane/swift/FastlaneSwiftRunner/FastlaneSwiftRunner.xcodeproj
というメッセージが出ています。
生成されたプロジェクトの開いて、グループの構成をみると下記のようになっていて、基本的にはFastfile.swift
だけを編集すればOK
* Autogenerated API => いわゆるRubyのブリッジ部分 * Fastfile Components * Networking => 文字通り * RunnerCode => フック部分 * Appfile.swift, Fastfile.swift => 設定、ビルドフローの定義ファイル * 各コマンド(Gymfile.swifなど)
このプロジェクトをビルドするとMacでexecutableなバイナリが出来上がります。
生成されたバイナリを介してfastlaneの部分が実行されています。また、fastlaneコマンドをつかって起動した場合、バイナリ自体のビルドもよしなに走るようになっています。
Fastfile
を編集してビルドする
Fastfile
クラスはLaneFile
クラスを継承しています。LaneFile
には各実装が織り込まれているのでビルドフローを詳しく知りたい人は読んでみるといいと思います。
つけるメソッド名はdebugLane
のように接尾辞を付ける必要があります。理由はlane
を接尾辞にもつメソッドをフィルタしてlaneを見つけ出してフックしているためです
class Fastfile: LaneFile { var fastlaneVersion: String { return "2.69.3" } func debugLane() { buildApp() crashlytics(apiToken: "TOKEN", buildSecret: "SECRET") } }
最低限の実装はこれだけで済むはずです。
ここからさらに ConfigurationやExport Methodなど、enumを定義してそれらを扱うクラスを用意すれば省コード化が可能になります。
enum Configuration: String { case debug case release var exportMethod: ExportMethod { switch self { case .release: return .appStore default: return .development } } }
上記はConfigurationの列挙ですが、プロジェクトによってはstagingや準本番のような環境もあるかと思います。
それらの環境変数を洗い出したあとは変数のマネージャクラスを用意すればOK
すべて記載すると長いので、Protocolだけ記載しておきます
protocol BuildContextProtocol { // Xcode var workspace: String { get } var scheme: String { get } var configuration: String { get } // Build var buildDir: String { get } var ipaName: String { get } var ipaPath: String { get } var dsymName: String { get } var dsymPath: String { get } var exportMethod: String { get } }
最終的には下記のようなコードに収まります。
func debugLane() { desc("Submit a new Beta Build to Crashlytics") let buildContext = BuildContext() buildContext.build() crashlytics(apiToken: "TOKEN" , buildSecret: "SECRET" , ipaPath: buildContext.ipaPath , groups: Fabric.testerGroup, notifications: true ) }
各環境変数・コンフィグの切り替えを省コード化して書いてみたサンプルをおいときます。
https://github.com/matsuokah/fastlane-swift-sample
今回省コード化のために抜き出した設定ファイルです
ハマったこと
- FastlaneRunnerをgit管理下に置こうとして失敗する
- gitignoreに追加しました。また、バイナリなので12MBほどの大きさです。毎度FastlaneのSwift部も勝手にビルドが行われて、バイナリが再度生成されるので追跡しなくていいと思います。
- ルビーでは配列で扱っている部分もすべてStringになっている
- Crashlyticsの
groups
とかがそうなのですが、rubyだと["tester1", "tester2"]
のように配列を指定できますが、SwiftではインターフェースにはString採用されているので使い方に工夫が必要になりそうです。
- Crashlyticsの
- 現状、メソッドにパラメータを渡していない
- 試してはいないのですが、フック部では
_ = fastfileInstance.perform(NSSelectorFromString(laneMethod))
と書かれていることから、引数が使えないのでは?と妄想しています。
- 試してはいないのですが、フック部では
まとめ
ということで、まだまだexperimantalで機能的にまだ満たされていない部分もありますがそこはPRポイントですね!
若干のワークアラウンドが発生しますが、FastfileがSwiftで定義でできるようになったことでグルーコードが非常に書きやすくなったかと思います!
個人的にはRubyが書けるならRubyでいいなと思います。あくまでRuby主導なリポジトリですので、Rubyで直接実行できるにこしたことはないです。