ybkuanysh

Что такое AppIntents?

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

Наконец сделаем наше действие доступным для 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, она попросит произнести текст для параметра и наконец откроет экран с переданным значением.