SwiftUIにはDynamicPropertyというprotocolがある。
これを使ってみようという趣旨の記事を見かけた。
ので、私も試してみました。
@Now
import Combine import SwiftUI class Clock: ObservableObject { @Published private(set) var date: Date = Date() init() { Timer.publish(every: 1, on: .main, in: .default) .autoconnect() .assign(to: &$date) } } @propertyWrapper struct Now: DynamicProperty { @StateObject private var clock = Clock() var wrappedValue: Date { get { clock.date } } }
こういうのを作っておいて
import SwiftUI struct ContentView: View { static let dateFormatter: DateFormatter = { let formatter = DateFormatter() formatter.dateStyle = .none formatter.timeStyle = .long return formatter }() @Now private var date: Date var body: some View { Text(date, formatter: Self.dateFormatter) } }
こう使う。
これでContentViewは毎秒更新され、現在時刻を表示し続ける。
DynamicPropertyって何
ドキュメントには、Viewが再計算される前にDynamicPropertyのupdateメソッドを呼んでくれるくらいの情報しかないが、実際にはもうちょっといろいろあるようだ。
上記の例でもわかるように、@ObservedObjectの更新によってViewが再描画される。
ここで急に、SwiftUIのCore Dataサポートのように、@FetchRequest的な形で何かできるんじゃないか、と気づくと思う。
実際にRealmにはそういう機能があった。
現実のユースケース
DynamicPropertyでは、StateなりStateObject(またはObservedObject)なり、あるいはEnvironment(やEnvironmentObject)を、Viewのpropertyで使えるようだ。
これは少しReact Hooksに似ている。ReactのuseStateとSwiftUIの@Stateの対称性のように、ReactのCustom Hookとちょっと似ている。
とはいえ、@Nowの例は、単にObservableObjectをそのまま使えばいいわけで、独自のDynamicPropertyを作ることが正当化されるような場面は稀かも。