개요
향후 Apple Intelligence로 인해 Siri와 App Intent에 대해 공부해 둬야겠다는 생각을 하고 있었는데
이번기회에 App Intent에 대해 정리한 글을 작성한다
이 글은 다음 영상(WWDC24)에서 발표한 내용을 정리한 글이다
App Intents
다음 이미지처럼 App Intents는 앱의 기능을 외부에서 사용하는데 공통적인 인터페이스라고 볼 수 있다
App Intents는 주로 두 가지 방식으로 사용된다
- Actions 정의: 앱의 기능을 시스템에 알려주는 역할, 이를 통해 Siri나 Spotlight가 앱의 기능을 인식하고 사용자에게 제공할 수 있다
- 데이터 제공: 앱이 가진 데이터를 외부에 제공하는 역할, 이를 통해 앱 외부에서도 앱의 데이터를 활용할 수 있다
핵심 구성요소
Intents
- AppIntent
Intents는 Action을 수행하는데, 마치 동사라고 볼 수 있다
예를 들어, 사용자가 Siri에게 "음악 재생"이라고 말하면, "음악 재생"이라는 인텐트가 실행되어 앱이 음악을 재생한다
Entities
- AppEntity
Entities는 Intents의 대상이 되는 오브젝트다
Intent가 동사라면 Entities는 명사라고 볼 수 있다
"음악 재생"이라는 인텐트가 있다면, "음악"이라는 엔티티는 재생될 음악을 나타내는 데이터를 가지고 있을 것이다
이러한 엔티티는 앱의 데이터 모델에 정의되어 있으며, 인텐트에 의해 참조될 수 있다
App shortcuts
- AppShortcutsProvider
위와같이 동사와 명사로 비유했다면, App shortcuts는 문장이다(Intents와 Entities를 조합)
이를 통해 사용자는 앱의 특정 기능을 빠르게 접근하고 실행할 수 있다
예를 들어, "음악 재생"이라는 인텐트와 "내가 좋아하는 음악"이라는 엔티티를 결합하여 "내가 좋아하는 음악 재생"이라는 앱 단축키를 생성할 수 있다
활용 예시
예시들은 다음과 같다
App shortcuts에서 Action으로 인텐트를 사용하는 경우,
이때 인자를 엔티티로 처리하는 경우,
그리고 직접 AppShortcutProvider를 통해 앱인텐트를 만드는 경우다
Shortcuts action
인텐트는 Shortcuts에서 Action으로 사용되며,
AppIntent, OpenIntent 프로토콜은 인텐트를 정의하는 데 사용된다
이때 perform()을 통해 IntentResult를 반환한다
다음과 같이 App shortcuts에서 볼 때, Intents를 만들려면 아래와 같이 AppIntent를 만들면된다
struct OpenPinnedTrail: AppIntent {
static let title: LocalizedStringResource = "Open Pinned Trail"
func perform() async throws -> some IntentResult {
NavigationModel.shared.navigate(to: .pinned)
return .result()
}
static let openAppWhenRun: Bool = true
}
해당 AppIntent는 앱이 열리고 지정된 뷰(.pinned)로 네비게이션을 통해 이동한다
openAppWhenRun은 OpenIntent를 준수하면 정의하지 않아도 된다
Parameterized Action
이전 App shortcuts에서 위와 같이 인자로 선택해야 하는 부분이 새로 생겼다
이는 아래와 같이 @Parameter로 AppEntity를 가지는 프로퍼티 덕분이다
struct OpenTrail: AppIntent, OpenIntent {
static let title: LocalizedStringResource = "Open Trail"
@Parameter(title: "Trail")
var target: TrailEntity
func perform() async throws -> some IntentResult {
NavigationModel.shared.navigate(to: target)
return .result()
}
}
AppEntity
AppEntity는 Model의 id를 가지며, EntityQuery를 가진다
struct TrailEntity: AppEntity {
@Property(title: "Trail Name")
var name: String
var displayRepresentation: DisplayRepresentation = {
DisplayRepresentation(title: name, image: Image(named: imageName))
}()
var id: Trail.ID
static var defaultQuery = TrailEntityQuery()
}
EntityQuery
EnitityQuery는 위와같이 다음 두 가지에 대한 구현이 필요하다
struct TrailEntityQuery: EntityQuery {
func entities(for identifiers: [TrailEntity.ID]) async throws -> [TrailEntity] {
return TrailDataManager.shared.trails(with: identifiers).map { TrailEntity(trail: $0) }
}
}
extension TrailEntityQuery: EnumerableEntityQuery {
func allEntities() async throws -> [TrailEntity] {
return TrailDataManager.shared.trails.map { TrailEntity(trail: $0) }
}
}
- 어떠한 Entity들이 존재하는가? → allEntities()
- allEntities는 메모리에 알맞게 Query 프로토콜을 선택하여 사용하면 되는데, 현재 예시에선 그냥 전부 불러오는 EnumerableEntityQuery를 사용한다
- 어떤 Entity가 해당 id를 가졌는가? → entities(for:)
- 위처럼 엔티티를 불러왔을 때, 선택하면 다음과 같이 인텐트가 적용된다
Spotlight and Siri
[AppShortcut]를 제공하는 AppShortcutProvider를 통해 Spotlight와 Siri에서 기능을 제공한다
AppShortcutProvider
이전에 나왔던 AppIntent를 등록하면 다음과 같이 앱이 설치되기만 하면 Spotlight에서 볼 수 있다(Siri, ActionButton, ApplePencil에서도 사용가능)
IntentResult - ShowSnippetView
현재 실행 중인 앱에서 나가지 않으면서도 시리에서 뷰를 보려면, 다음과 같이 AppIntent의 IntentResult를 이용한다
struct GetPinnedTrail: AppIntent {
static let title: LocalizedStringResource = "Get Pinned Trail"
func perform() async throws -> some IntentResult & ProvidesDialog & ShowsSnippetView {
let pinned = TrailDataManager.shared.pinned
return .result(
dialog: """
The latest reported condition of \(pinned.name) is \(pinned.currentConditions)
""",
view: trailConditionsSnippetView()
)
}
}
위와같이 ProvidesDialog를 통해 dialog를 전달할 수 있고, ShowSnippetView를 통해 view를 전달할 수 있다
이 결과는 아래와 같이 시리에 통합되어 보인다
마무리
정리해보면 App Intents는 앱의 기능을 외부에서 사용하는데 공통적인 인터페이스라고 볼 수 있으며
핵심 요소인 Intents는 동사, Entities는 명사, App shortcuts는 문장의 관계로 비유할 수 있다
App shortcuts은 사용자가 단축어앱에서 Intents와 Entities를 통해 직접 등록할 수도 있고, 개발자가 AppShortcutProvider으로 제공할 수도 있다
AppEntity는 EntityQuery를 가지는데, 전체 AppEntity를 불러오는 구현과 id를 바탕으로 AppEntity를 선택하는 구현이 필요하다
AppIntent는 IntentResult를 반환하는데, ShowSnippetView도 채택하여 perform()을 구현하면,
시리에서 뷰를 전달하여, 현재 실행 중인 앱을 나가지 않고도 해당 뷰를 볼 수 있게 해 준다
만약 해당 내용들을 사용해 보고 익숙해지면, 새롭게 업데이트된 내용을 공부할 예정이다
'iOS+' 카테고리의 다른 글
EventKit (0) | 2024.08.03 |
---|---|
MapKit (0) | 2024.07.27 |
AVFoundation (0) | 2024.07.14 |
상반기 후기와 후반기 계획 (0) | 2024.06.29 |
Unit Test (0) | 2024.06.22 |