ストリーミングはほとんどのブラウザと
Developerアプリで視聴できます。
-
UIデータソースの最新情報
UIデータソースを使用すると、自動差分出力機能によってテーブルビューおよびコレクションビューアイテムの更新を合理化することができます。設定した変更は高い精度と品質で自動的にアニメーション化されます。追加のコードは必要ありません。この向上したデータソースメカニズムにより、同期のバグ、例外、クラッシュを完全に回避できます。このセッションでは、識別子とスナップショットに使用するこの合理化されたデータモデルについて説明します。このデータモデルにより、UIデータ同期の些細な点ではなく、Appの動的なデータとコンテンツに注力することができます。
リソース
関連ビデオ
WWDC20
WWDC19
-
ダウンロード
(音楽)
(喝采と拍手) ようこそ
UIKitチームの スティーブ・ブリーンです 今日はAppKitチームのトロイと App Storeチームのジェイコブが 手伝ってくれます
UIデータソースについて少し お話しします (含み笑い) 今日の内容は4つです 最初は現行の最新技術です ECプラットフォームで データソースとどうやりとりするか?
次に iOS tvOS Macに導入した 新しいアプローチについてです そしてこの最新のAPIを使って デモを行います 最後に このAPIを最大限に 活用する方法を 詳しく説明します では現行の技術について お話ししましょう UIデータソースと どのようにやりとりするのか?
UICollectionViewのデータソースを 実装した例です 使っている方はご存じですね このプロトコルでは 必要な2つのメソッドを提供します セクションとアイテムの数を 聞くだけの単純なものです コンテンツ表示のために セルを要求します 単純です
約10年間 使われてきました いくつか改良点があり すぐ検証できます 2つのメソッドを提供するだけの場合 1次元データソースであれば 極めて簡単です そして素早く繰り返せます 応用範囲も広いです データソースを戻す特別なデータ構造が 必要がないからです 1次元配列ぐらい単純です もしアイテムとセクションが複数なら 2次元です すごく単純です
ところがAppは1次元や 2次元配列よりも少し複雑です
ユーザがより機能を求めるので 毎年複雑になります データソースはApp内の複雑な コントローラで支えられています
コントローラはCore Dataとの 相互作用や ウェブサービスとの通信など 多様な働きが可能です 手短に図解したいと思います これはUI層とコントローラー層が データを取得するときの会話です いいですか? やりとりは友好的に始まります “コンテンツを表示するので セクション内のアイテムの数をくれ” というように 単純で スムーズに進みます
それが 徐々に複雑になり コントローラがウェブサービスの 要求に応えます 例えば “ツイート用のデータがあるよ”とか するとコントローラ層が複雑になって “何かが変わった 変更だ” という感じです
ここで事態はより複雑になります 変更の決定はUI層に任されます そこでUI層に更新を行う必要が 出てきますが TableViewやCollectionViewに対する 変更を伴います 少し複雑になります この複雑性については 昨年のセッションでも説明しました バッチ更新を適切に構築し バッキングストアを変更する方法です
しかし努力しても― (笑い声) うまくいかない場合もあります
今 笑った方はこれに遭遇しましたね よくあることですが イライラして “何が悪かったんだ”となります コードを調べ スタック・オーバーフローを参照します 最後はreloadDataを コールするかもしれません 昨年も言いましたが Appには問題なさそうに見えます しかしこのやり方では アニメーション効果を失い ユーザ体験が損なわれます あまり良くありません
1枚だけ概念的なスライドを使います 問題は何か? 真の情報源はどこかということです すべての答えはどこにあるのか? 大きな問題はデータコントローラが 変化する独自の情報源を 持っていることです UIにも情報源があります UILayerCodeには同期を確認し これを緩和する役目があります ご存じのように 時に困難です 現行のアプローチはエラーが起きやすい 真の情報源を集約する 概念がないからです
これが現状です ではこれからは?
iOS tvOS macOSのための 新しいアプローチが誕生しました Diffable Data Sourceです (喝采と拍手) では詳しく見ましょう
performBatchUpdatesはありません クラッシュや複雑さなど すべての問題から解放されます applyという単一メソッドを使います applyは自動化された簡単なdiffです
Snapshotという新しい構成法です 実質上 現在のUIステートの 真の情報源です
IndexPathの代わりに セクションやアイテムの 一意識別子を連動させて使います IndexPathではなく識別子で更新します ではビジュアルを使って説明します
FOO BAR BIFを表示しました 創造的でしょ? これらのAppの識別子と会話します コントローラが変更したとして 適用したいSnapshotを取得します 新しい真の情報源を現在のSnapshotに 伝えるにはどうするか
BAR FOO BAZで新しいSnapshotを 構成します 順序を変えたアイテムがついてきて 新しいアイテムが入ります 概念的にはapplyは現在の状態を知り UI要素に適用しようとする 新しい状態を知ります
ステップ2はありません これだけです では方法を説明します 全てのプラットフォームにまたがる 4つのクラスがあります iOSとtvOSには UICollectionViewDiffableDataSource および UITableViewDiffableDataSourceです Macには NSCollectionViewDiffableDataSource そして NSDiffableDataSourceSnapshotは 4つに共通し 現状のUIステートに責任を持ちます 背景は以上です コードを見ていきましょう 同僚のトロイに お願いします (拍手) ありがとう パワフルで美しいAPIを使った事例を ご紹介できるのは大変うれしいです 必ずサンプルを ダウンロードしてください あとで いつでも勉強できます 最も重要なのはこの事例で 全体の仕組みをつかむことです コードが少なくて簡単です 事例を見て気付くと思います 今日のDiffable Data Sourceの 3つの事例以外に パワフルで新しい合成レイアウトAPIの 説明も含まれています これはセッション215で紹介しました これらは偶然にも Diffable Data Sourceを使い CollectionViewを事例のコンテンツに 簡単に投入する方法です では早速デモを見ましょう
デモ用のAppです 今日見ていく様々な事例には 繰り返し3つのステップが出てきます 変更を加えたい場合はいつも同じです UITableViewなどに新しいデータを 加える場合は Snapshotを作るだけです 更新サイクルに表示したい アイテムの記述にSnapshotを追加し UIの変更に自動的に コミットするように適用させます Diffable Data Sourceはdiffを見て UI要素に変更を知らせます 具体例を見ましょう Mountains Searchを開きます 典型的な検索UIです 機能は見れば分かりますね 連絡先Appでも同様のコードが ありますが この場合は世界中の山を検索します 一番上に検索窓があります 検索窓にタイプすると マッチしたリストが自動的に現れます このようにタイプするだけで こんな風に自動的に どんどん結果が現れます
Diffable Data Sourceを使えば 短いコードで簡単にできます 仕組みを見てみましょう
MountainsViewControllerSourceを 見ます
すべての動作は検索バーに 文字を打つことで始まります searchBarTextDidChangeという コールバックが コントローラに送られます
performQueryをコールし 検索バーの 検索テキストを渡します performQuery自体は非常に単純です
mountainsControllerを コールするだけです そして検索語と一致する 山のリストを要求します 山のリストが出ました
3つのステップを見ましょう NSDiffableDataSourceの Snapshotを作ります 最初 このSnapshotは空です 投入するセクションやアイテムを 選ぶのは我々です 今回表示するのは 単一セクションだけなので 1つのセクションを追加し それを任意にメインセクションとします
次にこの更新の中に表示したい アイテムの識別子を追加します 正式には通常 識別子の配列を渡します しかしSwiftでは独自の ネイティブの型を使って もっと簡単に行えます ネイティブのデータ型があり 構造体や列挙型などの値型であれば 型をハッシュ可能にしてSwift独自の オブジェクトを渡すだけです その結果どうなるか見ましょう Snapshotを作成しました Diffable Data Sourceをコールし Snapshotを適用して違いを アニメーション化させるだけです Diffable Data Sourceは消え 自動的に更新の内容を把握します ここに コードはありません 今まではユーザが入力する前に 一時停止して前回の更新を 把握する必要がありました 今は自動的に実行されます 脆弱で一時的なIndexPathも 使いません IndexPathは特定の更新を参照し 別々の更新を行いました 今では強固で永続的な識別子を使います すべてが本当に簡単にできます ここでSnapshotについて 気付いて欲しいことがあります これはSwiftの汎用クラスなので SectionIdentifierTypeと ItemIdentifierTypeに パラメータ化されます 初めにSectionIdentifierTypeを見ます 単一セクションの場合 便利なテクニックです 列挙型を宣言するだけです Swiftでの列挙型は 自動的にハッシュ可能です ハッシュ可能性が合成されています ですので列挙型の例を 1つだけ取り上げます
“山”の型について MountainsControllerを見ます
これはモデル層です “山”をSwiftの構造体として宣言し 構造体をハッシュ可能と宣言しました そのため識別子を渡さなくても Diffable Data Sourceで使用できます 重要なのは個々の“山”をハッシュ値で 識別可能にすることです
個々の“山”に自動的に 一意識別子を与えることで実現します
ここで約束したハッシュ可能性を 実装する場合 ハッシュ値を提供する 識別子のみを使います これで値型であっても 個々の“山”が得られます コピーされて回送され 参照するポインタはありません しかし識別子と特にそのハッシュ値は Diffable Data Sourceが更新を 追跡するのに十分な一意性を持ちます ハッシュ可能性の一環として 等価判定も実装しています こうしてDiffable Data Sourceに 変更を指示します ではどう設定するのか? MountainsViewControllerに 戻りましょう
これはデータソースを構成する configureDataSourceです 非常に短いコードです
この場合UICollectionViewで 作業するので UICollectionViewDiffableDataSourceの インスタンスを生成
セクションとアイテムの型で パラメータ化します 作業するCollectionViewに ポインタを渡します Diffable Data Sourceがポインタを 受け取り 自動的に CollectionViewのデータソースとして 接続します
最後にクロージャパラメータの Diffable Data Sourceがあります ゼロから独自のデータソースを 実装する場合に必要だったコードで cellForItemAt IndexPathメソッドです それが今から紹介するデータソースの コールバックメソッドです CollectionViewにコールバックします
必要なデータを表示する 適切な型のセルを要求し セルに表示したいものを 投入して戻します cellForItemAt IndexPathを 取り込みます そしてデータソースの インスタンス生成の時に渡す カプセル化したクロージャに 移植するだけです 今までと違う 非常に便利な点が1つあります 求められるアイテムのIndexPathに加え 識別子も与えられることです この場合 表示したい特定の項目に 対応するSwiftの値型です “山”を渡したので 何も必要ありません IndexPathを取り込み 関連するモデル層の オブジェクトを調べる必要もありません “山”を渡したので山の名前を取得でき セルのラベルテキストとして 設定できます それだけです CollectionViewの設定と構成方法に ついては以前と同じです performBatchUpdatesはありません 非常に簡単です 別の例を見ましょう
Wi-Fi設定のための iOSのUIモックアップです 2つの異なるセクションを使うため 今回は少しだけ複雑になります 最上部はWi-Fiの有効 無効ボタンがある 設定セクション そして接続中の 現在のネットワークの表示 下部には別のセクションがあり 接続可能と検出された ネットワークが表示されます もう1つあります Wi-Fi無効ボタンをタップ あるいは再びオンに切り替えると 折りたたみや再展開の アニメーションのUIが現れます Diffable Data Sourceを使えば無料です このUIの実装方法を見ましょう WiFiSettingsViewControllerに移動
updateUIという名前を付けた 関数を見てみます 表示すべきものが変更された場合 常にこれがコールされます 大抵は利用可能な ネットワークを検出した時ですが ユーザがトグルボタンを 切り替えた場合もです UIを変更する場合 必ずこれがコールされます
ここでも3つのステップがあります 表示したいデータを取得したら 通常は必要なアイテムを 取得するだけです 次にSnapshotを作成します 最初は空なので 表示したい内容を投入します
最上部に現れる 構成セクションに追加します Wi-Fiが有効かどうかで 1つか2つのアイテムを追加します
Wi-Fiが有効であればモデル層の バックエンドとも会話し 利用可能なネットワークリストを 要求します
この後 説明するアイテムの型で リストをラップ
ネットワークリストに このセクションを追加します 次にそのセクションに入る アイテムを追加します 2つのセクションを使用するので 各アイテムを追加する セクションを明確にできます
これで表示の準備が整いました Diffable Data Sourceに変更を適用し 差異をアニメーション化もできます アニメーション化しないこともあります 初期のデータセットを 表示する場合などは どちらもあり得ます リアルタイムにしたい場合 このように 差異のアニメーション化をオフにします
使用するのはDiffable Data Sourceを パラメータ化する― セクションとアイテムの型です 最上部を見ると セクションはまだ列挙型です 異なる2つのセクションの 使用で済みます 列挙型のセクションはSwiftで自動的に ハッシュ可能なので好都合です
宣言したアイテムの型は構造体です ハッシュ可能と宣言しました この型を宣言する理由は リストに含まれるほとんどが ネットワークアイテムだからです 加えて最上部のアイテムが異質な Wi-fiの有効 無効ボタンです 混成リストです ラッパー型の個々のアイテムを カプセル化するだけです しかしこれはDiffable Data Sourceを 渡すアイテムの型なので ハッシュ値で一意に識別されることを 確認しないといけません ネットワークアイテムの場合 そこから一意識別子を取得し 主要アイテムに移植できます
設定アイテムについてはUUIDを ダイナミックに生成します
ハッシュ関数は 一意識別子値に基づき ハッシュ値を計算するだけです 更新サイクルの間で 同じアイテムを識別するのに Diffable Data Sourceが必要なのは それだけです データソースを構成する場所を 見ましょう
今までと非常に似ていますが 今回はUITableViewで作業します Snapshotの作成とコミットの点で APIはよく似ていますが 適切なDiffable Data Sourceを インスタンス化する必要があります UITableViewDiffableDataSourceを 取得し 使用するセクションとアイテムの型で クラス名をパラメータ化します そしてポインタをtableViewに渡すと 自動的にデータソースとして Diffable Data Sourceに接続します
最後にプロバイダのクロージャを 取得します 一見すると複雑に見えますが 多様な型のアイテムがあるので当然です 表示するアイテムは3種の混成で 処理方法が異なります このコードは Diffable Data Sourceを使わなくても ItemIndexPathメソッドの セルに存在します このUIの設定は非常に簡単で Diffable Data Sourceを 構成するのでさえ容易です
最後は見て最高に楽しい事例です 再びUICollectionViewです アイテムを色見本として表示しています 最初は色がランダムに並んでいます ソートボタンをタップすると ソートが繰り返され スペクトル順になります 見ていて本当に魅力的で楽しいですね (含み笑い) (拍手) スティーブのおかげです 見て楽しいだけではなく この例は他と少し異なります 更新とコミットの方法が違うのです もし― すべてを最後の状態に ソートするだけなら簡単です このデモではソートの進行状況が 中間段階を通じて分かるように なっています このため段階ごとに1つずつソートする 武器を実装しています 連続する新しい状態ごとに Snapshotを作成し 更新サイクルを行うたびに適用します このアニメーションはそのおかげです
実装方法と違いを見ましょう
InsertionSortViewControllerを見ます
すべての興味深いアクションは performSortStepで起こります いつもの3つのステップです Snapshotを取得し 投入して適用します しかし今回は― 空のSnapshotは要求しません 代わりにDiffable Data Sourceに 現在のSnapshotを尋ねます このSnapshotにはUICollectionViewに 表示される真の情報源が あらかじめ入っています ゼロから始める必要はなく その状態から 次の中間段階を計算できます
ここでSnapshotを追加すると 見慣れたappendItemsが コールされています しかしdeleteItemsもあります SnapshotのAPIを見れば 既存のものを修正する さまざまな機能があります アイテムの場所を移動するなどです しかし他の点では同じで 表示したい最終状態を設定しています IndexPathではなく識別子を 使用しているだけです 完了したら最後に― SnapshotをDiffable Data Sourceに 適用させるだけです すばらしく革新的なソーティングです カッコいいでしょ (拍手) ありがとう (拍手) 設定したDiffable Data Sourceです UICollectionView用です 使用する型をcollectionViewで 指定します 次にアイテムを提供する クロージャを得ます 色見本の表示だけなので簡単です さて 3つの事例を見て ダイナミックなUIをいかに短いコードで 作成できるかが分かりました しかも変更に強いのです コードで奇妙な例外に当たることを 恐れず変更ができます APIに機能が織り込み済みです 微妙な違いにも触れました 特にオブジェクトを 一意に識別することは重要です Swift型を使用する場合なども ハッシュ可能である必要があります より掘り下げて これらの問題をクリアにするため スティーブが説明します ありがとう (拍手) これらのデモをとおして UIの機能が実感できました このAPIを最大限に活用する方法を 詳しく説明します
デモで見たように ステップは3つだけです Snapshotを作成し 必要に応じ構成し適用します 常にapplyメソッドをコールします 逆にperformBatchUpdatesは コールしません insertItemsもダメです コールすればフレームワークが 文句を言います 分かりますよね
Snapshotを作成する方法は2つです 最も一般的には空の Snapshotを作成します ここではセクションと アイテムの型を使います デモのように現在の状態からも作成でき 小さな修正の必要が生じたときに 非常に有効です
作成時にコピーが戻るので 好きに変更ができ 元のデータソースは影響を受けません
Snapshotができれば こんな質問もできます “アイテムの数は? セクションはいくつ?” SDKで調べられるAPIもたくさんあります
IndexPathは使いません Snapshotを構成する時 IndexPathは決してAPIに現れません
アイテムやセクションを追加する 一般的な例を見てきましたが 挿入や移動 削除などもできます これらのAPIはすべて状況を伝えるため 関連する識別子を取り込みます 別の識別子の前後に 20個の一意識別子を 挿入する場合は そのためのAPIがあります “この識別子の前に挿入しろ” と言います
特定のセクションに何も入れない場合は リンクの識別子はありません そのためにアイテムとセクションを 追加するAPIがあります 多数のセクションがあるパスで Snapshotを構成する場合 セクションデータをループします セクションを指定せず アイテムを追加できます Swiftにはヌルに指定する規定の パラメータがあります この例では判明している 最後のセクションに追加します
では 識別子についてですが 一意である必要があります ほとんどのAppはモデルオブジェクトに IDの概念があるので問題ありません ですから一意識別子を使うのは自然です
Swiftではハッシュ可能にします 便利なことにSwiftでは自動的に 行われます この自動合成を列挙型で見ました
文字列や整数型 UUIDなどを Diffable Data Sourceで使えます
また識別子にモデルデータを 取り込めましたね 非常に便利です IDは一意識別子から取りますが 他の属性からも取り込めます 名前や山で見たように 事例を管理できます そして 全部そろっているので セルを構成する際 手間がかかりません
事例でお話したことを テンプレートで見ましょう 構造体を使用するSwiftで ハッシュ可能にする方法です 非常に簡単です
IndexPathに基づくAPIには CollectionViewと TableViewがあります IndexPathのAPIが山ほどあり ほとんどが デリゲートメソッドです ユーザがコンテンツを操作し アイテムをタップすると didSelectItemAt indexPathの メッセージが出ます しかし識別子の世界で IndexPathの処理法は? 昔ながらの方法です 新しいAPIで識別子と IndexPathsを変換し IndexPathsから識別子に戻します 事例があります 元はIndexPathであったものを 識別子に変換します 一定の時間ですが非常に速いです
次にパフォーマンスです
可能な限り高速にする努力をしました 低レベルの多くのものが 改善されました
コンピュータサイエンスを 学んだ人なら 差分を計算するのは 線形操作だと分かります つまりアイテムが多ければ diffに時間がかかるのです
開発中はAppの測定が非常に重要です メインキューが常にユーザイベントに 対応できるようにします すべてを迅速に表示します 開発中 特に終了に向かって Appを測定します メインキュー上のすべてをフリーに しておきます 線形diffに多くの項目があり 余分な時間がかかる場合 バックグラウンドキューから applyをコールします (拍手) 本当にすごいのはサポートすべきAPIが ないことです (笑い声) 最高のAPIです (喝采と拍手) でしょ?
バックグラウンドキューから applyをコールすると フレームワークは メインキューにいると認識します そしてそこでdiffを続けます diffが計算されるとメインキューに戻り diffの結果から適用し通常に戻ります
1つだけ注意しておきます
この方式を選んだら一貫して バックグランドから行ってください 決してメインキューからは コールしないことです 常に同じ方式です
間違えたら文句を言いますよ さてAppleでは協働を重視し 強みとしています お互いに話し合い 一緒に問題を解決します
この一環としてフレームワーク作成者は クライアントらの悩みを見つけます これも明らかに悩みの1つです そこでShare Sheetのチームと チャットしました iOS 13で再設計されたShare Sheetで AirDrop機能付きです 彼らは新デザインの終盤に気付き “これはすごい” と言って採用しました Share Sheetチームの同僚を呼びます 新APIを採用したジェイコブです (拍手) こんにちは Share Sheetが 新しいCollectionViewのAPIを iOS 13でどう活用しているか 紹介します
最新のShare Sheetです 新しい構成レイアウトのAPIと Diffable Data Sourceを活用しています しかし真価を発揮するのは AirDropの拡張機能の中です
デバイスを閲覧する ブラウザをつけました UUIDを使って検出されたデバイスを 一意に識別します 新しいデバイスが検出されると 空のSnapshotを作成できます セクションとアイテムを追加し 差異を適用します あとはDiffable Data Sourceが 処理します どんなに項目を削除しても すばらしいアニメーションです すべてが一変しました 皆さんがどう活用するか楽しみです ではトロイにまとめをお願いします
(拍手) すばらしいですね Diffable Data Sourceは すでにApp開発を変化させています そして社内で採用したAPIを プラットフォームをまたいで 使ってもらえることに 本当にワクワクしています
Diffable Data Sourceは 必要な作業を大幅に簡素化します モデルデータをUITableVewなどに 楽に取り込めます まさにゲームチェンジャー 驚くほど簡単で強固です デバッグや書き込みにくい バッチ更新コードとはおさらば 面倒はフレームワークに任せ やりたいことに集中できます Diffable Data Sourceは現在 iOS tvOS macOSで使えます diffを把握するだけでなく それを自動的にUIにコミットさせ アニメーション化した変更を得ます 心地よいユーザとのやりとりに 追加作業は必要ありません 組み込みのdiffは高速で 耐性もテスト済みです 強固なAPIである Diffable Data Sourceが使用可能です 取り込んで使えるよう Appを適用させてください 今すぐ 負担のない 楽しい体験をしてください 悩みも少なく より短時間で作成ができます
CollectionViewで作業する人は セッション215も気に入るでしょう まったく新しいレイアウトシステムで CollectionViewのあらゆる カスタムレイアウトを行います サブクラスなしで実装でき高性能です このセッションもお見逃しなく ありがとうございました (拍手)
-
-
特定のトピックをお探しの場合は、上にトピックを入力すると、関連するトピックにすばやく移動できます。
クエリの送信中にエラーが発生しました。インターネット接続を確認して、もう一度お試しください。