MintでSwiftPM製のCLIツールを管理する

Swift界隈では、Swift Package Manager (SwiftPM) で作られたCLIツールが増えつつあって、それのインストールやバージョン管理をどうしようかという問題がある。一部のツールはHomebrewやCocoaPodsでの配布もしているけど、全ては賄えない。となると各ツールのリポジトリをチェックアウトして$ swift buildしてパス通った箇所にコピーするなどしないといけないけど流石にそれは面倒。

というところで https://github.com/yonaskolb/Mint を使うとその辺の管理が楽になりそう(Mint自体はHomebrewでインストールできる)。

$ mint run yonaskolb/XcodeGen@1.2.4 xcodegen --spec spec.yml # pass some arguments
$ mint install yonaskolb/XcodeGen@1.2.4 --global=false # installs a certain version but not globally
$ mint install yonaskolb/XcodeGen # install newest tag
$ mint run yonaskolb/XcodeGen@1.2.4 # run 1.2.4
$ mint run XcodeGen # use newest tag and find XcodeGen in installed tools

$ mint install/usr/local/binにもリンクしてくれるようなので便利そう。

参考: Swift Package Managerを使って作成したコマンドをMintでインストールする

SwiftでCLIツールを書くためのArgument Parser

SwiftでCLIツールを書く時のArgument Parser(引数やサブコマンドをいい感じに解釈してくれるやつ)の選択肢を書き連ねておきます。これら以外の良い選択肢をご存知の方はぜひ教えてください 🙏

Cirrus CIというCIサービスを知った

(当然)macOSではないのでXcodeは動かせないけど、Swift on LinuxのCIには使えるかもしれない。

Trailing Closureを禁止する

SwiftLint 0.22で追加された multiple_closures_with_trailing_closure というルールをご存知でしょうか。これは次のような複数のクロージャを引数に取るAPIを使用する時に、最後の引数のクロージャTrailing Closureとして渡すことを警告するというルールです。

func fetch(id: Int, success: () -> Void, failure: (Error) -> Void) { ... }

