SwiftUIはWWDC20で大きく更新された。アプリ全体をSwiftUIで作る方法が確立されたほか、新しい抽象がいくつも導入されている。どれも非常に興味深いが、本記事ではその中からActionパターンを見出し、紹介する。
Actionの導入
WWDC20ではSwiftUIに、以下の4つの「Action」が導入された。
また対応するプロパティがEnvironmentValuesに追加されている。
ドキュメントに使い方が載っている。
struct SupportView : View { @Environment(\.openURL) var openURL var body: some View { Button(action: contactSupport) { Text("Email Support") Image(systemName: "envelope.open") } } func contactSupport() { openURL(mailToSupport) } }
@Environment(\.openURL) var openURL
としてEnvironmentから取得しているのが、OpenURLAction
構造体の値である。これをcontactSupport()
関数の中で、openURL(mailToSupport)
という風に呼び出している。
構造体の値を関数のように呼び出しているが、これは「SE-253 Callable values of user-defined nominal types」で議論され、Swift 5.2で導入された機能だ。このような値をcallable valueという。callAsFunction
メソッドを持っている構造体の値やクラスのインスタンスは、関数のように呼び出すことができる。関数のシグネチャはcallAsFunction
メソッドのシグネチャと一致する。またcallAsFunction
メソッドはオーバーロードできる。
Actionという抽象
4つのActionが、「Action」と名付けられていることには意味がある。Actionの特徴は、以下のようになる。
- Actionの名前はAction接尾語を持つ(
XxxAction
のように) - Actionの名前は動詞と目的語で構成される
- Actionはcallable valueである(
callAsFunction
メソッドを持っている) - ActionはEnvironment経由で取得できる
- Actionを表す
EnvironmentValues
のプロパティはActionの名前からAction接尾語を除いたものになる(OpenURLAction
はopenURL
になる) - (おそらく)Actionは値型である
最後だけは「おそらく」と書いたが、Actionを参照型で作って状態を共有するようなケースはおおよそ考えられないし、またそうあるべきではないと思われるから、値型(つまり構造体など)で作るのが普通だろう。
これらの特徴を備えたActionは必然的に、コンテキストの親(祖先)が提供する機能を、子(子孫)から呼び出す、というユースケースになる。
独自のActionを作る
Actionとして抽象化できそうな処理があれば、独自のActionを作ることができる。
Actionとなる構造体を作るのは簡単だ。
struct PrintStringAction { func callAsFunction(_ string: String) { print(string) } }
これをEnvironmentに入れるには、以下のようにKeyを作って、EnvironmentValues
を拡張する。
struct PrintStringActionKey: EnvironmentKey { static var defaultValue: PrintStringAction { return PrintStringAction() } } extension EnvironmentValues { var printString: PrintStringAction { get { return self[PrintStringActionKey.self] } set { self[PrintStringActionKey.self] = newValue } } }
これだけで、@Environment(\.printString) var printString
として利用できる。
(この例はActionとして抽象化するのが適切ではない……。)
SwiftUIにおけるAction
「Action」という語は、伝統的なCocoaにおいてはTarget-Actionパターンに登場し、さらにUIKitにはUIActionが登場している。あるいはFluxでも使われる。このように、文脈に依存して意味が少しずつ異なるが、SwiftUIではここまで説明したようなものが「Action」パターンである。
ということで、SwiftUIにおけるActionパターンを見出した。パターンというのは、再利用可能な設計のプラクティスである。
Actionパターンは、SwiftUIにおいて必要不可欠な抽象化になっていくだろう。