ストリーミングはほとんどのブラウザと
Developerアプリで視聴できます。
-
SwiftUIにおけるObservationの説明
ObservationでSwiftUIのデータモデルを簡素化します。Observableマクロがどのようにモデルを簡素化し、アプリのパフォーマンスを向上させるかを紹介します。Observationを知り、マクロの基礎を学び、ObservableObjectからObservableへの移行方法を見つけます。
関連する章
- 1:03 - What is Observation?
- 4:23 - SwiftUI property wrappers
- 7:34 - Advanced uses
- 10:27 - ObservableObject
リソース
関連ビデオ
WWDC23
-
ダウンロード
♪ ♪
こんにちは Philippeです Swiftの不思議な新機能を紹介できて 本当に嬉しいです Observation この機能により 標準的なSwiftの 構文を使用してモデルを定義し そのモデルへの変更にUIが応答するように それらの型を使用できます これにより SwiftUIでの開発は シームレスで直感的なものになります 今日はいくつかのトピックを取り上げます Observationが何であるかの概要 SwiftUIからのプロパティラッパーを いつ使うかについての便利な規則 そしてobservableのより高度な使い方を いくつか取り上げます そして @ObservableObjectを使っていた コードを新しいObservableマクロに 更新する方法の例をいくつか紹介します
Observationは プロパティの変更を 追跡するためのSwiftの新機能です 通常のSwiftの型を使って動作し マクロのマジックでそれらを変換します 私たちはよくデータモデル型を書きますが それらは最終的にSwiftUIで使いたい 多くのプロパティを持っています @Observableを追加するだけで データモデルの変更に UIが反応するようになると言ったら どうでしょう? Swift 5.9の新機能により これまで以上に シンプルなモデルを作成できます これはSwiftの新しいマクロシステムを 使っていいます "@Observable" は Swiftコンパイラに あなたが書いたものから 型を観察できる拡充形に コードを変換するように指示します SwiftUIのビューを動かすために observable型を使用できます 驚くべき点は 動作にプロパティーラッパーの ようなものを必要としないことです ドーナツ屋台アプリのおいしそうな サンプルがあるので さっそく見てみましょう ここではドーナツをシンプルに 表示しています SwiftUIはボディコールを実行するときに モデルが特定のプロパティに アクセスすることを知っています この場合 ドーナツメニュービューの ボディを実行するときに プロパティ'donuts'にアクセスしたことを 検出することができます ボディが実行されるとき SwiftUIは 'Observable'型から使用される プロパティへのすべてのアクセスを 追跡します 次に その追跡情報を取得し それを使用して 特定のインスタンスの プロパティに対する次の変更が いつ変更されるかを判断します ここで ドーナツ追加ボタンをクリックして ドーナツ配列を変更すると ドーナツメニュービューが無効になり それに応じてUIも更新されます 素晴らしいのは たとえば注文が追加された場合 そのプロパティはビューのボディを 実行するときに決定された トラッキングされた プロパティの一部ではないので ビューが無効にならないことです 次に 計算プロパティを使うと どうなるかを説明します 計算プロパティの追加は 前と同じルールに従います 使用されているプロパティが変更されると UIが更新されます 新しく追加されたコンテンツでは モデルのorderCountが呼び出され 注文プロパティにアクセスします つまり この例では 注文が変更されると orderCountが注文のプロパティに アクセスするため そのテキストが更新されるということです "@Observable"マクロを使用することで Observationをサポートする型が 拡張されます これにより SwiftUIはこれらの プロパティへのアクセスを追跡し 次のプロパティがそのObservationから いつ変更されるかを観察できます このようなトラッキングを行うことで 特定のプロパティが変更されたときにのみ UIがビューのボディを再計算するようになり それにより 本当に素晴らしい パフォーマンスの向上が見られました マクロについて詳しく知りたい場合は 「Write Swift macros」と 「Expand on Swift macros」のセッションを 必ずチェックしてください Observableによって SwiftUIの プロパティラッパーは簡単になりました State environment bindableは SwiftUIで動作する 3つの主要なプロパティラッパーです SwiftUIでobservable型と インターフェイスするために プロパティラッパーが必要ないケースは すでに取り上げましたが 必要なケースを見てみましょう まずは@Stateから始めます ビューの状態をモデルに 格納する必要がある場合は @Stateプロパティを使用します 観察可能なモデルオブジェクトDonutが シートプレゼンテーションで使われています シートが表示されると donutToAdd ステート変数が編集可能フィールドに 値をバインドするために使用されます "donutToAdd"プロパティは それが含まれるビューの ライフタイムによって管理されます 次は@Environmentです Environmentは 値をグローバルに アクセシブルな値として伝播させます これにより 多くの場所で 物事を共有できます Observable型はアクセスに基づいて 更新が作成されるため ここでは非常に効果的です フードトラックメニュービューの ボディを呼び出すと アカウントオブジェクトの userNameプロパティにアクセスします そのため userNameが変更されると メニュービューが更新されます プロパティラッパーファミリーの中で 最も新しいものは'@Bindable'です バインド可能なプロパティラッパーは 実に軽量です その型からバインディングを 作成できるようにすることだけです バインディング可能なラッププロパティから バインディングを取り出すのは実に簡単です そのプロパティへのバインディングを 取得するには $構文を使用するだけです 多くの場合 これは観測可能な型への バインディングになります ドーナツビューでは 名前をテキストで表示しています しかし実際には その名前を 編集できるようにしたいです そこで Textの代わりに TextFieldを使うことができます そのTextFieldはバインディングを取ります TextFieldの値を入力するために バインディングから読み込みますが ユーザーが値を変更すると バインディングに書き戻します ドーナツにバインドするために必要なのは ドーナツプロパティに '@Bindable'プロパティラッパーを 使うことだけです プロパティラッパーアノテーションを 使用すると'donut.name'構文を使用でき 使用時にバインディングが作成されます ラッパーをまとめるとSwiftUIで observableモデルを使うために 答える必要がある質問は3つだけです このモデルはビュー自体の状態である 必要がありますか? その場合は'@State'を使用します このモデルはアプリケーションのグローバル 環境の一部である必要がありますか? その場合は'@Environment'を使用します このモデルに必要なのは バインディングだけですか? その場合は新しい'@Bindable'を使用します これらの質問のどれにも答えが イエスでない場合は モデルをビューのプロパティとして 使用するだけです これまで モデル内で保存された状態から 始まるプロパティについて説明しました Observableはさらに多くのことが可能です SwiftUIはインスタンスごとに フィールドへのアクセスを追跡するので 配列 オプション またはそれに関しては 観測可能なモデルを含む任意の型を 使用できることを意味します ドーナツリストビューは ドーナツモデルの配列を持っています 各モデル自体は'@Observable'です これらのドーナツの名前のいずれかが 変更されたとき SwiftUIは特定のインスタンスで そのプロパティへのアクセスを検出し ビューを無効にするタイミングを知るために それを追跡します そのため ここでランダム化ボタンによって ドーナツの名前が変更されると それに応じてビューも更新されます これによって 好きなようにモデルを 作ることができます 観測されるモデルの配列を持つこともでき また 他の観測可能なモデル型を含む モデル型を持つこともできます 一般的なルールとして Observableの場合 使用されているプロパティが変更されると ビューが更新されます そのルールが完全に適用されないケースが あります 計算プロパティが それを構成する 保存プロパティを持たない場合 Observationで動作させるために 2つの特別な手順を踏む必要があります これは 観測されるプロパティが 観測可能な型に保存プロパティの ある種のcompositionによって 変更されない場合にのみ 行われる必要があります この場合 プロパティが アクセスされたときと プロパティが変更されたときに Observationに伝えるだけです これは Observationが通常 プロパティへのアクセスを組み込む方法ですが ここでは 監視できない場所を読み取って 名前を保存できるように カスタムアクセスポイントを手動で 書き換えています ほとんどの場合 このような 手動ケースは必要ありません なぜなら 問題のモデルのプロパティは 他の保存プロパティから 構成されているからです しかし そのような高度な能力を 必要とするまれなケースにおいて Observationは十分に柔軟でありながら 自分自身で行うには十分簡単です SwiftUIはそれらのプロパティへの アクセスによって観測可能な型を 追跡するので 構成の変更を 識別することができます つまり 計算プロパティが 他の保存プロパティから構成されている場合 Observationはそのまま機能します しかし そうでない少数のケースでは Observationを直接使用し アクセスや変化にフラグを付ける 呼び出しを手動で追加できます 以前のFood Truckアプリでは ObservableObject を使用して 新しい@Observableマクロと 同じことを実現しました 現在SwiftUIを使用している アプリを持っている場合 非常に似た状況にあるかもしれません Observableマクロはコードを単純化し パフォーマンスも向上させることができます 変更前 FoodTruckModel型は ObservableObject適合性を持っており @Publishedプロパティラッパーで マークされたプロパティを いくつか持っていました @Observableマクロへの変更は とても簡単でした 必要なのは ObservableObjectへの 適合性を削除し '@Published'を削除し '@Observable'マクロでマークするだけです ビューに関しては '@ObservedObject'と '@EnvironmentObject'プロパティラッパーが 多数ありました '@ObservedObject'ラッパーの すべてのケースで バインディングがなくなるか バインディングだけが必要になり 新しい'@Bindable'に変更されました '@EnvironmentObject' ラッパーは '@Environment'だけになりました ObservableObjectから新しい '@Observable'マクロへの変更は ほとんどアノテーションを削除するだけです あるいは @State @Environment @Bindableの 3つの主要なプロパティラッパーに 単純化することもできます これにより 考慮すべきオプションが 少なくなるため 新機能の作成が容易になります Observationにはちょうどいいレベルの マジックがあります @Observableマクロを使用することで 簡単に開始でき データモデルを 直接操作できます 必要に応じて 高度なユースケース用の マニュアルバージョンを 書くことができます 新規開発の場合 Observableを使用するのが 最も簡単な方法です また 既存のアプリケーションでは Observableの使用で モデルを簡素化し 新しい機能を追加する際の パフォーマンスを向上できます ぜひ試してみて マジックを活用してみることを おすすめします ♪ ♪
-
-
1:26 - Using @Observable
@Observable class FoodTruckModel { var orders: [Order] = [] var donuts = Donut.all }
-
2:12 - SwiftUI property tracking
@Observable class FoodTruckModel { var orders: [Order] = [] var donuts = Donut.all } struct DonutMenu: View { let model: FoodTruckModel var body: some View { List { Section("Donuts") { ForEach(model.donuts) { donut in Text(donut.name) } Button("Add new donut") { model.addDonut() } } } } }
-
3:12 - SwiftUI computed property tracking
@Observable class FoodTruckModel { var orders: [Order] = [] var donuts = Donut.all var orderCount: Int { orders.count } } struct DonutMenu: View { let model: FoodTruckModel var body: some View { List { Section("Donuts") { ForEach(model.donuts) { donut in Text(donut.name) } Button("Add new donut") { model.addDonut() } } Section("Orders") { LabeledContent("Count", value: "\(model.orderCount)") } } } }
-
4:41 - Using @State
struct DonutListView: View { var donutList: DonutList @State private var donutToAdd: Donut? var body: some View { List(donutList.donuts) { DonutView(donut: $0) } Button("Add Donut") { donutToAdd = Donut() } .sheet(item: $donutToAdd) { TextField("Name", text: $donutToAdd.name) Button("Save") { donutList.donuts.append(donutToAdd) donutToAdd = nil } Button("Cancel") { donutToAdd = nil } } } }
-
5:14 - Using @Environment
@Observable class Account { var userName: String? } struct FoodTruckMenuView : View { @Environment(Account.self) var account var body: some View { if let name = account.userName { HStack { Text(name); Button("Log out") { account.logOut() } } } else { Button("Login") { account.showLogin() } } } }
-
6:27 - Using @Bindable
@Observable class Donut { var name: String } struct DonutView: View { @Bindable var donut: Donut var body: some View { TextField("Name", text: $donut.name) } }
-
7:53 - Storing @Observable types in Array
@Observable class Donut { var name: String } struct DonutList: View { var donuts: [Donut] var body: some View { List(donuts) { donut in HStack { Text(donut.name) Spacer() Button("Randomize") { donut.name = randomName() } } } } }
-
9:18 - Manual Observation
@Observable class Donut { var name: String { get { access(keyPath: \.name) return someNonObservableLocation.name } set { withMutation(keyPath: \.name) { someNonObservableLocation.name = newValue } } } }
-
-
特定のトピックをお探しの場合は、上にトピックを入力すると、関連するトピックにすばやく移動できます。
クエリの送信中にエラーが発生しました。インターネット接続を確認して、もう一度お試しください。