WWDC Quick Look 💓 By SwiftGGTeam
Data Essentials in SwiftUI

Data Essentials in SwiftUI

元の動画を見る

ハイライト

SwiftUI のデータ フローはその中心的な設計コンセプトであり、初心者が最も混乱しやすい場所でもあります。セッション 「データ依存関係グラフ」の概念に基づいて、システムはすべてのデータ フロー関連の属性ラッパーを分類します。


主要内容

View SwiftUI の構造は存続期間が短いです。上位レベルの状態が変化するたびに、SwiftUI はビュー構造体を再作成し、それを使用して新しいインターフェイスを生成することがあります。開発者がデータ ライフ サイクルをビュー構造体のライフ サイクルとして理解している場合、更新、ナビゲーション、マルチウィンドウ、および非同期読み込み中に状態の損失が発生することになります。

このスピーチでは、このビューにはどのようなデータが必要なのか、データは変更されるのか、データはどこから来たのかという固定的な質問が行われます。上位層から渡されたデータのみを表示する場合は、通常の属性を使用してください。使用 @State 現在のビューに一時的な状態がある場合。サブビューが親ビューの同じ状態を変更する必要がある場合は、次を使用します。 @Binding

データがモデル層に入ると、 @State 十分ではありません。この講義では、読書アプリを使用して説明します。読書の進行状況、完了ステータス、表紙の読み込み、グローバル ブック リストには異なるライフ サイクルがあります。 SwiftUI はこれらのライフサイクルを次のように分離します。 ObservableObject@ObservedObject@StateObject@EnvironmentObject@SceneStorage そして @AppStorageこれにより、インターフェイスは手動でイベントをリッスンするのではなく、データに依存できるようになります。

最後の段落ではパフォーマンスに焦点を当てます。 body インターフェイスのみを記述する必要があり、高価なオブジェクトを作成したり、その中でブロッキング作業を行ったりすることはできません。新しく導入されたのは、 @StateObject 繰り返し作成するというよくある間違いを解決します。 ObservableObject ビューの初期化パスに問題があるため、更新が遅くなり、データがリセットされます。


詳細

@State 現在のビューが所有する一時的な状態を保存します

(05:09)

講義は読書の進捗状況を更新するシートから始まります。 BookView シートが表示されているかどうか、ユーザーのメモ、現在の進行状況を保存する必要があります。これらのフィールドは一緒に変化するため、Apple はそれらを 1 つの値の型に収集します。 EditorConfig そしてそれらをに引き渡します @State 管理用。

struct EditorConfig {
    var isEditorPresented = false
    var note = ""
    var progress: Double = 0
    mutating func present(initialProgress: Double) {
        progress = initialProgress
        note = ""
        isEditorPresented = true
    }
}
struct BookView: View {
    @State private var editorConfig = EditorConfig()
    func presentEditor() { editorConfig.present() }
    var body: some View {

        Button(action: presentEditor) {  }

    }
}

キーポイント:

  • EditorConfig 3 つの関連フィールドがまとめられるため、進行状況、メモ、表示ステータスが複数のプロパティに分散されません。
  • present(initialProgress:) です mutating メソッド。値自体に対して更新が行われることを示します。
  • @State SwiftUI にこのストレージを管理するように指示します。ビュー構造体が再作成されると、SwiftUI は既存の状態に再接続します。
  • presentEditor() リクエストのみ EditorConfig 状態を更新し、状態の変化に応じて SwiftUI によってインターフェイスの更新が完了します。

@Binding 変更のために同じ状態をサブビューに渡します

(07:22)

ProgressEditor を変更する必要があります EditorConfigBookView。値を直接渡すと、サブビューはコピーにのみ変更されます。サブビューが独自の宣言を行っている場合 @State、2番目の真実の情報源が生成されます。の機能 @Binding 親ビューステートの読み取りおよび書き込みエントリを渡すことです。

struct EditorConfig {
    var isEditorPresented = false
    var note = ""
    var progress: Double = 0
}
struct BookView: View {
    @State private var editorConfig = EditorConfig()
    var body: some View {

        ProgressEditor(editorConfig: $editorConfig)

    }
}

struct ProgressEditor: View {
    @Binding var editorConfig: EditorConfig

        TextEditor($editorConfig.note)

}

