ハイライト
iOS 16 と macOS Ventura では、フォーカス (フォーカス モード) 用のフォーカス フィルター メカニズムが導入されています。以前は、Focus はシステム レベルでのみ通知動作を制御できましたが、各アプリケーションの内部ロジックまでドリルダウンできるようになりました。
主要内容
Focus は、iOS 15、macOS Monterey、watchOS 8 に搭載されています。まず、システム レベルの中断制御を解決します。作業中に通知できるのは同僚のみであり、個人的な時間にユーザーに通知できるのは少数のアプリだけです (00:18)。
iOS 16 と macOS Ventura では、フォーカス フィルターによってアプリにこの機能が提供されます。ユーザーがフォーカスをアクティブ化すると、アプリは現在のシーンに応じてコンテンツ、アカウント、外観、通知、バッジを切り替えることができます。カレンダーには個人のカレンダーのみが表示され、電子メールには現在の Focus 関連のメールボックスと通知のみが表示されます (01:04)。
講演の例はチャット アプリです。ユーザーは仕事用アカウントと個人用アカウントの両方を持つ場合があります。パーソナル フォーカスが有効になっている場合、アプリは個人アカウントのみを表示し、ステータスを個人時間に変更し、ユーザーが設定した外観にインターフェースが従うことを許可します。 Work Focus が有効になっている場合、アプリは仕事用アカウントと仕事用通知に戻ります (02:52)。
開発者は 3 つのことを行う必要があります。まず、App Intents を使用して、SetFocusFilterIntent、このアプリがフォーカスフィルターをサポートしていることをシステムに伝えます。次に、ユーザー構成可能なオプションをパラメータとして宣言します。第三に、フォーカスが変更されたときに実行しますperform()、システムから返されたパラメータをアプリまたは拡張機能に適用します (03:41)。
詳細
フォーカスフィルターの入り口を定義します
(04:57) フォーカスフィルターは基本的に準拠しています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.
"""
}
キーポイント:
import AppIntentsフォーカス フィルターが配置される App Intents 機能を導入します。 -ExampleChatAppFocusFilterこれは、アプリによってシステムに提供されるフォーカス フィルター タイプです。 -SetFocusFilterIntentこのインテントがフォーカスの下でアプリの動作を設定するために使用されることを示します。 -titleフォーカス設定のカード サブタイトルの位置に表示されます。 -descriptionユーザーが設定ページをクリックすると、追加の手順が表示されます。- タイトルと説明は次のとおりです。
static、アプリのインストール時にシステムがそれらを読み取ります。
設定可能な項目をパラメータとして宣言する
(07:02) ユーザーは何を構成できますか@Parameter決める。パラメータは次のとおりですBool、String、Float他の標準タイプ、またはアプリ自体によって定義されたエンティティにすることもできます。オプション以外のパラメータにはデフォルト値を指定する必要があります。
// 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?
// ...
}
キーポイント:
alwaysUseDarkModeは必須のブールパラメータなので、使用しますdefault: falseデフォルト値を指定します。 -statusオプションの文字列であるため、ユーザーはフォーカスのステータス メッセージを設定できません。 -accountアプリ独自のものですAccountEntity、ユーザーがアカウントを選択できるようにするために使用されます。- それぞれ
@Parameterのtitleユーザーがこのオプションを理解できるように、システム設定インターフェイスに が表示されます。 - パラメーターの値は各フォーカス設定でユーザーによって入力され、アプリはこの構成 UI を独自に構築する必要はありません。
設定ページに特定の構成を表示させます
(08:43) フォーカス フィルターを構成すると、システム設定ページに動的なタイトルと字幕が表示されます。displayRepresentation「どのパラメータが設定されているか」と「これらのパラメータの現在の値」を説明するために使用されます。
// 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)
}
// ...
}
キーポイント:
localizedDarkModeStringブール値をユーザーが判読できる外観の説明に変換します。 -titleListアカウント、ステータス、外観などの設定されたパラメータ名を保存します。 -subtitleListアカウントの表示名、ステータスメッセージ、ダークまたはダイナミックなどのパラメータ値を保存します。 -accountそしてstatusこれはオプションであり、ユーザーが設定した後にのみ表示コンテンツを入力します。 -alwaysUseDarkModeは必須パラメータであるため、Look は常に表示されます。 -DisplayRepresentationのtitleそしてsubtitleフォーカス設定カードのメイン コピーとサブ コピーになります。
フォーカスが変更されたときにアプリを更新する
(09:23) アプリがフォーカスの変更に応答する必要があるとシステムが判断すると、フォーカス フィルターが呼び出されます。perform()。アプリが実行中の場合、呼び出しはアプリ内で発生します。アプリが実行されていない場合、開発者は拡張機能を実装し、システムに拡張機能を起動させて変更を処理させることができます。アプリ自体のビューのみを更新する場合、通常はアプリ内実装で十分です。ウィジェット、通知、バッジを変更する必要がある場合は、拡張機能を検討する必要があります。
(11:24)perform()呼び出されると、パラメーターにはユーザーがフォーカス設定で構成した値が入力されます。この例では、これらの値を次のように組み立てます。AppDataそして、それをアプリのモデル層に渡して、インターフェイスと動作を更新します。
// 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()
}
// ...
}
キーポイント:
perform()フォーカス遷移後の実行エントリです。 -self.alwaysUseDarkMode現在のフォーカスに対してユーザーが構成した外観オプションを読み取ります。 -self.status現在のフォーカスの下でステータス メッセージを読み取ります。 -self.account現在 Focus にバインドされているアカウントを読み取ります。 -AppDataこれは、これらの構成を一元的に転送するために使用されるサンプル アプリ独自のデータ構造です。 -updateAppWithData(myData)フォーカス構成をアプリに適用します。 -return .result()このインテントの実行が完了したことをシステムに伝えます。
現在のフォーカス フィルターをアクティブにクエリします。
(11:47) 場合によっては、アプリはシステムコールを待つのではなく、現在のフォーカスフィルターをアクティブに読み取る必要があります。perform()。現時点では、特定のフォーカス フィルター タイプにアクセスできます。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
}
}
キーポイント:
ExampleChatAppFocusFilter.current現在有効なフォーカス フィルター パラメーターを非同期的に返します。 -try await現在の構成の読み取りが失敗する可能性があるか、システムの結果を待つ必要がある可能性があることを示します。 -currentFilter.myRequiredBoolValue必要なパラメータを読み取ります。 -currentFilter.myOptionalStringValue、myOptionalAppEnumそしてmyAppEntityオプションのパラメータまたはカスタム オブジェクトを読み取ります。 -updateAppWithData(myData)再利用とperform()同じ更新パス。 -catchエラーを記録し、それを再スローして、サイレント読み取り損失エラーを回避します。
アプリコンテキストを使用して通知をフィルタリングする
(12:16) フォーカス フィルターは、アプリの現在のコンテキストをシステムに返すこともできます。一般的な用途は通知のフィルタリングです。FocusFilterAppContext中程度の設定filterPredicate、お知らせ内容は改めてご案内させていただきますfilterCriteria。 2 つが一致しない場合、通知はミュートされます。
// 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)
}
}
キーポイント:
appContextシステムが必要とする追加のコンテキストを返します。 -allowedAccountList現在フォーカスの中断を許可されているユーザーのアカウント識別子を保存します。 -NSPredicate(format: "SELF IN %@", allowedAccountList)通知フィルターの条件を定義します。 -FocusFilterAppContext(notificationFilterPredicate: predicate)フィルタリング条件をシステムに送信します。- このコンテキストは次のように使用できます。
perform()結果が返されます。また、必要に応じて、invalidate を使用してシステムに結果を再読み取りさせることもできます。
(13:53) 通知を送信するとき、アプリは通知が属するアカウントを書き込む必要がありますfilterCriteria。音声の例では、Personal Focus は現在個人アカウントのみを許可しています。職場アカウントの通知が付属していますwork-account-identifier, そのため、システムによってミュートされます。
// 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"
キーポイント:
UNMutableNotificationContent()ローカル通知コンテンツを作成します。 -title、subtitleそしてbody通知のテキストを入力します。 -filterCriteriaこの通知が属するコンテキスト ID を記述します。- システムは、
filterCriteriaフォーカスフィルター付属filterPredicateマッチ。 - リモート通知では、JSON ペイロードにフィルター基準を設定することもできます。
(14:29) コーナー マークも現在のフォーカスに従う必要があります。言及されたスピーチUNUserNotificationCenter新しいsetBadgeCount, 開発者は、コーナーマークを現在のフォーカスの下で本当に重要な番号に変更して、無関係なリマインダーを減らすことができます。
重要ポイント
1. マルチアカウント アプリのアカウント切り替えに重点を置く
- 対処方法: チャット、電子メール、カスタマー サービス、またはソーシャル アプリで、「仕事フォーカス」の下に仕事用アカウントのみが表示され、「個人フォーカス」の下に個人アカウントのみが表示されるようにします。
- 実行する価値がある理由: スピーチでは、マルチアカウント アプリは、特定のフォーカスを特定のアカウントにリンクして、間違ったシナリオでユーザーに無関係なコンテンツが表示されるのを防ぐのに適していると明確に述べられています。
- 開始方法: アカウントを次のようにモデル化します。
AppEntity、存在するSetFocusFilterIntentでのステートメント@Parameter(title: "Selected Account") var account: AccountEntity?、 またperform()現在のアカウントとリストを更新します。
2. フォーカスに応じてステータスと外観を切り替える
- 内容: Work Focus で「作業中」ステータス メッセージを自動的に設定し、Personal Focus でより軽量なインターフェイス レイアウトまたは外観を使用します。
- 実行する価値がある理由: フォーカス フィルターの例には次のものが含まれます
statusそしてalwaysUseDarkMode, これは、フォーカス フィルターがコンテンツのフィルター処理に限定されず、アプリの動作や外観も制御できることを示しています。 - 開始方法: ステータスメッセージと外観を個別に宣言
@Parameter、使用displayRepresentationユーザー設定を明確に表示し、設定ページには曖昧な名前だけを使用しないでください。
3. 通知にコンテキスト フィルタリングを追加する
- 対処方法: 通知を送信する前にアカウント、プロジェクト、またはチャンネル ID を書き込んでください。これにより、システムは現在のフォーカスの下にある関連する通知のみを強調表示します。
- 実行する価値がある理由: スピーチメモ
filterPredicateそしてfilterCriteria一致しない場合、通知はミュートされます。これは、現在のシーン以外の中断を減らすのに適しています。 - 開始方法:
FocusFilterAppContext戻ってnotificationFilterPredicate、ローカルに通知されますUNMutableNotificationContent.filterCriteriaまたは、同じ識別子のセットをリモート通知ペイロードに書き込みます。
4. コーナーマークには、現在のフォーカスの保留中の項目のみを反映させます。
- 対処方法: Work Focus サブスクリプトは仕事用アカウントの未読数のみをカウントし、Personal Focus サブスクリプトは個人的なコンテンツのみをカウントします。
- 実行する価値がある理由: このプレゼンテーションでは、アプリがシステムに追加のコンテキストを提供して無関係なリマインダーを減らす方法としてバッジをリストしています。
- 開始方法: フォーカスが変更された後、現在のフォーカスの重要度カウントを再計算し、呼び出します。
UNUserNotificationCenterのsetBadgeCountバッジを更新します。
関連セッション
- アプリのインテントの詳細 — フォーカス フィルターによって使用されます
SetFocusFilterIntentAppIntent、パラメータ、エンティティの定義と理解は、アクセスの前提条件です。 - アプリ インテントを使用したアプリ ショートカットの実装 — これもアプリ インテントに基づいて、アプリの機能をシステムの検出と操作に引き渡す方法を示します。
- アプリのショートカットのデザイン - フォーカス設定ページの表示表現デザインに隣接して、システム入口のコピーライティング、アイコン、およびユーザーの理解を補足します。
- 待っている効率: SwiftUI のバックグラウンド タスク — システム イベントに応答するときにアプリがバックグラウンド更新をスケジュールする方法を理解するのに役立ちます。
コメント
GitHub Issues · utterances