28. Apr 2025iOS

App Intents tipy a triky: Jak udělat aplikace pro iOS přístupnější než kdy dřív

App Intents je rámec představený v iOS 16, který usnadňuje vytváření zkratek, automatizací a hlasových příkazů pro Siri v našich aplikacích. Umožňuje zlepšit způsob, jakým uživatelé pracují s vaší aplikací, aniž by ji museli otevřít.

Lukáš KubaliakiOS Developer
Štylizovaná postava s logom GoodRequest namiesto hlavy skúma App Intents ikonu s lupou. Za ikonou sa nachádza úryvok kódu na tmavej obrazovke, všetko na jasne modrom pozadí.

🚀 Proč používat App Intents?

  • Akce dostupné přes Siri, Spotlight a Shortcuts.
  • Zjednodušené vytváření vlastních zkratek.
  • Možnost použití ve Widgetech.
Ikona App Intents je obklopena čtyřmi označenými funkcemi. Na bílém pozadí jsou „Widgety“, „Zkratky“, ‚Siri‘ a „Spotlight“, každá se stručným popisem vysvětlujícím její funkci.

Otevření aplikace

Abychom si ukázali, jak pracovat s App Intents, implementujeme jednoduchý intent pro otevření aplikace. Tento intent bude možné spustit pomocí Siri, Shortcuts nebo Spotlightu, což uživatelům umožní rychlý přístup k aplikaci a jejím klíčovým funkcím. Jdeme na to!

Nejprve vytvoříme vlastní intent tím, že implementujeme protokol AppIntent.

import AppIntents

struct OpenApp: AppIntent {
    
    static let title = LocalizedStringResource(stringLiteral: "Open app")
    static let description = IntentDescription(stringLiteral: "This intent opens the app")
    
    static let openAppWhenRun: Bool = true

    func perform() async throws -> some IntentResult {
        return .result()
    }

}

Title je čitelný název intentu, který se zobrazí v Shortcuts. Používáme LocalizedStringResource, který usnadňuje lokalizaci názvu do více jazyků. Stejně jako title, i description pomáhá uživatelům pochopit, k čemu intent slouží.

Pokud chceme zajistit, aby se aplikace po spuštění intentu skutečně otevřela, musíme nastavit proměnnou openAppWhenRun na hodnotu true.

Nakonec se dostáváme k samotnému jádru našeho intentu, kterým je funkce perform(). Logika v ní obsažená se provede po spuštění intentu a vrátí funkci IntentResult. V tomto případě ji můžeme nechat prázdnou, protože se nám otevře aplikace, a to bylo cílem této akce.

V tomto okamžiku je náš intentu na otevření aplikace viditelný a dostupný uživatelům v Shortcuts. Odtud ji mohou spustit, přidat na domovskou obrazovku nebo vytvořit vlastní automatizaci.

Abychom mohli ještě více zpřístupnit funkce naší aplikace, konkrétně aby se mohla zobrazovat ve vyhledávání Spotlight a spouštět hlasovými příkazy prostřednictvím Siri, musíme intent zaregistrovat do služby AppShortcutsProvider. To můžeme udělat jednoduše následujícím způsobem:

struct ShortcutsProvider: AppShortcutsProvider {
    
    @AppShortcutsBuilder
    static var appShortcuts: [AppShortcut] {
        AppShortcut(
            intent: OpenApp(),
            phrases: [
                "Open \(.applicationName)",
                "Open \(.applicationName) app",
                "Launch \(.applicationName)",
                "Start \(.applicationName)",
                "Run \(.applicationName)",
            ],
            shortTitle: "Opens app",
            systemImageName: "square.stack"
        )
    }
    
}

Tímto způsobem také definujeme příkladové fráze, které se spustí prostřednictvím Siri. Definováním řady různých frází umožňujeme uživateli širší a flexibilnější způsob interakce s aplikací.

Vyhledávání v aplikaci

Velmi užitečným příkladem je implementace in-app search intentu. Zaměřuje se na vyhledávání, což je funkce, kterou dnes v různých podobách nabízí většina aplikací. Při spuštění takového záměru si aplikace vyžádá vstupní parametr - výraz, který má být vyhledán. Systém pak čeká, až uživatel tento výraz zadá. Po jeho zadání se aplikace otevře ve stavu vyhledávání, již předvyplněná výrazem, který uživatel zadal.

Tento proces umožňuje rychlejší a efektivnější využití vyhledávací funkce aplikace, a tím zlepšuje uživatelský komfort.

struct SearchInAppIntent: ShowInAppSearchResultsIntent {
    
    static let title = LocalizedStringResource(stringLiteral: "Search in app")
    static let searchScopes: [StringSearchScope] = [.general]
    
    @Parameter(
        title: "Search term",
        description: "Enter a keyword to search",
        requestValueDialog: .init("What would you like to search for?")
    )
    var criteria: StringSearchCriteria
    
    func perform() async throws -> some IntentResult {
        await coordinator?.search(for: criteria.term)

        return .result()
    }
    
}

Interaktivní widgety

Interaktivní widgety jsou skvělým nástrojem pro zlepšení uživatelské zkušenosti a zvýšení zapojení do aplikací. Widgety umožňují uživatelům rychlý přístup k důležitým funkcím aplikace přímo z domovské obrazovky, aniž by museli otevírat samotnou aplikaci. Interaktivní widgety nejen šetří čas, ale také zvyšují pohodlí při každodenním používání aplikace, což přispívá k vyšší spokojenosti uživatelů a častějšímu zapojení do aplikace.

