๐ฃ๏ธ Siri ์์ฑ ์ ์ด ์ฑ ๋ง๋ค๊ธฐ
App Intents๋ก "ํ ์ผ ์ถ๊ฐํด์ค"๋ผ๊ณ ๋งํ๋ฉด ๋์ํ๋ ์ฑ์ ๋ง๋ญ๋๋ค.
โจ App Intents๋?
App Intents๋ ์ฑ์ ๊ธฐ๋ฅ์ Siri, ๋จ์ถ์ด, Spotlight์ ๋ ธ์ถํฉ๋๋ค. iOS 16+์์ ์ฌ์ฉ ๊ฐ๋ฅํ๋ฉฐ, ์ด์ ์ SiriKit๋ณด๋ค ํจ์ฌ ๊ฐ๋จํฉ๋๋ค.
๐ ์ฒซ ๋ฒ์งธ Intent ๋ง๋ค๊ธฐ
import AppIntents struct AddTodoIntent: AppIntent { static var title: LocalizedStringResource = "ํ ์ผ ์ถ๊ฐ" @Parameter(title: "์ ๋ชฉ") var todoTitle: String func perform() async throws -> some IntentResult { TodoStore.shared.add(title: todoTitle) return .result(dialog: "\(todoTitle) ์ถ๊ฐ ์๋ฃ!") } }
๐ค Siri ์์ฑ ๋ช ๋ น ๋ฑ๋ก
struct TodoShortcuts: AppShortcutsProvider { static var appShortcuts: [AppShortcut] { AppShortcut( intent: AddTodoIntent(), phrases: [ "ํ ์ผ ์ถ๊ฐํด์ค \(.applicationName)", "\(.applicationName)์ ํ ์ผ ์ถ๊ฐ" ], shortTitle: "ํ ์ผ ์ถ๊ฐ", systemImageName: "plus.circle" ) } }
๐ก HIG ํ
์์ฐ์ค๋ฌ์ด ๋ฌธ์ฅ์ผ๋ก phrases๋ฅผ ์์ฑํ์ธ์. "ํ ์ผ ์ถ๊ฐํด์ค", "์ ํ ์ผ ๋ง๋ค์ด์ค" ์ฒ๋ผ ์ฌ์ฉ์๊ฐ ์ค์ ๋ก ๋งํ ๋ฒํ ํํ์ ์ฌ์ฉํฉ๋๋ค.
๐ ํ๋ผ๋ฏธํฐ ๋ํ ํ๋ฆ
ํ๋ผ๋ฏธํฐ๊ฐ ์์ผ๋ฉด Siri๊ฐ ์๋์ผ๋ก ๋ฌผ์ด๋ด ๋๋ค:
guard let title = todoTitle else { throw $todoTitle.needsValueError("์ด๋ค ํ ์ผ์ ์ถ๊ฐํ ๊น์?") }
๐ Entity ์ ์
Todo ํญ๋ชฉ์ Entity๋ก ์ ์ํ๋ฉด Siri๊ฐ ํ ์ผ์ ์ ์ํ๊ณ ๊ฒ์ํ ์ ์์ต๋๋ค:
struct TodoEntity: AppEntity { static var typeDisplayRepresentation: TypeDisplayRepresentation { "ํ ์ผ" } var id: UUID var title: String var isCompleted: Bool var displayRepresentation: DisplayRepresentation { DisplayRepresentation(title: "\(title)") } // Spotlight ๊ฒ์ ์ง์ static var defaultQuery = TodoEntityQuery() }
๐ EntityQuery ๊ตฌํ
struct TodoEntityQuery: EntityQuery { func entities(for identifiers: [UUID]) async throws -> [TodoEntity] { TodoStore.shared.todos.filter { identifiers.contains($0.id) } } func suggestedEntities() async throws -> [TodoEntity] { // ๋ฏธ์๋ฃ ํญ๋ชฉ 3๊ฐ ์ ์ TodoStore.shared.todos .filter { !$0.isCompleted } .prefix(3) .map(TodoEntity.init) } }
๐ฑ SwiftUI ํตํฉ
AppIntentsUI๋ฅผ ์ฌ์ฉํด Siri ์๋ต์ SwiftUI๋ก ๊พธ๋ฐ ์ ์์ต๋๋ค:
import AppIntentsUI extension AddTodoIntent { @MainActor func perform() async throws -> some IntentResult & ProvidesDialog & ShowsSnippetView { let todo = TodoStore.shared.add(title: todoTitle) return .result( dialog: "\(todoTitle) ์ถ๊ฐํ์ด์!", view: TodoSnippetView(todo: todo) ) } } struct TodoSnippetView: View { let todo: TodoEntity var body: some View { HStack { Image(systemName: "checkmark.circle") Text(todo.title) } .padding() } }
๐ก ํ ์คํธ ๋ฐฉ๋ฒ
Xcode์์ ์คํ ํ Siri์๊ฒ "ํ ์ผ ์ถ๊ฐํด์ค [์ฑ ์ด๋ฆ]"์ด๋ผ๊ณ ๋งํด๋ณด์ธ์. ์๋ฎฌ๋ ์ดํฐ์์๋ Hardware โ Siri๋ก ํ ์คํธํ ์ ์์ต๋๋ค.