Ana Sayfa / Blog / App Intents ve Shortcuts: kullanıcıya doğrudan eylem

App Intents ve Shortcuts: kullanıcıya doğrudan eylem

App Intents’i 3 projede uyguladım. Doğru tasarlandığında kullanıcının uygulamayı açmadan işini görmesini sağlıyor; Shortcuts app, Siri, Spotlight ve hatta Focus Mode filter’ları üzerinden tetiklenebiliyor. Yanlış tasarlandığında ise görünmez bir özellik olarak kalıyor. Temel bir intent: görev ekleme import AppIntents struct AddTaskIntent: AppIntent { static var title: LocalizedStringResource = "Görev Ekle" static var description = IntentDescription("Listeye […]

App Intents’i 3 projede uyguladım. Doğru tasarlandığında kullanıcının uygulamayı açmadan işini görmesini sağlıyor; Shortcuts app, Siri, Spotlight ve hatta Focus Mode filter’ları üzerinden tetiklenebiliyor. Yanlış tasarlandığında ise görünmez bir özellik olarak kalıyor.

Temel bir intent: görev ekleme

import AppIntents

struct AddTaskIntent: AppIntent {
    static var title: LocalizedStringResource = "Görev Ekle"
    static var description = IntentDescription("Listeye hızlı görev ekle")
    @Parameter(title: "Görev") var text: String
    func perform() async throws -> some IntentResult & ProvidesDialog {
        try await TaskStore.shared.add(text)
        return .result(dialog: "Eklendi: (text)")
    }
}

Bu kadar basit bir intent bile Shortcuts app’te görünüyor ve Siri ile “uygulama adı, görev ekle X” diyerek tetikleniyor. Ek Info.plist veya storyboard gerekmiyor.

AppShortcutsProvider: görünürlük için şart

Intent’i tanımlamak yetmiyor; kullanıcının keşfedebilmesi için AppShortcutsProvider gerekiyor:

struct MyAppShortcuts: AppShortcutsProvider {
    static var appShortcuts: [AppShortcut] {
        AppShortcut(
            intent: AddTaskIntent(),
            phrases: [
                "(.applicationName) ile görev ekle",
                "(.applicationName)'a görev ekle"
            ],
            shortTitle: "Görev Ekle",
            systemImageName: "plus.circle"
        )
    }
}

Phrases kısmı kritik; Türkçe ekli-ek kurallarına dikkat etmek gerekiyor. applicationName placeholder’ı app ismini koyuyor; örneğin uygulama adı “Not” ise “Not ile görev ekle” çıkıyor.

Parametrize intent

Bir görevin önceliğini parametre olarak almak istersen:

enum Priority: String, AppEnum {
    case low, medium, high
    static var typeDisplayRepresentation = TypeDisplayRepresentation(name: "Öncelik")
    static var caseDisplayRepresentations: [Priority: DisplayRepresentation] = [
        .low: "Düşük", .medium: "Orta", .high: "Yüksek"
    ]
}

struct AddTaskIntent: AppIntent {
    @Parameter(title: "Görev") var text: String
    @Parameter(title: "Öncelik") var priority: Priority
    func perform() async throws -> some IntentResult {
        try await TaskStore.shared.add(text, priority: priority)
        return .result()
    }
}

Shortcuts app parametreyi dropdown olarak gösteriyor. Siri çağrısında parametre eksikse kullanıcıya soru soruyor.

EntityQuery: entity’lerle çalışma

“Görevi tamamla” intent’i için hangi görevi seçeceğini kullanıcıya sorman gerekiyor. Bunu AppEntity ve EntityQuery ile yapıyorsun:

struct TaskEntity: AppEntity {
    static var typeDisplayRepresentation = TypeDisplayRepresentation(name: "Görev")
    static var defaultQuery = TaskQuery()
    let id: UUID
    let title: String
    var displayRepresentation: DisplayRepresentation {
        DisplayRepresentation(title: "(title)")
    }
}

struct TaskQuery: EntityQuery {
    func entities(for identifiers: [UUID]) async throws -> [TaskEntity] {
        try await TaskStore.shared.fetch(ids: identifiers)
    }
    func suggestedEntities() async throws -> [TaskEntity] {
        try await TaskStore.shared.fetchOpen(limit: 10)
    }
}

Focus Filter: projede kullandığım senaryo

Çalışma Focus’unda sadece iş görevleri görünsün; Kişisel Focus’ta kişisel olanlar. SetFocusFilterIntent ile kullanıcı Focus Mode ayarında uygulama filter’ımı görüyor:

struct WorkFocusFilter: SetFocusFilterIntent {
    static var title: LocalizedStringResource = "Çalışma Görevleri"
    @Parameter(title: "Tag") var tag: String
    func perform() async throws -> some IntentResult {
        UserDefaults.standard.set(tag, forKey: "focusTag")
        NotificationCenter.default.post(name: .focusChanged, object: nil)
        return .result()
    }
}

Ana uygulama açıldığında focusTag‘i okuyup listeyi filtreliyor.

Interactive widget ile entegre

iOS 17 widget’larında button ve toggle, App Intent’i direkt tetikleyebiliyor. Benim todo widget’ımda checkbox tıklandığında:

Button(intent: ToggleTaskIntent(taskId: task.id)) {
    Image(systemName: task.isDone ? "checkmark.circle.fill" : "circle")
}

Widget refresh otomatik oluyor; ayrıca timeline reload çağırmana gerek kalmıyor.

Pitfall: async’ten dialog dönmek

Intent async işlem sırasında dialog göstermek istiyorsan ProvidesDialog trait’ini ekle ve .result(dialog:) döndür. Sadece .result() döndürürsen Siri sessiz kalıyor, kullanıcı ne oldu anlamıyor.

Localization

LocalizedStringResource kullandığın her yerde String Catalog (.xcstrings) entegrasyonu otomatik yapılıyor. Ama phrase’lerdeki interpolation parametreleri için ekstra özen lazım; Türkçe’nin ek yapısı İngilizce sabit kalıp varsayımını bozuyor.

Son tavsiye

3 iyi intent, 30 eksik intent’ten değerli. Kullanıcıların sana 3 hızlı aksiyonla erişmesi, uygulamayı günlük kullanımına oturtmaya yetiyor. Silip takılmayan özellik böyle çıkıyor.

Bu konuda bir projeniz mi var?

Kısa bir özet bırakın, 24 saat içinde size dönüş yapayım.

İletişime Geç