Příklad jednoduché implementace počítadla může vypadat následovně:

struct CountEntry: TimelineEntry {
    
    let date: Date
    let count: Int
    
}

Nejprve vytvoříme položku CountryEntry (slouží jako datový model pro widget). date je povinný parametr, podle kterého WidgetKit určuje zobrazení dat, a parametr count uchovává hodnotu počítadla, které se ve widgetu zobrazuje.

struct Provider: TimelineProvider {
    
    private var actualCount: Int { SharedStorage().count() }
    
    func placeholder(in context: Context) -> CountEntry {
        CountEntry(date: Date(), count: actualCount)
    }

    func getSnapshot(in context: Context, completion: @escaping (CountEntry) -> ()) {
        completion(CountEntry(date: Date(), count: actualCount))
    }

    func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
        let timeline = Timeline(
            entries: [CountEntry(date: Date(), count: actualCount)],
            policy: .atEnd
        )
        completion(timeline)
    }

}

TimelineProvider je zodpovědný za poskytování aktuálních a budoucích dat, která budou použita ve widgetu.

  • placeholder: Slouží jako náhled při přidávání widgetu v editoru a poskytuje obsah, zatímco se načítají skutečná data.
  • getSnapshot: Slouží k poskytnutí okamžitého stavu widgetu.
  • getTimeLine: Tato funkce slouží k poskytování dat v aktuálním čase a nabízí možnost poskytovat data v budoucnosti. V tomto konkrétním případě nemá smysl definovat budoucí data, protože pro zobrazení stavu počítadla používáme statickou hodnotu. Pokud by se hodnota měnila předvídatelným způsobem (časovač, odpočet, ...), mohli bychom budoucí data předvídat a poskytnout je v této funkci.
struct counterEntryView : View {
    
    var entry: Provider.Entry
    
    var body: some View {
        VStack {
            Text("\(entry.count)")
                .font(.title)
                .fontWeight(.bold)
                .contentTransition(.numericText())

            if #available(iOS 17.0, *) {
                Button(
                    intent: IncrementCounterIntent(),
                    label: { Text("Tap me!") }
                )
            }
        }

    }
    
}

V posledním kroku vytváříme uživatelské rozhraní widgetu, které zobrazuje počet (stav počítadla) a obsahuje interaktivní tlačítko (minimální verze iOS je 17.0). Takové tlačítko je schopno spustit akci, kterou definujeme jako App Intent. Daná akce a za ní implementovaná logika se spustí a provede bez nutnosti otevřít aplikaci.

Implementace akce IncementCounterIntent() je jednoduchá a jejím jediným úkolem je inkrementovat počítadlo uložené v lokální paměti zařízení.

struct IncrementCounterIntent: AppIntent {
    
    static var title: LocalizedStringResource { "Counter title" }
    static var description: IntentDescription { "Counter description" }
    
    func perform() async throws -> some IntentResult {
        SharedStorage().increment()
        
        return .result()
    }
    
}

Ovládací centrum

Control widgety jsou výkonným nástrojem, který uživatelům umožňuje snadno ovládat různé funkce aplikací přímo z Ovládacího centra nebo dokonce ze zamčené obrazovky.

@available(iOSApplicationExtension 18.0, *)
struct OpenAppControl: ControlWidget {
    
    let kind: String = "openAppControl"
    
    var body: some ControlWidgetConfiguration {
        
        StaticControlConfiguration(
            kind: kind,
            content: {
                ControlWidgetButton(
                    action: OpenApp(),
                    label: { Label("Open App", systemImage: "square.stack") }
                )
            }
        )
        
    }
    
}

Control widgety jsou k dispozici až od verze iOS 18 a vyšší. kind je jedinečný identifikátor tohoto control widgetu. Samotné tělo widgetu obsahuje konfiguraci se statickým obsahem - v tomto případě tlačítko, které provede akci (intent) OpenApp(), kterou jsme již implementovali výše.

Takto implementovaný Control widget nám umožňuje snadno přidat akci do Ovládacího centra nebo přímo na zamykací obrazovku. Tento přístup zvyšuje pohodlí a efektivitu, protože umožňuje provádět akce přímo z Ovládacího centra nebo ze zamykací obrazovky.

✅ Shrnutí

App Intents jsou výkonným nástrojem pro integraci aplikací se Siri, Shortcuts a Spotlight. Jejich implementace je jednoduchá a umožňují intuitivní ovládání aplikací hlasem nebo automatizací. Pokud chcete zlepšit uživatelský zážitek ve své aplikaci, App Intents jsou rozhodně tou správnou cestou!

Zajímá vás, jak tyto funkce zlepšují obchodní výsledky? Přečtěte si více na našem business blogu!

Ilustrácia postavy s logom GoodRequest namiesto hlavy, ktorá sa naťahuje k App Intents ikone na tmavom pozadí.
BusinessiOS5 Mins reading

App Intents: Jak zpřístupnit aplikaci pomocí Siri, Spotlight a Widgetů

Lukáš Kubaliak28 Apr 2025
Lukáš KubaliakiOS Developer