Focus: 특정 시간동안 시스템의 행동을 조정해서 원하는 것에 집중할 수 있도록 하는 기능
Focus filter: 현재 포커스 상태에 따라 앱의 동작을 커스터마이징 할 수 있는 기능
Use case
동작 원리
Defining a Focus filter
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.
"""
}
매개변수 정의: 실제로 앱에서 설정 가능한 부분을 정의
@Parameter 프로퍼티 래퍼로 데코레이팅 된 프로퍼티들
커스텀 타입을 스는 경우는 Enitity로 정의되어야 한다. → AppIntent 프레임워크 참조
옵셔널 타입인 경우는 선택적으로 제공하며, 논옵셔널인 경우는 반드시 기본 값을 제공해야 한다.
// 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?
// ...
}
display representation 설정: focus filter 설정이 이미 되어 있는 경우의 표현
이 경우는 사용자 설정 값을 기반으로 동적으로 만들어낼 수 있다.
// 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)
}
// ...
}
Acting on a Focus filter
Focus filter가 변경된 경우 AppIntent를 통해서 앱에 전달된다.
FocusFilterIntent의 perform 메소드를 구현한다.
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()
}
// ...
}
현재 포커스 필터 값을 쿼리해오기
// 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
}
}
Providing additional context
[invalidateFocusFilterAppContext()](<https://developer.apple.com/documentation/appintents/setfocusfilterintent/invalidatefocusfilterappcontext()>)
메소드를 명시적으로 호출함으로노티피케이션 필터링 예제
// 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)
}
}
// Pass filterCriteria on UNNotificationContent
// remote notification 페이로드에도 담을 수 있다.
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"
뱃지 카운트 조정
이러한 기능을 제공하는 목적은 유저가 focus 모드를 설정했을 때 관련한 정보만 보여주기 위함이다.