App Intents — это современный фреймворк от Apple, который позволяет разработчикам делать функциональность своих приложений доступной за пределами самого приложения: через Siri, Shortcuts, Spotlight, виджеты и другие системные возможности iOS, macOS и watchOS.
По сути, App Intent — это описание конкретного действия, которое может выполнить ваше приложение. Вместо того чтобы пользователь открывал приложение и вручную выполнял серию действий, он может вызвать это действие голосом, добавить в автоматизацию или запустить через виджет.
Для начала напишем простое "действие". Объявляем структуру, реализующую AppIntent. Нам необходимо дать название действию и добавить функцию самого действия.
// TestIntent.swift
import Foundation
import AppIntents
struct TestIntent: AppIntent {
static var title: LocalizedStringResource = "no title"
func perform() async throws -> some IntentResult {
return .result()
}
}Собрав и установив приложение, данное действие уже станет доступно в Shortcuts для использования в своей команде. Однако если мы создадим в Shortcuts команду с нашим действием, то очевидно ничего не произойдет. Команда просто завершит выполнение.
Идем далее и заставим команду открывать наше приложение для выполнения действия Intent. Для этого определяем переменную openAppWhenRun.
// TestIntent.swift
import Foundation
import AppIntents
struct TestIntent: AppIntent {
static var title: LocalizedStringResource = "no title"
static var openAppWhenRun: Bool = true
func perform() async throws -> some IntentResult {
print("test")
return .result()
}
}Теперь при выполнении команды, откроется главный экран приложения.
Теперь попробуем передать нашему действию строку и показать ее на экране приложения. Для этого пишем простой @Observable класс.
// LibraryManager.swift
import Foundation
@Observable
final class LibraryManager {
var helloText: String?
}Чтобы мы могли использовать наш менеджер, apple предоставляет свою примитивную реализацию Dependecy Injection, который лежит в том же пакете AppIntents. С помощью него, мы регистрируем наш объект на старте приложения. Также передаем вверх объект вверх по дереву с помощью environment.
// IntentsLessonApp.swift
import SwiftUI
import AppIntents
@main
struct IntentsLessonApp: App {
let library: LibraryManager
init() {
let library = LibraryManager()
AppDependencyManager.shared.add(dependency: library)
self.library = library
}
var body: some Scene {
WindowGroup {
ContentView()
}
.environment(library)
}
}Параметр мы получаем с помощью @Parameter. Так как мы зарегистрировали наш менеджер в контейнере, мы можем его инжектировать в действие и передать значение параметра. Раз менеджер @Observable, то мы записываем значение в MainActor.
// OpenHelloWorld.swift
import AppIntents
struct OpenHelloWorld: AppIntent {
static var title: LocalizedStringResource = "Open Hello World"
static var openAppWhenRun: Bool = true
@Parameter(title: "hello text")
var helloText: String
@Dependency
var library: LibraryManager
func perform() async throws -> some IntentResult {
await MainActor.run {
library.helloText = helloText
}
return .result()
}
}Получаем наш менеджер из Environment и добавляем вывод значения на экран.
// ContentView.swift
import SwiftUI
struct ContentView: View {
@Environment(LibraryManager.self) private var library
var body: some View {
VStack {
Image(systemName: "globe")
.imageScale(.large)
.foregroundStyle(.tint)
Text(library.helloText ?? "Hello, world!")
}
.padding()
}
}
#Preview {
ContentView()
}Теперь при запуске команды, будет появляться окно ввода текста.
Наконец сделаем наше действие доступным для Siri. Для этого нам необходимо написать свой Shortcut. Определяем структуру реализующую протокол AppShortcutsProvider, в которой мы создаем нашу команду. Также мы здесь описываем текст для вызова нашей команды. Для примера я также добавил возможность выполнения для Siri на русском и японском языках (Еще будет полезно добавить локализованное название приложения)
// ShortcutsProvider.swift
import Foundation
import AppIntents
struct LessonShortcuts: AppShortcutsProvider {
static var appShortcuts: [AppShortcut] {
AppShortcut(
intent: OpenHelloWorld(),
phrases: [
"Show hello world in \(.applicationName)",
"\(.applicationName)で挨拶を見せて",
"Покажи приветствие в \(.applicationName)",
],
shortTitle: "Show",
systemImageName: "person.and.background.dotted"
)
}
}Теперь сказав одну из этих фраз Siri, она попросит произнести текст для параметра и наконец откроет экран с переданным значением.