キーポイント:

  • $editorConfig からの予測値 @State、タイプは Binding<EditorConfig>
  • ProgressEditor 受け取ります @Binding、それは所有する同じ状態を変更します BookView
  • TextEditor それ自体もバインディングを受けます。 $editorConfig.note 既存のバインディングから派生した文字列バインディングです。
  • バインディングは特定のソースから独立しています。それはから来る可能性があります @State、または基礎となるモデル オブジェクトまたはストレージ ラッパーから。

ObservableObject モデルの変更をビューに公開します

(13:15)

この講演では、データの永続化、同期化、または副作用の処理が必要な場合にモデル層に入ることが推奨されています。 ObservableObject は、オブジェクトが変更されようとしていることを SwiftUI に通知するクラス制約プロトコルです。 objectWillChange 出版社。一般的な書き方は次のようになります @Published 変更されるプロパティについて。

/// The current reading progress for a specific book.
class CurrentlyReading: ObservableObject {
    let book: Book
    @Published var progress: ReadingProgress

    // …
}

struct ReadingProgress {
    struct Entry : Identifiable {
        let id: UUID
        let progress: Double
        let time: Date
        let note: String?
    }

    var entries: [Entry]
}

キーポイント:

  • CurrentlyReading 特定の書籍の読書状況を管理するために使用される参照型です。
  • 使用 let のために book なぜなら、本自体はスピーチ中に変更されないからです。
  • progress 用途 @Published, そのため、レコードの変更を読み取る前に、SwiftUI の依存関係の更新がトリガーされます。
  • ReadingProgress は依然として値型です。この講演では、値型を使用してデータをモデル化し、参照型を使用してライフサイクルと副作用を管理できることが明確になりました。

@ObservedObject 外部モデルを指します。 @StateObject モデルのライフサイクルを所有する

(15:36)

@ObservedObject 読書に適しています ObservableObject 外から入ってきた。これにより、SwiftUI がオブジェクトのサブスクライブを行います。 objectWillChangeただし、このインスタンスを作成して維持する責任はありません。

struct BookView: View {
    @ObservedObject var currentlyReading: CurrentlyReading

    var body: some View {
        VStack {
            BookCard(
                currentlyReading: currentlyReading)

            //…

            ProgressDetailsList(
                progress: currentlyReading.progress)
        }
    }
}

キーポイント:

  • currentlyReading がこのビューの信頼できる情報源ですが、インスタンスのライフサイクルは外部で管理されます。
  • BookCard そして ProgressDetailsList どちらも同じオブジェクトからデータを読み取り、変更されたときに一緒に更新します。
  • ビューは、データがインターフェイスにどのようにマップされるかを宣言するだけであり、手書きのサブスクリプション コードや更新コードは必要ありません。

(20:20)

@StateObject は 2020 年の新機能です。ビューが ObservableObject を持つのに適しています。この講義では例としてカバー ローダーを使用します。カバーはビューが表示されている場合にのみ必要であり、ローダーはビューのライフ サイクルに従って作成およびリリースされる必要があります。

struct BookCoverView: View {
    @StateObject var loader = CoverImageLoader()

    var coverName: String
    var size: CGFloat

    var body: some View {
        CoverImage(loader.image, size: size)
            .onAppear { loader.load(coverName) }
    }
}

キーポイント:

  • @StateObject 実行前に SwiftUI がインスタンス化する初期値を提供します body 初めて。
  • SwiftUI は保持されます loader ビューのライフサイクル全体を通じて、ビュー構造体が再構築されるときにオブジェクトが失われないようにします。
  • onAppear 読み込みアクションのみをトリガーします。オブジェクトが解放されると、 CoverImageLoader それ自体で作業をキャンセルできます deinit

(26:39)

スピーチでは、特に間違った書き方を比較しました。 ReadingListStore() 直接 @ObservedObject 財産。ビュー構造体には安定したライフサイクルがないため、次の場合に繰り返しオブジェクトが割り当てられます。 body 再構築されるため、更新が遅くなり、データが失われます。修正方法は使用することです @StateObject その代わり。

struct ReadingListViewer: View {
    var body: some View {
        NavigationView {
            ReadingList()
            Placeholder()
        }
    }
}

struct ReadingList: View {
    @StateObject var store = ReadingListStore()

    var body: some View {
        // ...
    }
}

