ストリーミングはほとんどのブラウザと
Developerアプリで視聴できます。
-
集中モードフィルタについて
現在有効になっているユーザの集中モードに基づいてAppの動作をカスタマイズする方法をご確認ください。App インテントを使用して、Appの集中モードフィルタを定義したり、システムによる変更に対応させたり、Appのビューをさまざまな方法で表示したりする方法を紹介します。また、通知のフィルタリング方法やバッジカウントの更新方法も紹介します。 このセッションを最大限に活用するには、WWDC22の「Appインテントの詳細」を最初にご覧ください。
リソース
関連ビデオ
WWDC22
-
ダウンロード
♪♪ ♪ iOS System ExperienceチームのTejaです 集中モードフィルタが このセッションのテーマです 集中モードはiOS 15 macOS Monterey watchOS 8で導入されました システムの動作を 設定して 重要なことに 集中できるようになります 集中モードを有効にするには コントロールセンターへ システムが提供する集中モードか カスタムの集中モードを選択します 集中モードが有効な間は 通知のカスタマイズが 可能です 例えば仕事の 集中モードでは 同僚からの通知のみを 許可したり 仕事関連の一部のAppからの 通知のみを 許可したりできます 各集中モードについて システムの挙動に関する 設定とスケジュールが 可能です iOS 16とmacOS Venturaは 集中モードフィルタにより 集中モードをさらに強化します まず集中モードフィルタと その動作を 紹介します 次にAppで集中モードフィルタを 定義する方法 その後集中モードフィルタに対する 操作が意味することについて 最後にAppがシステムに いかに追加のコンテキストを 提供できるか説明します 集中モードフィルタは 現在の集中モードに基づき Appの動作を カスタマイズする方法です 集中モードフィルタ採用の システムAppの 素晴らしい例のいくつかです カレンダーAppでは デフォルトで表示する カレンダーを フィルタリングできます 普段はこんな感じ このように仕事と 個人の予定が 混在しています カレンダーの 集中モードフィルタを設定し 個人用カレンダーだけを 表示できます 集中モードフィルタ設定後 これが私の カレンダーです 集中モードによる フィルタリングが示され フィルタの切り替え方法が 提供されています これで個人的な時間を 楽しむときに 仕事に 邪魔されません メールAppでも フィルタして 特定のメールボックスのみを 表示することができます メールの通知も フィルタされるので 該当する通知だけが目立つよう 強調表示されます 仕事の集中モード中は 個人的なメールの影響なしで 仕事関連のメールのみ 配信し 影響を受けないよう 設定できます 集中モードフィルタを Appに実装する理由は 色々考えられます Appが複数のアカウントを 管理している場合 特定のアカウントを集中モードに 関連付けることをお勧めします 大量のデータがあるAppの場合 コンテンツの フィルタリングが 必要になるかもしれません ユーザーが集中している時に 気が散らないよう バッジ数を減らしたり App内のアラートや 通知を 集中モードに対して 減らします Appの見え方において 集中モードに基づき テーマやレイアウトを 適用することもできます Appが異なるコンテンツを 表示できる場合 集中モードフィルタで ユーザー体験を 向上させることができます 集中モードフィルタの仕組みを 説明しましょう Appにて集中モードごとに カスタマイズ可能なことを定義します これには Appインテントを使用します システムに集中モードごとに 構成できる内容を示し Appインテントで定義される プロパティを設定するUIは 集中モードフィルタとして 集中モードの設定に現れます ユーザーがAppに対し 集中モードフィルタを設定することで Appが特定の動作を するように 設定が可能になります 集中モードフィルタを コードベースに 組み込む方法です 集中モードフィルタの定義には いくつかパーツがあります まずSetFocusFilterIntentを 実装すること Appが 集中モードごとの カスタム設定の意思があることを システムに対し示します 次にパラメータを 定義します これらはApp内で 設定できる内容を 表したものです 最後にディスプレイ表現を 設定して システム設定に 集中モードフィルタの 正しい内容を 表示させるようにします これによりユーザは 設定内容を認識できます コードの説明です まずAppIntentsをインポート SetFocusFilterIntentを 実装した構造体を定義します これが集中モードフィルタです タイトルと説明の設定で ユーザーがその内容を 見つけるのをサポートします 集中モードフィルタは 設定の グリッドビューに表示され 集中モードフィルタの 設定前は このような感じでユーザーに 表示されます アイコンは Appのアイコン プライマリテキストは Appの名前 セカンダリテキストは ユーザーがフィルタを設定すると 集中モードフィルタの 設定したタイトル変数と 一致したものになります フィルタの設定のためにタップを すると同じ内容が表示されます ただしこの場合 システムには追加情報として 入力された説明文字も 含む形となります タイトルと説明文字は 静的なもので Appのインストール時に システムに読み取られます 集中モードフィルタを 定義する際には プロパティの指定により カスタマイズできる内容を 示すことができます パラメータを指定する際は 名前とデータ型を 与える必要があります パラメータは Bool型や float型 文字列など 標準データ型が使え さらにエンティティを使って カスタムデータ型として パラメーターを 指定することもできます エンティティと Appインテントの詳細は 「Appインテントの詳細 」 をご確認ください 集中モードフィルタの定義は パラメーターの データ型と名前の指定 各集中モードで適用される パラメータ値は ユーザーが設定します パラメータは オプショナルとして マークが可能で その場合設定は任意です オプショナルでない場合は デフォルト値を提供します コードではパラメータ または オプショナルパラメータ の指定は 集中モードフィルタ内で 変数を定義し @Parameterを指定します 集中モードフィルタが 常にダークモードを 使用かを表すBool型の パラメータを作成しました 既定値はfalseです オプショナルの文字列 パラメータも作成し 集中モード中の ステータスを表現します 最後にAppで定義した エンティティである 特定のアカウント情報を 含む アカウントパラメータを オプショナルで用意しました それぞれのパラメータに 設定されるタイトルは パラメータの説明のため 設定で表示されます 集中モードの設定で 集中モードフィルタを設定すると 先ほどと同様に グリッドに表示されます 今回はフィルタが 既に構成されているため 内容を反映させる ダイナミックな内容になります アイコンはAppと同じ プライマリテキストと セカンダリテキストは FocusFilterIntentの ディスプレイ表現プロパティで カスタマイズできます プライマリテキストは Select Account Set Status などといった表現で どのパラメータが 設定されているかを表現します セカンダリテキストは Work AccountやWorkingなど パラメータの設定内容を 表現する必要があります 私のコードでは ディスプレイ表現を 動的に生成するように 設定しました アカウントとステータスは オプショナルなので 実際に設定された場合のみ 動的な形で テキストに含まれます alwaysUseDarkModeは 必須のパラメータなので 両方のテキストに含まれます 集中モードフィルタの 定義ができました 集中モード設定に行くと 特定の値を カスタマイズできます カスタマイズされたことを Appが知る方法は? そして それに応じて App自体を更新するには? システムからの変化に 対応する必要があります 集中モードの変更が発生し システムが それがAppに対し 重要なものだと判断した場合 いずれかの方法で その情報をAppに提供します Appが起動中であれば FocusFilterIntent内の performメソッドが 呼び出しを受けます 起動してない場合 Extensionを実装することで 対応できます FocusFilterIntentに performを実装していれば Extensionで呼び出されます performはAppからも Extensionからも稼働しますが すべてのAppにExtensionが 必要ということではありません 通常 集中モードの 遷移に対応して 自身のViewを 更新するだけなら Appでのみperformを 実装するだけで十分です Appのウィジェット 通知などを 集中モード遷移に基づいて 変化させる場合は Extensionの実装を 検討する必要があります 基本的にAppが View以外を更新する場合は Extensionの実装が必要です これ以降の「みなさんのApp」 といった場合 AppとExtensionの いずれかを 意味することもあります 集中モードフィルタに対応するには perform関数を実装し 設定で提供される パラメータ値にアクセスし AppのViewと 動作を更新します performの実装は Appが Focusの遷移に 必要と判断すると 呼び出されます performは 以前に提供された値が 適切なものではなくなったと 判断された場合にも呼び出されます この場合集中モードフィルタの パラメータは デフォルト値で 設定されます performが呼び出されると 設定内容と一致する パラメータの値が 入力されます 指定のパラメータ値を 読み取るには self."name of the parameter."から この例では performの最後に 受け取ったデータで Appを更新します 時には 現在の パラメーターを照会したい 場合があるかもしれません 私の場合 ExampleChatApp FocusFilterなので ExampleChatAppFocus Filter.currentにアクセスします
これで対応できます 次の手順ではAppが どう変更されたかの 追加のコンテキストを 提供することで ユーザー体験の向上 実現します
追加のコンテキストを 提供すると Viewの外部にも 影響を与えることができます たとえば通知の フィルタリングや Appの通知バッジ数の 設定などです 情報提供の方法のひとつが AppContextデータを 経由するものです このデータは performメソッドの結果の 一部として戻せるものです あるいは 集中モードフィルタで 任意でAppContextを返し invalidateを呼び出し システムに更新値を 取得させることもできます 集中モードフィルタが有効な場合 特定の通知が 割り込みにとなるかを 判断する追加コンテキストを 持てます この情報を渡すためには filterPredicateプロパティの 設定が必要です このFilter Predicateは filterCriteriaという 新しい文字列を使い 連携動作します 通知のフィルタ基準が Filter Predicateと 一致しない場合は 通知は消音となります FocusFilterIntentからの Filter Predicate設定は App Contextにそれを含めます パーソナルの集中モードが有効で ユーザーが 個人アカウントを選択し 集中モードを 設定したとします 個人アカウントの 識別子を Filter Predicateに設定 受信する通知が 個人アカウントからでない場合 ユーザーの作業を 中断しないように この通知を設定するとき filterCriteriaを 仕事用アカウントのIDに設定 通知が仕事用アカウント に行くことを把握しているので この場合アカウントが 一致しないため 通知は消音された 形になります なぜなら アカウント識別子は 私が先ほど設定した 個人の識別子と 一致しないためです これはローカル通知用ですが フィルタの条件を リモート通知の JSONペイロードに することもできます システムに追加のコンテキストを 提供する別の方法は Appのバッジ数を 更新して 集中モードにとって重要なもの だけを反映させることです これでユーザーが 集中できるようになります UNUserNotificationsに それ用の 新しいAPIがあります UNUserNotificationCenterでは 新しいバッジの値を表す setBadgeCountを 呼び出すだけです バッジ数の設定など 追加のコンテキストを提供する 方法が分かりました この機能の目的は 集中モードに対する 最も関連性の高いことを ユーザーに提供することです 集中モードの邪魔に ならぬよう コンテンツの最小化が 必要なこともあるでしょう 次のステップは Appのどの部分に 集中モードフィルタが有効か その検討と特性を決定し AppとExtensionの 設定と処理 追加コンテキスト の提供で さらに一歩踏み込んで 判断することです 以上が 集中モードフィルタです ありがとうございました WWDCを楽しみましょう ♪
-
-
4:57 - Implementing SetFocusFilterIntent
// Implementing SetFocusFilterIntent import AppIntents struct ExampleChatAppFocusFilter: SetFocusFilterIntent { static var title: LocalizedStringResource = "Set account, status & look" static var description: LocalizedStringResource? = """ Select an account, set your status, and configure the look of Example Chat App. """ }
-
7:02 - Defining your Parameters & Entities
// Defining your Parameters & Entities import AppIntents struct ExampleChatAppFocusFilter: SetFocusFilterIntent { @Parameter(title: "Use Dark Mode", default: false) var alwaysUseDarkMode: Bool @Parameter(title: "Status Message") var status: String? @Parameter(title: "Selected Account") var account: AccountEntity? // ... }
-
8:43 - Display Representation
// Display Representation struct ExampleChatAppFocusFilter: SetFocusFilterIntent { // ... var localizedDarkModeString: String { return self.alwaysUseDarkMode ? "Dark" : "Dynamic" } var displayRepresentation: DisplayRepresentation { var titleList: [LocalizedStringResource] = [], subtitleList: [String] = [] if let account = self.account { titleList.append("Account") subtitleList.append(account.displayName) } if let status = self.status { titleList.append("Status") subtitleList.append(status) } titleList.append("Look") subtitleList.append(self.localizedDarkModeString) let title = LocalizedStringResource("Set \(titleList, format: .list(type: .and))") let subtitle = LocalizedStringResource("\(subtitleList.formatted())") return DisplayRepresentation(title: title, subtitle: subtitle) } // ... }
-
11:24 - Implementing Perform on your Focus filter
// Implementing Perform on your Focus filter import AppIntents struct ExampleChatAppFocusFilter: SetFocusFilterIntent { // ... func perform() async throws -> some IntentResult { let myData = AppData( alwaysUseDarkMode: self.alwaysUseDarkMode, status: self.status, account: self.account ) myModel.shared.updateAppWithData(myData) return .result() } // ... }
-
11:47 - Calling Current
// Calling Current import AppIntents func updateCurrentFilter() async throws { do { let currentFilter = try await ExampleChatAppFocusFilter.current let myData = AppData( myRequiredBoolValue: currentFilter.myRequiredBoolValue, myOptionalStringValue: currentFilter.myOptionalStringValue, myOptionalAppEnum: currentFilter.myOptionalAppEnum, myAppEntity: currentFilter.myAppEntity ) myModel.shared.updateAppWithData(myData) } catch let error { print("Error loading current filter: \(error.localizedDescription)") throw error } }
-
13:27 - Set a filterPredicate
// Set filterPredicate on an App context import AppIntents struct ExampleChatAppFocusFilter: SetFocusFilterIntent { var appContext: FocusFilterAppContext { let allowedAccountList = [account.identifier] let predicate = NSPredicate(format: "SELF IN %@", allowedAccountList) return FocusFilterAppContext(notificationFilterPredicate: predicate) } }
-
13:53 - Pass filterCriteria on UNNotificationContent
// Pass filterCriteria on UNNotificationContent let content = UNMutableNotificationContent() content.title = "Curt Rothert" content.subtitle = "Slide Feedback" content.body = "The run through today was great. I had few comments about slide 22 and 28." content.filterCriteria = "work-account-identifier"
-
-
特定のトピックをお探しの場合は、上にトピックを入力すると、関連するトピックにすばやく移動できます。
クエリの送信中にエラーが発生しました。インターネット接続を確認して、もう一度お試しください。