// NG
fetch(
    id: 12345, 
    success: { print("success") }
) { error in
    print("error: \(error)"
}

// OK
fetch(
    id: 12345, 
    success: { print("success") }, 
    failure: { error in print("error: \(error)")}
)

こうしたAPIでは最後のクロージャだけ引数ラベルが落ちてしまうと、各引数の間の対称性がなくなってしまってコードが読みにくくなってしまいそうです。普段よく使うであろうAPIとしては UIView.animate(withDuration:animations:completion:) が挙げられると思います。

// With trailing closure
UIView.animate(
    withDuration: 0.3,
    animations: { ... }
) { finished in
    ...
}

// Without trailing closure
UIView.animate(
    withDuration: 0.3,
    animations: { ... },
    completion: { finished in ... }
)

さて、Lintで利用者側がこうした使用方法を警告・禁止できるとしても、ライブラリの提供者側で禁止することはできないでしょうか。以下に1つのアイデアを示してみようと思います。

func fetch(id: Int, success: () -> Void, failure: (Error) -> Void, _ tcDisabler: () = ()) { ... }

考え方としては単純で、クロージャが最後の引数にならないようにしてあげるだけですね。意味のない値をデフォルト引数として設定しておくことで、利用者サイドは特に気にせずに利用できるはずです。引数名や値に考える余地はありそうですが、他にもいい手段がないか、ご意見お待ちしています。

Xcodeのブレークポイントで音を鳴らす

Xcodeブレークポイントでは音を鳴らすことができて、これは結構便利です。例えばシミュレーターの動作を目で確認する一方で、ブレークポイントを通過したかどうかは耳で確認することができます。シミュレーターの画面を見ながらコンソールのログを追うのは大変ですからね。

また "Automatically continue after evaluating actions" という実行を停止させないオプションと組み合わせるとさらによいですね。ちなみに僕は "Frog" の音が好きです。

f:id:ikesyo:20171218210226p:plain
Sound ActionとAutomatically continue after evaluating actions

これは便利なので仕事中にもたまに使うことがありますが、その時には当然Macのミュートを解除する必要があります。そうすると今度はデバッグの最中にメールやSlackの通知音がオフィスに鳴り響いたりしてちょっとやりづらい。なのでデバッグを始めようとすると、いそいそとイヤホンをMacにつなぎ出すようになっているここ最近です*1

AirPodsでも買ったらいいんでしょうか。

*1:仕事中に音楽は聞かないタイプです

SwiftのSDK overlayとswift-corelibs-foundation

SDK overlay

SwiftのSDK overlayというものを耳にしたことはあるでしょうか?これはSwiftのOSSプロジェクトであるapple/swiftの開発において、FoundationやAppKit、UIKitなどのObjective-Cで書かれたシステムフレームワークに対し、SwiftらしいAPIを提供するための拡張レイヤーです。

https://github.com/apple/swift/tree/master/stdlib/public/SDK

Swift 3で、NSDateに対するDateNSURLに対するURLなど、参照型と対応する複数の値型がFoundationに追加された*1のを覚えている人も多いかと思います。これらの値型はもちろんSwiftで実装されているわけですが、これも実はSDK overlayの一部です(この場合はFoundationのoverlay)。こう聞くと、SDK overlayが少しは身近に感じられないでしょうか。

swift-corelibs-foundation

ここで話は変わります。Swiftの標準ライブラリに次ぐコアライブラリであるFoundationはObjective-Cで実装されているため、そのままではLinuxなどのDarwin以外のプラットフォームでは使用できません。これではせっかくSwift自体がLinuxに対応しても、種々の機能不足に悩まされたり、クロスプラットフォームのライブラリの開発に支障をきたしてしまいます。

この状態を解消するために、SwiftのOSS化と共に公開されたのがswift-corelibs-foundationです。このリポジトリは、FoundationのベースとなっているCoreFoundationというC実装のフレームワークを使い、FoundationをSwiftで再実装するというものです。

Foundation overlayとswift-corelibs-foundationの同期

さて、Foundationの再実装と聞いて気になった人はいないでしょうか?Objective-C版のFoundationへの追加レイヤーであったoverlayはどうなるのでしょうか。答えは、Foundation overlayはswift-corelibs-foundationの側でも実装される必要があるのです*2

こうして、apple/swiftとswift-corelibs-foundationの間でFoundation overlayの実装を同期する必要が出てきます。例えばNSStringAPI.swiftのコメントに書かれているように、一方を更新したらもう一方を更新するのを忘れないようにしなければなりません。実際、一方にプルリクエストを出すと、もう一方にもプルリクエストを出してくれとレビュワーからも依頼されることがあります。

とはいえ人間忘れてしまうもので、どうしても内容の乖離は発生しています。しかしこれは見方を変えればコントリビューションチャンスですね!?

2つのリポジトリ間での差分を見つけて、より新しい方の内容をもう一方に反映することは、SwiftのOSSプロジェクトに参加する一歩目としては比較的とっかかりやすいと思います。Foundationに興味があるあなた、まずはここから始めてみませんか?

おわりに

この記事を機に、Swift開発するだけでなく、Swift開発することに興味を持つ、取り組んでくれる人が増えれば幸いです。数少ない日本のSwiftコミッターからは以上です。


この記事ははてなエンジニア Advent Calendar 2017の9日目の記事です。

昨日8日目はid:aerealさんの大コンテナ時代を生きのこるためのJSON Schemaでした。

明日10日目はid:nanto_viさんの担当です!

*1:SE-0069: Mutability and Foundation Value Types

*2:後者においては実装の一部となるため、overlayとは呼ばないのですが。

『iOS 11 Programming』の執筆に参加しました&一般販売開始

技術書クラウドファンディングPEAKSのプロジェクトとして成立した『iOS 11 Programming』に、執筆陣の一人として参加させていただき、先日完成を迎えることができました。また本日からクラウドファンディング参加者に限らない一般販売が開始されました。

iOS 11 Programming

iOS 11 Programming

  • 著者:堤 修一,吉田 悠一,池田 翔,坂田 晃一,加藤 尋樹,川邉 雄介,岸川克己,所 友太,永野 哲久,加藤 寛人,
  • 発行日:2017年11月16日
  • 対応フォーマット:製本版,PDF
  • PEAKSで購入する

個人的には昨年の『WEB+DB PRESS Vol.96』の特集に続き、二度目の執筆活動となりました。この時は特集約30ページを一人で書き上げるのにかなり苦労した思い出があります。今回は一冊の技術書ですが、1・2章程度の粒度で分担して書くのは、単著を書くよりもかなりハードルが低く、また他の著者陣が書き上げていくのに刺激されて自分もやらなければ!という気持ちにさせてくれるので、とてもいいやり方だなぁと感じました。

これを読んだ皆さんも応募フォームから書いてみたい、読んでみたい、といったリクエストをどんどん投げつけてみましょう!

第4章を書きました

僕は「第4章 Swift 4の新機能とアップデート」を担当しました。Codable では実装の自動生成が大きなポイントの1つですが、仮にもSwiftコミッターなので、コンパイラーの自動生成部分のコードも読み込みながら挙動を把握し、丁寧に解説しようとしました。自動生成と手動実装を下手に混ぜると危ういといった注意点も書きたかったことの1つですね。

またSmart KeyPathsについては、基本的な使い方はそんなに困らないだろうという前提で、より掘り下げたクラス階層やジェネリクス的な視点、read-onlyかread-writeか、といった点に着目しています。

すでにSwift 4に移行済みの人も少なくないとは思いますが、これらの機能はSwift 3.2モードでも使用可能です。なのでSwift 4に移行前でもどんどん使っていって、皆で色んな知見を溜めていけるといいですね。

あわせて読みたい

どうぞお買い求めください 🙏