Custom Siri Intents: מ-INExtension ל-AppIntents
משתמשים ממשיכים לפתוח את האפליקציה ידנית כי סירי לא מבינה את הפקודות שלהם. Custom Intents פותרים את זה: אתם מגדירים פקודות קוליות, סירי מתמללת פרמטרים ומבצעת את הפעולה ברקע. יישמנו מעל 30 פרויקטים עם SiriKit — אינטגרציה ממוצעת אורכת שבועיים. בניגוד ל-Shortcuts בסיסיים, Custom Intents תומכים בדיאלוגים להבהרת פרמטרים. זה מפחית שגיאות קלט ומשפר את שביעות הרצון. לפי נתונים פנימיים, יישום אינטנטים מעלה את תדירות השימוש באפליקציה ב-15–20%. לקוח אחד חסך $18k–26k בשנה על תמיכה בקוד ישן לאחר מעבר ל-AppIntents.
איך INExtension עובד בפועל
Parameter: date Type: Date (INDateComponentsResolutionResult) Display Name: "дата" Prompt: "На какое число?" Siri Dialog: "На какое число назначить встречу?" הוא תהליך שמופעל על ידי סירי כאשר נאמרת פקודה. הוא מכיל handlers עבור כל Intent שמוגדר ב-// Кастомный INObject class ProjectObject: INObject { // identifier и displayString обязательны } // В IntentHandler func provideProjectOptionsCollection( for intent: CreateTaskIntent, with completion: @escaping (INObjectCollection<ProjectObject>?, Error?) -> Void ) { let projects = ProjectRepository().fetchAll() let objects = projects.map { ProjectObject(identifier: $0.id, display: $0.name) } completion(INObjectCollection(items: objects), nil) } . Xcode מייצר קוד עבור מחלקות Intent, Response ופרמטרים. שקלו מבנה פרמטרים:
Parameter: date
Type: Date (INDateComponentsResolutionResult)
Display Name: "дата"
Prompt: "На какое число?"
Siri Dialog: "На какое число назначить встречу?"סוגי פרמטרים: func resolveTaskName(for intent: CreateTaskIntent, with completion: @escaping (INStringResolutionResult) -> Void) { guard let name = intent.taskName, name.count >= 2 else { completion(.needsValue()) // Siri: "Как назвать задачу?" return } guard name.count <= 255 else { completion(.unsupported(forReason: .tooLong)) return } completion(.success(with: name)) } , func confirm(intent: CreateTaskIntent, completion: @escaping (CreateTaskIntentResponse) -> Void) { guard ProjectRepository().exists(id: intent.project?.identifier) else { let response = CreateTaskIntentResponse(code: .failure, userActivity: nil) response.failureReason = "Проект не найден" completion(response) return } completion(CreateTaskIntentResponse(code: .ready, userActivity: nil)) } , func handle(intent: CreateTaskIntent, completion: @escaping (CreateTaskIntentResponse) -> Void) { let store = TaskStore(appGroup: "group.com.yourapp") let task = store.create( name: intent.taskName!, projectId: intent.project?.identifier, dueDate: intent.dueDate?.dateComponents ) let response = CreateTaskIntentResponse(code: .success, userActivity: nil) response.task = TaskObject(identifier: task.id, display: task.name) completion(response) } , Date, CLPlacemark (מיקום גיאוגרפי), INPerson (איש קשר מספר הטלפונים), סוגי INObject מותאמים אישית עבור ישויות האפליקציה שלכם. סירי מקבלת פרמטרים דרך INObject — סוג מותאם אישית שמתאר ישויות. ב-INExtension, הטמיעו provide…Options…Collection שמחזיר אובייקטים לבחירה.
בעיות שאנחנו פותרים
ההרחבה לא נקראת — הסיבה הנפוצה ביותר. ודאו שה-Intent נוסף ל-NSExtension.NSExtensionAttributes.IntentsSupported ב-Info.plist של ההרחבה, וגם ביעד הראשי. App Group לא משותף — ההרחבה והאפליקציה נמצאים ב-sandboxes שונים. השתמשו ב-FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.com.yourapp") או ב-UserDefaults(suiteName: "group.com.yourapp"). סירי לא מבינה מונחים ספציפיים — השתמשו ב-INVocabulary.shared().setVocabularyStrings(projectNames, of: .organizationName) וב-AppIntentVocabulary.plist גלובלי עבור מונחים סטטיים.
איך לפתור בעיות זיהוי מונחים של סירי
השתמשו ב-INVocabulary וב-AppIntentVocabulary.plist כדי לרשום שמות של פרויקטים, משימות או ישויות אחרות. זה מאפשר לסירי לזהות אותם בקול ללא הבהרות נוספות. בפרויקט אחד, הוספנו מעל 200 מונחים, ודיוק הזיהוי עלה מ-70% ל-95%.
איך אנחנו מפתחים Custom Intents
השלב הראשון הוא יצירת קובץ .intentdefinition עם פרמטרים ודיאלוגים. לאחר מכן אנו מייצרים מחלקות Swift וכותבים handlers:
// Кастомный INObject
class ProjectObject: INObject {
// identifier и displayString обязательны
}
// В IntentHandler
func provideProjectOptionsCollection(
for intent: CreateTaskIntent,
with completion: @escaping (INObjectCollection<ProjectObject>?, Error?) -> Void
) {
let projects = ProjectRepository().fetchAll()
let objects = projects.map { ProjectObject(identifier: $0.id, display: $0.name) }
completion(INObjectCollection(items: objects), nil)
}Resolve, Confirm, Handle — שלוש שיטות מחזור חיים: Resolve — מאמת ומבהיר כל פרמטר:
func resolveTaskName(for intent: CreateTaskIntent, with completion: @escaping (INStringResolutionResult) -> Void) {
guard let name = intent.taskName, name.count >= 2 else {
completion(.needsValue()) // Siri: "Как назвать задачу?"
return
}
guard name.count <= 255 else {
completion(.unsupported(forReason: .tooLong))
return
}
completion(.success(with: name))
}Confirm — בדיקה סופית:
func confirm(intent: CreateTaskIntent, completion: @escaping (CreateTaskIntentResponse) -> Void) {
guard ProjectRepository().exists(id: intent.project?.identifier) else {
let response = CreateTaskIntentResponse(code: .failure, userActivity: nil)
response.failureReason = "Проект не найден"
completion(response)
return
}
completion(CreateTaskIntentResponse(code: .ready, userActivity: nil))
}Handle — ביצוע:
func handle(intent: CreateTaskIntent, completion: @escaping (CreateTaskIntentResponse) -> Void) {
let store = TaskStore(appGroup: "group.com.yourapp")
let task = store.create(
name: intent.taskName!,
projectId: intent.project?.identifier,
dueDate: intent.dueDate?.dateComponents
)
let response = CreateTaskIntentResponse(code: .success, userActivity: nil)
response.task = TaskObject(identifier: task.id, display: task.name)
completion(response)
}תבניות תגובה מוגדרות ב-.intentdefinition: סירי תאמר "Task $(taskName) created in project $(project)".
תהליך
- אנליטיקה — לימוד הלוגיקה העסקית והגדרת פקודות.
- עיצוב — יצירת
.intentdefinitionעם פרמטרים ודיאלוגים. - יישום — כתיבת handlers,
INObject, אינטגרציה עם App Group ו-INVocabulary. - בדיקות — הדמיית סירי על מכשיר אמיתי, בדיקת כל תרחישי הכשל.
- השקה — הכנה ל-App Store Review, עדכון קבצי קונפיגורציה במידת הצורך.
מה כלול
-
קובץ
.intentdefinitionעם פרמטרים ודיאלוגים. -
INExtensionעם מחזור resolve/confirm/handle מלא. - הגדרת App Group עבור מסד נתונים משותף.
- אינטגרציה עם
INVocabularyלזיהוי קולי. - (אופציונלי) מעבר ל-AppIntents עבור גרסאות iOS עדכניות.
- תיעוד אינטגרציה.
אפשרויות נוספות
- אינטגרציה עם AppIntents עבור iOS 16+
- תמיכה ב-Shortcuts עם AppEnum ו-AppEntity
- לוקליזציה של דיאלוגים של סירי למספר שפות
לוחות זמנים משוערים
| משימה | לוח זמנים |
|---|---|
| Intent מותאם אישית אחד (פרמטרים פשוטים) | 1–2 ימים |
| Intent עם INObject מותאם אישית + דיאלוג resolve | 2–3 ימים |
| מעבר ל-AppIntents + הגדרת widget | 3–5 ימים |
השוואת SiriKit לעומת AppIntents
| פרמטר | SiriKit | AppIntents |
|---|---|---|
| נפח קוד לכל Intent | עד 150 שורות | כ-40 שורות |
| תמיכה ב-async/await | לא | כן |
| תאימות ל-Widget | חלקית | מלאה |
| צורך ב-.intentdefinition | נדרש | לא נדרש |
AppIntents (גרסאות iOS עדכניות) משתמש ב-async/await, הוא תמציתי יותר, תומך ב-SwiftUI ועובד ב-widgets. קוד ישן דורש עד 150 שורות לכל Intent, בעוד קוד חדש אורך כ-40. AppIntents מהיר פי 2–3 ליישום וקל יותר לתחזוקה. אם האפליקציה שלכם כבר משתמשת בתבניות מודרניות, AppIntents הוא הבחירה הברורה. פרטים נוספים: AppIntents.
קבלו ייעוץ ממפתח מנוסה כדי להעריך את הפרויקט שלכם. צרו קשר כדי לדון בפרטים ולהזמין ביקורת על פתרון ה-SiriKit הנוכחי שלכם. הזמינו פיתוח Custom Intents — אנו מבטיחים אישור App Store Review ותמיכה מלאה באינטגרציה.







