스트리밍은 대부분의 브라우저와
Developer 앱에서 사용할 수 있습니다.
-
Focus 필터 소개
다른 사람이 현재 활성화한 Focus를 기준으로 앱 동작을 맞춤화하는 방법을 확인하세요. 앱 인텐트를 사용하여 앱의 Focus 필터를 정의하고 시스템의 변경 사항에 대해 조치를 취하고 앱의 보기를 다양한 방법으로 표시하는 방법을 보여드리겠습니다. 또한 알림을 필터링하고 배지 수를 업데이트하는 방법을 살펴보겠습니다. 이 세션을 최대한 활용하려면 먼저 WWDC22의 ‘Dive into App Intents(앱 인텐트 자세히 알아보기)'를 시청하시기 바랍니다.
리소스
관련 비디오
WWDC22
-
다운로드
♪ 부드러운 힙합 음악 ♪ ♪ 안녕하세요, Teja입니다 iOS 시스템 환경 팀 엔지니어죠 이번 세션에서는 집중 모드 필터를 소개할게요 집중 모드는 iOS 15, watchOS 8 macOS Monterey에 도입됐죠 일정 시간 동안 시스템 동작을 구성하면서 사람들이 중요한 것에 집중할 수 있는 방법이죠 제어 센터로 가서 시스템 집중 모드나 사용자 지정 집중 모드 중에 하나를 선택하면 집중 모드가 활성화됩니다 집중 모드가 활성화된 동안에는 알림 동작을 사용자가 설정할 수 있습니다 예를 들어 업무 집중 모드인 동안에는 동료한테서만 알림을 받고 싶을 수 있죠 또 일과 관련 있는 몇 개의 앱만 선택해 알림을 받을 수도 있습니다 각 집중 모드는 설정에서 시스템 동작을 구성하고 예약할 수 있어요 iOS 16과 macOS Ventura는 집중 모드 필터로 집중 모드의 기능을 향상시켰습니다 우선 집중 모드 필터를 소개하고 어떻게 작동하는지 알려 드릴게요 그다음 앱에서 집중 모드 필터를 정의하는 법을 살펴보고 집중 모드 필터에서의 동작이 어떤 의미인지 알아보죠 마지막으로 여러분의 앱이 어떻게 시스템에 추가 컨텍스트를 다시 제공할 수 있는지 설명할게요 집중 모드 필터는 현재 활성화된 집중 모드를 기준으로 사용자가 앱 동작을 맞춤 설정하는 방식입니다 집중 모드 필터를 적용한 시스템 앱 중에 몇 가지 좋은 예가 있어요 캘린더는 집중 모드가 활성화됐을 때 기본적으로 어떤 달력이 보여야 하는지 필터링할 수 있죠 제 캘린더 앱은 보통 이렇게 보여요 보시다시피 달력에 일과 개인 행사가 섞여 있죠 캘린더에 대한 집중 모드 필터를 구성할 수 있습니다 개인 집중 모드에는 개인 달력만 표시하는 것처럼요 집중 모드 필터를 설정하고 나서 제 캘린더는 이렇게 보입니다 달력에는 집중 모드가 일정을 필터링했고 필터링을 전환하는 법을 제공한다고 나와 있죠 이제 개인적인 시간을 즐길 때 업무 일정에 신경 쓸 필요가 없겠네요 Mail의 받은 편지함은 집중 모드에서 관련 메일함만 볼 수 있게 필터링됩니다 메일 알림 역시 필터링이 돼서 관련 알림만 눈에 띄게 표시되죠 즉 업무 집중 모드에는 업무 관련 메일 알림만 받도록 Mail을 설정해서 개인 메일 알림으로 방해받지 않겠죠 여러분의 앱이 집중 모드 필터를 구현할 이유는 여러 가지가 있어요 앱이 계정 여러 개를 관리할 때 특정 계정과 집중 모드를 연결하는 게 적절하겠죠 데이터의 양이 많은 앱은 집중 모드의 필터로 콘텐츠를 필터링할 필요가 있죠 사용자가 집중하는 동안 주의가 산만해지지 않게 도와주고 싶다면 배지 수나 앱 내 알림 알림을 줄여서 도와줄 수 있어요 활성화된 집중 모드에서 가장 중요한 부분이죠 또 앱은 활성화된 집중 모드를 기준으로 테마 또는 레이아웃을 표시할 수 있어요 기본적으로 앱이 상황에 따라 다른 콘텐츠를 표시할 때 집중 모드 필터를 사용하면 사용자 경험을 향상시킬 수 있죠 집중 모드 필터가 어떻게 작동하는지 설명할게요 앱은 집중 모드 별로 사용자가 지정할 수 있는 항목을 정의해요 이 작업은 AppIntent를 사용하죠 시스템은 집중 모드 별로 구성할 수 있는 내용을 보여 줘요 AppIntent로 정의한 속성을 구성하는 UI는 집중 모드 설정에서 집중 모드 필터로 표시되죠 사용자들은 집중 모드 설정에 가서 앱에 대한 집중 모드 필터를 구성해 앱이 특정 방식으로 작동게 구성할 수 있어요 이제 여러분의 코드베이스에 집중 모드 필터를 통합하는 방법을 알아볼게요 집중 모드 필터를 정의하는 데는 몇 단계가 있어요 SetFocusFilterIntent의 구현이 첫 번째예요 집중 모드 별 사용자 지정 설정을 원하는 앱이 있다는 걸 시스템에 나타내는 거죠 두 번째는 앱의 매개 변수를 정의하는 거예요 사용자가 앱 내에서 구성할 수 있는 항목을 나타내는 거죠 마지막 단계로 디스플레이 표현을 설정합니다 집중 모드 필터가 시스템 설정에 나타날 때 제대로 된 내용을 담는 거죠 이렇게 하면 사용자가 구성을 알 수 있습니다 코드로 들어가 볼게요 우선 AppIntents를 불러와요 SetFocusFilterIntent를 구현하는 구조체를 정의하죠 이게 여러분의 집중 모드 필터예요 제목과 설명을 정하면 이 앱의 집중 모드가 무엇인지 사용자에게 이해시킬 수 있죠 집중 모드 필터는 설정에서 표처럼 나타나요 집중 모드 필터를 구성하기 전에 이런 모습으로 사용자에게 표시되죠 이 아이콘은 여러분의 앱 아이콘이고 주 텍스트는 앱 이름이며 보조 텍스트는 집중 모드 필터에서 설정한 제목 변수와 일치하죠 사용자가 필터를 구성하기 위해 눌러서 들어가면 똑같은 내용이 표시돼요 이번에는 시스템에 추가 설명으로 제공한 문자열도 포함되죠 제목과 설명 문자열은 모두 고정되어 있고 앱 설치 시에 시스템에서 읽여 들여요 집중 모드 필터를 정의할 때 매개 변수로 장식된 일련의 속성을 제공해 사용자 지정 항목을 명시해야 합니다 매개 변수를 정의할 때 이름과 데이터 유형을 지정해야 해요 매개 변수는 표준 데이터 유형이 될 수 있어요 부울, 문자열 부동 소수점 등이 있죠 구성하고 싶은 맞춤형 데이터 유형이 있는 경우 엔터티로 만들면 매개 변수로 꾸밀 수 있습니다 엔터티와 앱 인텐트에 대해 더 알고 싶다면 '앱 인텐트 파고들기' 세션을 보세요 집중 모드 필터를 정의할 때 매개 변수에 대한 데이터 유형과 이름만 지정합니다 집중 모드마다 적용될 매개 변수의 값을 구성하는 건 사용자의 몫이니까요 매개 변수를 선택 사항으로 표시할 수 있으니 매개 변수를 구성할 필요가 없습니다 선택 사항이 아닌 매개 변수는 기본값을 제공해야 합니다 코드로 볼까요? 집중 모드 필터에서 원하는 유형의 변수를 정의하고 매개 변수로 장식하여 선택적인 매개 변수 혹은 매개 변수를 지정합니다 여기서는 필수인 부울 매개 변수를 만들었습니다 집중 모드 필터가 늘 다크 모드를 사용해야 하는지를 나타내죠 저는 기본값을 거짓으로 설정했습니다 또 선택적 문자열 매개 변수를 만들었습니다 집중 모드 동안 사용자의 상태 메시지를 나타내는 변수죠 마지막으로 앱에서 정의한 앤터티인 선택적 계정 매개 변수를 포함했습니다 이 매개 변수는 특정 계정에 대한 정보를 포함하죠 세 가지 매개 변수에 모두 제목을 설정했는데 이 제목은 사용자에게 매개 변수를 설명하기 위해 설정에 표시됩니다 집중 모드 설정에서 사용자가 앱의 집중 모드 필터를 구성하면 아까 보여 드린 것과 유사한 표 모양으로 나타납니다 그런데 이번에는 필터가 이미 구성돼 있어서 구성된 내용을 반영하기 위해 내용이 동적으로 표시되죠 이 아이콘은 여전히 앱 아이콘입니다 주 텍스트와 보조 텍스트는 FocusFilterIntent의 디스플레이 표현 속성을 사용해 바꿀 수 있습니다 주 텍스트는 어떤 매개 변수가 설정됐는지 나타냅니다 '계정 선택'이나 '상태 설정' 같은 거죠 보조 텍스트는 매개 변수의 설정 결과를 나타냅니다 '업무 계정'이나 '업무 중'처럼요 제 코드에서 저는 디스플레이 표현이 동적으로 생성되도록 설정했습니다 계정과 상태가 선택적 매개 변수이므로 실제로 설정된 경우에만 동적 주 텍스트와 보조 텍스트에 포함되죠 alwaysUseDarkMode는 필수 매개 변수이므로 항상 주 텍스트와 보조 텍스트에 포함됩니다 자, 이제 집중 모드 필터를 정의했습니다 이제 사용자들은 집중 모드 설정에 들어가 특정 집중 모드에 대한 일정한 값을 설정할 수 있죠 그런데 앱은 그 설정을 어떻게 알 수 있을까요? 앱이 어떻게 알아서 스스로 업데이트될까요? 시스템에서의 변화에 따라 움직여야죠 집중 모드의 변경 사항이 생기고 앱이 이 변경을 인지해야 한다고 시스템이 판단하면 두 가지 방법 중 하나로 정보가 전달될 겁니다 앱이 실행 중이면 FocusFilterIntent에서 이 내용을 구현했는지 수행 메서드가 호출됩니다 앱이 실행 중이 아니면 확장앱을 구현할 수 있고 확장앱이 열리겠죠 정리해서 만약 FocusFilterIntent에서 수행 메서드를 구현하면 확장 앱에서 호출을 받을 겁니다 수행 메서드 호출은 앱이나 확장앱이 하기 때문에 모든 앱에 확장앱이 필요한 건 아닙니다 보통 앱이 집중 모드 전환에 반응해 화면만 업데이트한다면 앱 내에서만 구현해도 충분합니다 앱의 위젯, 알림, 배지가 집중 모드 전환에 기반해 바뀌어야 한다면 확장앱에서의 구현을 고려해 볼 수도 있겠죠 기본적으로 앱이 화면 이외의 내용을 업데이트하려면 확장앱도 구현해야 합니다 나머지 세션에서 제가 여러분의 '앱'이라고 하면 그건 맥락에 따라 앱이 될 수도 있고 확장앱이 될 수도 있어요 집중 모드 필터에 응답하려면 수행 함수를 구현하고 설정에 제공된 매개 변수의 채워진 값에 접근한 다음 이 값을 사용해서 앱의 뷰와 동작을 업데이트합니다 시스템에서 앱이 집중 모드 전환에 응답해야 한다고 판단할 때 수행 함수의 구현이 호출될 겁니다 이전에 제공된 값이 더 이상 관련 없다고 시스템이 판단할 때도 수행 함수는 호출됩니다 이 경우 집중 모드 필터의 매개 변수는 기본값으로 구성됩니다 앱의 집중 모드 필터에서 수행이 호출되면 모든 매개 변수의 값이 설정에서 구성한 값과 일치하도록 채워집니다 명명된 매개 변수의 값은 self.'매개 변수 이름'을 호출하여 읽을 수 있습니다 이 예시에서는 수행의 끝에서 제가 받은 데이터로 앱을 업데이트했습니다 가끔은 현재의 집중 모드 필터 매개 변수를 쿼리해야 할 수도 있습니다 제 경우에는 ExampleChatAppFocusFilter라는 필터이므로 ExampleChatAppFocus Filter.current에 접근하죠
이제 앱이 집중 모드 필터에 작동하게 됐으니 다음 단계는 사용자 경험을 더 발전시키는 겁니다 앱 동작이 시스템에서 어떻게 다시 변경되었는지 추가 컨텍스트를 제공하면서 말이죠
추가 컨텍스트를 제공하면서 앱의 화면 외부에서 앱 동작에 영향을 줄 수 있습니다 예를 들어 알림 필터링과 알림 배지 수 설정에 영향을 줄 수 있겠죠 시스템 정보를 제공하는 한 가지 방법은 AppContext 개체를 사용하는 겁니다 이 개체는 수행 함수 결과 중에서 일부로 반환될 수 있습니다 아니면 언제든지 집중 모드 필터의 AppContext를 반환하고 무효화를 호출해서 시스템에 업데이트된 값을 가져오게 하면 되죠 집중 모드 필터가 활성화되면 앱에는 특정 알림이 사용자를 방해하면 안 되는지 여부를 결정하는 추가 컨텍스트가 존재하게 될 겁니다 이 정보를 전달하려면 앱이 AppContext의 filterPredicate 속성을 설정해야 합니다 이 필터 조건자는 UNNotification의 filterCriteria라는 새로운 문자열 속성과 함께 작동합니다 알림의 필터 조건이 필터 조건자와 일치하지 않으면 알림이 음소거됩니다 FocusFilterIntent에서 필터 조건자를 설정하려면 앱 컨텍스트에 포함하세요 개인 집중 모드가 장치에 활성화되고 사용자가 개인 계정만 선택하도록 설정했다고 가정해 볼게요 이 경우에 필터 조건자를 개인 계정의 식별자로 설정했어요 수신 알림이 개인 계정에서 온 게 아니면 사용자를 방해해선 안 됩니다 여기서 알림을 구성할 때 filterCriteria를 업무 계정의 식별자로 설정했어요 이렇게 하면 알림이 업무 계정에서 전송되는 걸 알았을 때 알림이 울리지 않을 테니까요 계정 식별자가 개인 계정 식별자와는 일치하지만 방금 설정한 조건자와는 일치하지 않기 때문이죠 이 예시는 로컬 알림에 대한 거지만 필터 조건은 원격 알림의 JSON 페이로드에서도 설정할 수 있어요 시스템의 추가 컨텍스트를 제공하는 또 다른 방법은 현재 활성화된 집중 모드 동안 무엇이 중요한지 반영하도록 배지 수를 업데이트하는 거예요 이러면 사용자의 주의가 분산되지 않겠죠 UserNotifications의 새 API가 이걸 목적으로 만들어졌죠 UNUserNotificationCenter에서 새 배지 값인 부호 없는 정수로 setBadgeCount를 호출하기만 하면 됩니다 이제 추가 컨텍스트를 제공해 알림을 필터링하거나 배지 수를 설정하는 법을 배웠어요 이 기능의 목표는 사용자가 집중할 때 그것과 가장 관련 있는 걸 드러내는 거예요 집중 모드가 활성화됐을 땐 주의 산만을 방지하려고 관련 없는 콘텐츠를 최소화해야 할 때가 있죠 다음 단계로는 여러분의 앱이 집중 모드 필터에서 이점을 얻을지 고려하고 구성할 수 있는 속성을 결정하고 처리하기 위해 앱과 확장을 설정한 다음 한 단계 더 나아가 추가 컨텍스트를 제공할지 여부를 판단해 보세요 집중 모드 필터는 여기까지예요 이번 세션에 참여해 주셔서 감사합니다 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"
-
-
찾고 계신 콘텐츠가 있나요? 위에 주제를 입력하고 원하는 내용을 바로 검색해 보세요.
쿼리를 제출하는 중에 오류가 발생했습니다. 인터넷 연결을 확인하고 다시 시도해 주세요.