5.9 KiB
5.9 KiB
id, title, category, status, source_trust_level, verification_status, created_at, updated_at, tags, tech_stack, applied_in, aliases
| id | title | category | status | source_trust_level | verification_status | created_at | updated_at | tags | tech_stack | applied_in | aliases | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| ios-app-intents-shortcuts | App Intents — Shortcuts / Siri / Spotlight | Coding | draft | B | conceptual | 2026-05-09 | 2026-05-09 |
|
|
|
App Intents (iOS 16+)
Siri / Shortcuts / Spotlight / Focus / Action button 을 한 번에. 선언형 Swift 코드만. iOS 18+ Apple Intelligence 가 자동 사용. 옛 SiriKit / NSUserActivity 보다 단순.
📖 핵심 개념
- AppIntent: 사용자가 시작 가능한 액션.
- AppEntity: 도메인 객체 (Note / Item / Recipe).
- AppShortcutsProvider: 앱 내장 shortcut 등록.
- Parameter: 사용자에게 물음 또는 자동.
💻 코드 패턴
AppEntity
import AppIntents
struct NoteEntity: AppEntity, Identifiable {
let id: UUID
let title: String
let body: String
static var typeDisplayRepresentation: TypeDisplayRepresentation = "Note"
var displayRepresentation: DisplayRepresentation {
DisplayRepresentation(title: "\(title)")
}
static var defaultQuery = NoteQuery()
}
struct NoteQuery: EntityQuery {
func entities(for ids: [UUID]) async throws -> [NoteEntity] {
Database.notes(ids: ids)
}
func suggestedEntities() async throws -> [NoteEntity] {
Database.recentNotes(limit: 5)
}
}
AppIntent
struct CreateNoteIntent: AppIntent {
static var title: LocalizedStringResource = "Create Note"
static var description = IntentDescription("Create a new note in the app.")
@Parameter(title: "Title")
var title: String
@Parameter(title: "Body", default: "")
var body: String
@MainActor
func perform() async throws -> some IntentResult & ReturnsValue<NoteEntity> {
let note = try await Database.create(title: title, body: body)
return .result(value: note)
}
}
AppShortcuts
struct AppShortcuts: AppShortcutsProvider {
static var appShortcuts: [AppShortcut] {
AppShortcut(
intent: CreateNoteIntent(),
phrases: [
"Create note in \(.applicationName)",
"New \(.applicationName) note",
],
shortTitle: "Create Note",
systemImageName: "square.and.pencil"
)
AppShortcut(
intent: SearchNotesIntent(),
phrases: ["Search \(.applicationName)"],
shortTitle: "Search",
systemImageName: "magnifyingglass"
)
}
}
Open App Intent
struct OpenNoteIntent: AppIntent {
static var title: LocalizedStringResource = "Open Note"
static var openAppWhenRun: Bool = true
@Parameter(title: "Note")
var note: NoteEntity
@MainActor
func perform() async throws -> some IntentResult {
Router.shared.navigate(to: .note(id: note.id))
return .result()
}
}
Spotlight (CoreSpotlight + AppEntity)
import CoreSpotlight
func indexNotes() async {
let notes = await Database.allNotes()
let items = notes.map { note -> CSSearchableItem in
let attrs = CSSearchableItemAttributeSet(contentType: .note)
attrs.title = note.title
attrs.contentDescription = note.body
return CSSearchableItem(uniqueIdentifier: note.id.uuidString, domainIdentifier: "notes", attributeSet: attrs)
}
try? await CSSearchableIndex.default().indexSearchableItems(items)
}
// Spotlight tap → handle
func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
if userActivity.activityType == CSSearchableItemActionType,
let id = userActivity.userInfo?[CSSearchableItemActivityIdentifier] as? String {
Router.shared.navigate(to: .note(id: UUID(uuidString: id)!))
}
}
Focus filter (집중 모드)
struct WorkFocusFilter: SetFocusFilterIntent {
static var title: LocalizedStringResource = "Work mode"
@Parameter(title: "Show only work projects")
var workOnly: Bool
func perform() async throws -> some IntentResult {
FocusManager.shared.setWorkOnly(workOnly)
return .result()
}
}
Action button (iPhone 15 Pro+)
- Settings → Action Button → Shortcut → 앱의 AppShortcut 선택.
Widget interactivity (iOS 17+)
struct CreateNoteButton: View {
var body: some View {
Button(intent: CreateNoteIntent(title: "Quick", body: "")) {
Image(systemName: "plus")
}
}
}
위젯에서 직접 intent 실행 — 앱 안 열림.
iOS 18 Apple Intelligence
- AppEntity + AppIntent 기반으로 Siri 가 똑똑하게 사용.
- AssistantSchema (예:
.system.intents.search) 가 새로움.
🤔 의사결정 기준
| 기능 | 사용 |
|---|---|
| Siri / Shortcuts | AppIntent + AppShortcutsProvider |
| Spotlight 검색 | CoreSpotlight + AppEntity |
| Widget 버튼 | Button(intent:) |
| Live Activity 인터랙션 | Same intent |
| Focus mode | SetFocusFilterIntent |
| Action button | AppShortcut |
❌ 안티패턴
- AppIntent perform 무거움: timeout. 짧게 + background work 따로.
- Phrases 가 너무 generic: "Search" 같은 거 — 다른 앱과 충돌.
- localized X: 다국어 사용자 무시. LocalizedStringResource.
- Open intent + 무 navigation: 앱만 열림 — 사용자 어색.
- Old SiriKit + AppIntent 둘 다: 혼란. 새로 = AppIntent.
- Spotlight 인덱스 한 번만: stale. 변경 시 update.
- EntityQuery suggestedEntities 없음: Shortcut UI 가 아무 거 없음.
🤖 LLM 활용 힌트
- AppEntity + AppIntent + AppShortcutsProvider 3종.
- Widget interactivity 도 같은 intent.
- iOS 18 Apple Intelligence 자동 활용.