Introduction
WWDC19でSwiftUIが発表されてiOS開発者界隈は騒然となりました。11年あるiOSの歴史のなかでSwiftの発表に次ぐ大きなインパクトのあるリリースだと言っても過言ではないでしょう。内容としては、Flutter, Kotlin Jetpack Composeに次ぐReact-likeなView構成フレームワークなのですが、iOS開発が大幅に効率化することは間違いありません。Flutterの導入なども本格的に検討していたので、本格投入する前に発表があって正直ほっとしています。
不思議構文
さて、発表を見て、よく訓練されたiOSエンジニアの方はひと目で気がついたかも知れませんが、スライドに写ったVStackを作るところで不思議構文が登場します。
VStack(alignment: .leading) {
Text(item.title)
Text(item.subtitle)
.color(.gray)
}
SwiftのClosureは別のthisコンテキストを持ったり(*1)、複数の返り値をもつことがない(*2)のにもかかわらず、VStackに渡されたClosureが期待通りに解釈されて、VStack<TupleView<(Text, Text)>>
の値を返しています。
ひょっとすると実行時の評価だけやっているのかなと思って、試しに3
を挿入してみると、
VStack(alignment: .leading) {
3
Text(item.title)
Text(item.subtitle)
.color(.gray)
}
きちんとコンパイルエラーになってくれます。不思議ですね。
TL;DR
VStackのinitializerの引数で ViewBuilder
というCustom Attributeを用いていて、
VStack(alignment: .leading) {
Text(item.title)
Text(item.subtitle).color(.gray)
}
上のコードは、以下のように変換されます。
struct ContentView : View {
var body: some View {
VStack(alignment: .leading) {
ViewBuilder.buildBlock(
Text(item.title),
Text(item.subtitle).color(.gray)
)
}
}
}
この、ViewBuilderは非公式の機能であるFunction Builderを用いて実現されていて、我々もこの機能を使うことができます。実際にやってみましょう。
Usage of FunctionBuilder
作るもの
blockAdd {
3
4
}
で3 + 4が実行されて、7が返ってくる魔法関数を作ってみます。
CustomAttributeを定義してみる
これで、CustomAttributeが定義できたことになります。SwiftUIではViewBuilderに対応する部分です。
@_functionBuilder public struct BlockAdder {
public static func buildBlock(_ a: Int) -> Int {
return a
}
public static func buildBlock(_ a: Int, _ b: Int) -> Int {
return a + b
}
public static func buildBlock(_ a: Int, _ b: Int, _ c: Int) -> Int {
return a + b + c
}
public static func buildBlock(_ a: Int, _ b: Int, _ c: Int, _ d: Int) -> Int {
return a + b + c + d
}
}
関数を定義
SwiftUIのソースコードでは、VStackのinitializerに対応しています。
func blockAdd(@BlockAdder block: () -> Int) -> Int {
return block()
}
使ってみる
let one = blockAdd {
1
} // 1
let three = blockAdd {
1 // SwiftUIのサンプルではTextに対応するところ
2
} // 3 (1 + 2)
let six = blockAdd { 1; 2; 3 } // 6
let ten = blockAdd { 1; 2; 3; 4 } // 10 (1 + 2 + 3 + 4)
望み通りの結果が得られました。
感想
私の知る限りでは、Swiftで自前でAttirbuteを実装できたのはこれが初めてなのでちょっと感慨深いです。
自由自在にAttributeを作れる機能についても議論されているので、SwiftでもRetrofitのように便利な(easyな)ライブラリが生まれる日が近いのかも知れません。
https://forums.swift.org/t/pitch-introduce-custom-attributes/21335
最後に、フランスの詩人ラ・フォンティーヌが書いた『寓話』の『裁判官と修道士と隠者』にある言葉を引用して筆を置きたいと思います。
すべての道はJavaに通ず - All roads lead to Java.
バージョン情報
Apple Swift version 5.1 (swiftlang-1100.0.38.29 clang-1100.0.20.14)
Target: x86_64-apple-darwin18.6.0
参考
apple/swiftのFunctionBuilderに関するPull Request
https://github.com/apple/swift/pull/25221
ForumのFunctionBuilderに関するdiscussion
https://forums.swift.org/t/pitch-function-builders/25167/10
ViewBuilderのドキュメント
https://developer.apple.com/documentation/swiftui/viewbuilder
故事ことわざ辞典
http://kotowaza-allguide.com/su/subetenomichi.html
注意
画像はこちら(https://developer.apple.com/xcode/swiftui/ )からの転載です。Apple Inc.かIncrements Inc.の方は twitter: _kentino にご連絡ください。苦情・マサカリも上記まで。