キーポイント:

  • ReadingListStore が所有する真実の情報源です ReadingList
  • @StateObject SwiftUI が適切なタイミングでオブジェクトを作成し、ビューの存続期間中それを永続化できるようにします。
  • この修正により、ヒープ割り当ての繰り返しとオブジェクトのリセットという 2 つの問題が同時に解決されます。

@SceneStorage そして @AppStorage 軽量 UI 状態を保存する

(32:43)

のライフサイクル @State そして @StateObject というプロセスに関係しています。アプリがシステムによって終了された後は、自動的には復元されません。スピーチで紹介されるのは、 Storage ファミリ。軽量状態を保存するために使用されます。 @SceneStorage 列インターフェイスで現在選択されているブックなど、各シーンを個別に保存するのに適しています。

struct ReadingListViewer: View {
    @SceneStorage("selection") var selection: String?

    var body: some View {
        NavigationView {
            ReadingList(selection: $selection)
            BookDetailPlaceholder()
        }
    }
}

キーポイント:

  • "selection" ストレージキーです。同じ種類のデータには、安定した一意のキーが必要です。
  • selection のように使用できます @State、バインディングは次の方法でも生成できます。 $selection
  • 各シーンは、iPad または Mac 上の複数のウィンドウに適した独自の選択状態を持つことができます。

(33:49)

@AppStorage 用途 UserDefaults 小さなアプリレベルのデータを保存します。音声の例は、表紙画像を更新するかどうかと読書の進行状況を同期するかどうかの 2 つの設定スイッチです。

struct BookClubSettings: View {
    @AppStorage("updateArtwork") private var updateArtwork = true
    @AppStorage("syncProgress") private var syncProgress = true

    var body: some View {
        Form {
            Toggle(isOn: $updateArtwork) {
                //...
            }

            Toggle(isOn: $syncProgress) {
                //...
            }
        }
    }
}

キーポイント:

  • 各設定は 2 つの独立したデータであるため、独自のキーを使用します。
  • @AppStorage デフォルトでは標準ユーザーのデフォルトを使用します。
  • バインディングを通じて設定値を変更すると、SwiftUI が自動的に保存および復元します。
  • このスピーチでは、永続化にはコストがかかるため、すべてのデータをストレージ ラッパーに詰め込まないように注意を促しています。

重要ポイント

  1. 読書進捗エディタ: 機能: 読書、コース、またはフィットネス アプリのメモを含む進捗更新シートを作成します。 実行する価値がある理由: EditorConfig このセッションでは、複数の関連フィールドを 1 つに収集する方法を説明します。 @State 価値。 開始方法: まず値型構成オブジェクトを定義し、次に値型構成オブジェクトを渡します $editorConfig 編集サブビューに移動します。

  2. カバーまたはアバターの読み込みビュー: 何をすべきか: ネットワーク イメージ ローダーを独立した SwiftUI ビューにカプセル化します。 実行する価値がある理由: @StateObject これにより、ビューのライフサイクルに従ってローダーが存続し、割り当てと状態のリセットの繰り返しが減少します。 開始方法: ローダーを準拠させる ObservableObject、で作成します @StateObject ビュー内で読み込みをトリガーします onAppear

  3. マルチウィンドウの選択状態の復元: 何をするか: iPad または Mac のリスト詳細インターフェイスで各ウィンドウの現在の選択を復元します。 実行する価値がある理由: @SceneStorage はシーンごとのストレージであり、軽量の UI 状態の保存に適しています。 開始方法: 選択値の一意のキーを定義し、渡す $selection リストビューへ。

  4. 軽量設定ページ: 何をするか: 同期するかどうか、表紙を更新するかどうか、特定の領域を表示するかどうかなどのスイッチを実装します。 実行する価値がある理由: @AppStorage に直接接続します UserDefaults、小規模なアプリレベルの設定に適しています。 開始方法: @AppStorage 各設定のプロパティを設定し、バインディングを渡す Toggle

  5. グローバル データ入力: 何をすべきか: 書籍リスト、アカウント ステータス、または現在のワークスペース モデルを SwiftUI アプリのルートに置きます。 実行する価値がある理由: スピーチは以下を使用してデモンストレーションされました @StateObject@main App すべてのシーンがモデルの変更に対応できるように、アプリ全体にわたる信頼できる情報源を作成します。 開始方法: 宣言する @StateObject private var store アプリ タイプでストアをルート ビューまたはインジェクション環境に渡します。


関連セッション

コメント

GitHub Issues · utterances