هل تناسب نوايا SiriKit تطبيقك؟ إذا كان الأمر كذلك ، فإليك كيفية استخدامها
نشرت: 2022-03-10منذ iOS 5 ، ساعد Siri مستخدمي iPhone في إرسال الرسائل وتعيين التذكيرات والبحث عن المطاعم باستخدام تطبيقات Apple. بدءًا من iOS 10 ، تمكنا من استخدام Siri في بعض تطبيقاتنا أيضًا.
من أجل استخدام هذه الوظيفة ، يجب أن يتلاءم تطبيقك مع "مجالات ونوايا" Siri المحددة مسبقًا من Apple. في هذه المقالة ، سنتعرف على ماهيتها ونرى ما إذا كان بإمكان تطبيقاتنا استخدامها. سنأخذ تطبيقًا بسيطًا هو مدير قائمة المهام ونتعلم كيفية إضافة دعم Siri. سننتقل أيضًا إلى إرشادات موقع ويب مطور Apple بشأن التكوين ورمز Swift لنوع جديد من الامتدادات تم تقديمه مع SiriKit: ملحق Intents .
عندما تصل إلى جزء الترميز من هذه المقالة ، ستحتاج إلى Xcode (على الأقل الإصدار 9.x) ، وسيكون من الجيد إذا كنت معتادًا على تطوير iOS في Swift لأننا سنضيف Siri إلى عمل صغير تطبيق. سننتقل خلال خطوات إعداد امتداد على موقع مطور Apple وإضافة رمز امتداد Siri إلى التطبيق.
"يا Siri ، لماذا أحتاجك؟"
أحيانًا أستخدم هاتفي أثناء الجلوس على أريكتي ، بدون استخدام يدي ، ويمكنني أن أعطي الشاشة انتباهي الكامل. ربما سأرسل رسالة نصية إلى أختي للتخطيط لعيد ميلاد والدتنا أو الرد على سؤال في Trello. يمكنني رؤية التطبيق. يمكنني النقر على الشاشة. أستطيع أن أطبع.
لكن ربما أتجول في مدينتي ، أستمع إلى بودكاست ، عندما يأتي نص على ساعتي. هاتفي في جيبي ، ولا يمكنني الرد بسهولة أثناء المشي.
باستخدام Siri ، يمكنني الضغط باستمرار على زر التحكم في سماعة الرأس والقول ، "أرسل رسالة لأختي بأنني سأكون هناك بحلول الساعة الثانية." يعد Siri رائعًا أثناء التنقل ولا يمكنه الاهتمام الكامل بهاتفك أو عندما يكون التفاعل بسيطًا ، ولكنه يتطلب عدة نقرات ومجموعة من الكتابة.
هذا جيد إذا كنت أرغب في استخدام تطبيقات Apple لهذه التفاعلات. لكن بعض فئات التطبيقات ، مثل المراسلة ، لها بدائل شائعة جدًا. الأنشطة الأخرى ، مثل حجز رحلة أو حجز طاولة في مطعم ، ليست ممكنة حتى مع تطبيقات Apple المدمجة ولكنها مثالية لـ Siri.
نهج Apple في المساعدين الصوتيين
لتمكين Siri في تطبيقات الطرف الثالث ، كان على Apple أن تقرر آلية لأخذ الصوت من صوت المستخدم ونقله بطريقة ما إلى التطبيق بطريقة تمكنه من تلبية الطلب. لجعل هذا ممكنًا ، تطلب Apple من المستخدم ذكر اسم التطبيق في الطلب ، لكن كان لديهم عدة خيارات لما يجب فعله مع بقية الطلب.
- ربما يكون قد أرسل ملفًا صوتيًا إلى التطبيق.
تكمن فائدة هذا الأسلوب في أن التطبيق قد يحاول معالجة أي طلب قد يطلبه المستخدم حرفيًا. ربما أحب Amazon أو Google هذا الأسلوب لأن لديهم بالفعل خدمات التعرف على الصوت المتطورة. لكن معظم التطبيقات لن تكون قادرة على التعامل مع هذا بسهولة بالغة. - كان بإمكانه تحويل الخطاب إلى نص وإرساله.
نظرًا لأن العديد من التطبيقات لا تحتوي على تطبيقات معقدة للغة الطبيعية ، فعادة ما يتعين على المستخدم الالتزام بعبارات معينة جدًا ، وسيكون الدعم بلغة غير اللغة الإنجليزية متروكًا لمطور التطبيق للتنفيذ. - ربما يطلب منك تقديم قائمة بالعبارات التي تفهمها.
هذه الآلية أقرب إلى ما تفعله أمازون مع Alexa (في إطار "المهارات") ، وهي تتيح استخدامات أليكسا أكثر بكثير مما يمكن لـ SiriKit التعامل معه حاليًا. في إحدى مهارات Alexa ، تقوم بتوفير عبارات بمتغيرات العناصر النائبة التي ستملأها Alexa نيابة عنك. على سبيل المثال ، "أليكسا ، ذكرني في$TIME$
إلى$REMINDER$
" - ستقوم Alexa بتشغيل هذه العبارة مقابل ما قاله المستخدم وإخبارك بقيمTIME
وREMINDER
. كما هو الحال مع الآلية السابقة ، يحتاج المطور إلى القيام بكل الترجمة ، ولا توجد مرونة كبيرة إذا قال المستخدم شيئًا مختلفًا قليلاً. - يمكنه تحديد قائمة الطلبات مع المعلمات وإرسال طلب منظم للتطبيق.
هذا هو ما تفعله Apple في الواقع ، والفائدة هي أنها يمكن أن تدعم مجموعة متنوعة من اللغات ، وتقوم بكل العمل لمحاولة فهم جميع الطرق التي يمكن للمستخدم من خلالها صياغة طلب ما. الجانب السلبي الكبير هو أنه يمكنك فقط تنفيذ معالجات للطلبات التي تحددها Apple. يعد هذا أمرًا رائعًا إذا كان لديك ، على سبيل المثال ، تطبيق مراسلة ، ولكن إذا كان لديك خدمة بث موسيقى أو مشغل بودكاست ، فليس لديك طريقة لاستخدام SiriKit في الوقت الحالي.
وبالمثل ، هناك ثلاث طرق للتطبيقات للتحدث مع المستخدم: بالصوت ، أو بالنص الذي يتم تحويله ، أو بالتعبير عن نوع الشيء الذي تريد قوله والسماح للنظام باكتشاف الطريقة الدقيقة للتعبير عنه. يضع الحل الأخير (وهو ما تفعله Apple) عبء الترجمة على Apple ، لكنه يمنحك طرقًا محدودة لاستخدام كلماتك لوصف الأشياء.
يتم تحديد أنواع الطلبات التي يمكنك معالجتها في مجالات وأهداف SiriKit. النية هي نوع من الطلبات التي قد يقدمها المستخدم ، مثل إرسال رسالة نصية إلى جهة اتصال أو البحث عن صورة. تحتوي كل نية على قائمة من المعلمات - على سبيل المثال ، تتطلب الرسائل النصية جهة اتصال ورسالة.
المجال هو مجرد مجموعة من النوايا ذات الصلة. قراءة نص وإرسال نص كلاهما في مجال المراسلة. حجز رحلة والحصول على موقع موجودان في مجال حجز الرحلات. هناك مجالات لإجراء مكالمات VoIP وبدء التدريبات والبحث عن الصور وبعض الأشياء الأخرى. تحتوي وثائق SiriKit على قائمة كاملة بالمجالات وأغراضها.
يتمثل أحد الانتقادات الشائعة لسيري في أنه يبدو غير قادر على التعامل مع الطلبات مثل Google و Alexa ، وأن النظام البيئي الصوتي للجهة الخارجية الذي يمكّنه منافسو Apple أكثر ثراءً.
أنا أتفق مع تلك الانتقادات. إذا كان تطبيقك لا يتناسب مع المقاصد الحالية ، فلا يمكنك استخدام SiriKit ، ولا يمكنك فعل أي شيء. حتى إذا كان تطبيقك مناسبًا ، فلا يمكنك التحكم في جميع الكلمات التي يقولها Siri أو يفهمها ؛ لذلك ، إذا كانت لديك طريقة معينة للتحدث عن الأشياء في تطبيقك ، فلا يمكنك دائمًا تعليم ذلك لـ Siri.
يأمل مطورو iOS أن تقوم Apple بتوسيع قائمة المقاصد الخاصة بها بشكل كبير وأن تصبح معالجتها للغة الطبيعية أفضل بكثير. إذا حدث ذلك ، فسيكون لدينا مساعد صوتي يعمل دون أن يضطر المطورون إلى القيام بالترجمة أو فهم كل الطرق لقول الشيء نفسه. وتنفيذ الدعم للطلبات المنظمة هو في الواقع سهل إلى حد ما - أسهل بكثير من بناء محلل لغة طبيعية.
فائدة كبيرة أخرى لإطار النوايا هي أنه لا يقتصر على طلبات Siri والصوت. حتى الآن ، يمكن لتطبيق الخرائط إنشاء طلب يستند إلى النوايا لتطبيقك (على سبيل المثال ، حجز مطعم). يقوم بذلك برمجيًا (وليس من الصوت أو اللغة الطبيعية). إذا سمحت Apple للتطبيقات باكتشاف المقاصد المكشوفة لبعضها البعض ، فسيكون لدينا طريقة أفضل بكثير لكي تعمل التطبيقات معًا (على عكس عناوين URL لنمط رد الاتصال x).
أخيرًا ، نظرًا لأن النية عبارة عن طلب منظم مع معلمات ، فهناك طريقة بسيطة لتطبيق ما للتعبير عن أن المعلمات مفقودة أو أنه يحتاج إلى مساعدة في التمييز بين بعض الخيارات. يمكن لـ Siri بعد ذلك طرح أسئلة متابعة لحل المعلمات دون أن يحتاج التطبيق إلى إجراء المحادثة.
مجال حجز الركوب
لفهم المجالات والأغراض ، دعنا نلقي نظرة على مجال حجز الرحلات. هذا هو المجال الذي قد تستخدمه لتطلب من Siri الحصول على سيارة Lyft.
تحدد Apple كيفية طلب مشوار وكيفية الحصول على معلومات عنها ، ولكن لا يوجد في الواقع تطبيق Apple مدمج يمكنه التعامل مع هذا الطلب. هذا هو أحد المجالات القليلة التي تتطلب تطبيقًا يدعم SiriKit.
يمكنك استدعاء أحد النوايا عبر الصوت أو مباشرة من الخرائط. بعض النوايا لهذا المجال هي:
- اطلب توصيلة
استخدم هذا لحجز رحلة. ستحتاج إلى توفير موقع الالتقاء والتوصيل ، وقد يحتاج التطبيق أيضًا إلى معرفة حجم الحفلة ونوع الرحلة التي تريدها. قد تكون عبارة نموذجية ، "احجز لي رحلة باستخدام <appname>". - احصل على حالة الركوب
استخدم هذه النية لمعرفة ما إذا كان قد تم استلام طلبك وللحصول على معلومات حول السيارة والسائق ، بما في ذلك موقعهما. يستخدم تطبيق الخرائط هذه النية لعرض صورة محدثة للسيارة وهي تقترب منك. - إلغاء رحلة
استخدم هذا لإلغاء المشوار الذي حجزته.
لأي من هذه النوايا ، قد يحتاج Siri إلى معرفة المزيد من المعلومات. كما سترى عندما نطبق معالج intent ، يمكن أن يخبر امتداد Intents Siri أن المعلمة المطلوبة مفقودة ، وسيطالب Siri المستخدم بذلك.
توضح حقيقة أنه يمكن استدعاء النوايا برمجيًا بواسطة الخرائط كيف يمكن للنوايا تمكين الاتصال بين التطبيقات في المستقبل.
ملاحظة : يمكنك الحصول على قائمة كاملة بالمجالات ونواياها على موقع مطوري Apple. يوجد أيضًا نموذج لتطبيق Apple مع العديد من المجالات والأهداف التي تم تنفيذها ، بما في ذلك حجز الرحلات.
إضافة قوائم ودعم مجال Notes إلى تطبيقك
حسنًا ، الآن بعد أن فهمنا أساسيات SiriKit ، فلنلقِ نظرة على كيفية إضافة دعم لـ Siri في تطبيق يتضمن الكثير من التكوين وفئة لكل نية تريد التعامل معها.
يتكون الجزء المتبقي من هذه المقالة من الخطوات التفصيلية لإضافة دعم Siri إلى أحد التطبيقات. هناك خمسة أشياء رفيعة المستوى عليك القيام بها:
- استعد لإضافة امتداد جديد إلى التطبيق عن طريق إنشاء ملفات تعريف التوفير مع استحقاقات جديدة لها على موقع مطور Apple على الويب.
- قم بتهيئة تطبيقك (عبر قائمة التشغيل الخاصة
plist
) لاستخدام الاستحقاقات. - استخدم قالب Xcode لبدء استخدام بعض التعليمات البرمجية النموذجية.
- أضف الرمز لدعم مقصد Siri الخاص بك.
- قم بتكوين مفردات Siri عبر
plist
s.
لا تقلق: سنستعرض كل منها ، ونوضح الامتدادات والاستحقاقات على طول الطريق.
للتركيز على أجزاء Siri فقط ، قمت بإعداد مدير قائمة مهام بسيط ، List-o-Mat.
يمكنك العثور على المصدر الكامل للعينة ، List-o-Mat ، على GitHub.
لإنشائه ، كل ما فعلته هو البدء باستخدام قالب تطبيق Xcode Master-Detail وجعل كلتا الشاشتين في UITableView
. أضفت طريقة لإضافة وحذف القوائم والعناصر ، وطريقة لإلغاء تحديد العناصر كما تم. يتم إنشاء كل التنقل بواسطة القالب.
لتخزين البيانات ، استخدمت بروتوكول Codable
(الذي تم تقديمه في WWDC 2017) ، والذي يحول الهياكل إلى JSON ويحفظها في ملف نصي في مجلد documents
.
لقد جعلت الشفرة بسيطة جدًا عن عمد. إذا كانت لديك أي خبرة مع Swift وعمل وحدات تحكم في العرض ، فلن تواجه مشكلة في ذلك.
يمكننا الآن متابعة خطوات إضافة دعم SiriKit. ستكون الخطوات عالية المستوى هي نفسها لأي تطبيق وأي مجال ونوايا تخطط لتنفيذه. سنتعامل في الغالب مع موقع ويب مطور Apple ، ونقوم بتحرير plist
وكتابة القليل من Swift.
بالنسبة لـ List-o-Mat ، سنركز على مجال القوائم والملاحظات ، والتي تنطبق على نطاق واسع على أشياء مثل تطبيقات تدوين الملاحظات وقوائم المهام.
في مجال القوائم والملاحظات ، لدينا المقاصد التالية التي قد تكون منطقية لتطبيقنا.
- احصل على قائمة المهام.
- إضافة مهمة جديدة إلى القائمة.
نظرًا لأن التفاعلات مع Siri تحدث بالفعل خارج تطبيقك (ربما حتى عندما لا يكون التطبيق قيد التشغيل) ، يستخدم iOS امتدادًا لتنفيذ ذلك.
امتداد النوايا
إذا لم تكن قد عملت مع الامتدادات ، فستحتاج إلى معرفة ثلاثة أشياء رئيسية:
- الامتداد هو عملية منفصلة. يتم تسليمه داخل حزمة تطبيقك ، لكنه يعمل بشكل كامل من تلقاء نفسه ، مع وضع الحماية الخاص به.
- يمكن لتطبيقك وإضافتك التواصل مع بعضهما البعض من خلال التواجد في نفس مجموعة التطبيقات. أسهل طريقة هي عبر مجلدات وضع الحماية المشتركة للمجموعة (بحيث يمكنهم القراءة والكتابة إلى نفس الملفات إذا وضعتها هناك).
- تتطلب الإضافات معرفات التطبيقات والملفات الشخصية والاستحقاقات الخاصة بها.
لإضافة امتداد إلى تطبيقك ، ابدأ بتسجيل الدخول إلى حساب المطور الخاص بك والانتقال إلى قسم "الشهادات والمعرفات والملفات الشخصية".
تحديث بيانات حساب تطبيق Apple Developer الخاص بك
في حساب مطور Apple الخاص بنا ، فإن أول شيء يتعين علينا القيام به هو إنشاء مجموعة تطبيقات. انتقل إلى قسم "مجموعات التطبيقات" ضمن "المعرفات" وأضف واحدًا.
يجب أن يبدأ group
، متبوعًا بمعرّفك المعتاد المستند إلى المجال العكسي. نظرًا لأنه يحتوي على بادئة ، يمكنك استخدام معرف التطبيق الخاص بك لبقية البيانات.
بعد ذلك ، نحتاج إلى تحديث معرف التطبيق الخاص بنا لاستخدام هذه المجموعة ولتمكين Siri:
- انتقل إلى قسم "معرفات التطبيق" وانقر على معرف التطبيق الخاص بك ؛
- انقر فوق الزر "تحرير" ؛
- تمكين مجموعات التطبيقات (إذا لم يتم تمكينها لملحق آخر).
- ثم قم بتكوين مجموعة التطبيقات بالنقر فوق الزر "تعديل". اختر مجموعة التطبيقات من قبل.
- قم بتمكين SiriKit.
- انقر فوق "تم" لحفظه.
الآن ، نحتاج إلى إنشاء معرف تطبيق جديد لإضافتنا:
- في قسم "معرفات التطبيق" نفسه ، أضف معرف تطبيق جديد. سيكون هذا هو معرف التطبيق الخاص بك ، مع لاحقة. لا تستخدم فقط
Intents
كلاحقة لأن هذا الاسم سيصبح اسم الوحدة الخاصة بك في Swift ومن ثم سيتعارض معIntents
الحقيقية. - قم بتمكين معرف التطبيق هذا لمجموعات التطبيقات أيضًا (وقم بإعداد المجموعة كما فعلنا من قبل).
الآن ، أنشئ ملفًا شخصيًا لتوفير التطوير لامتداد Intents ، وأعد إنشاء ملف تعريف توفير التطبيق الخاص بك. قم بتنزيلها وتثبيتها كما تفعل عادةً.
الآن بعد أن تم تثبيت ملفات التعريف الخاصة بنا ، نحتاج إلى الانتقال إلى Xcode وتحديث استحقاقات التطبيق.
تحديث استحقاقات التطبيق الخاص بك في Xcode
مرة أخرى في Xcode ، اختر اسم مشروعك في متصفح المشروع. بعد ذلك ، اختر الهدف الرئيسي لتطبيقك ، وانتقل إلى علامة التبويب "القدرات". هناك ، سترى مفتاحًا لتشغيل دعم Siri.
أسفل القائمة ، يمكنك تشغيل مجموعات التطبيقات وتهيئتها.
إذا قمت بإعداده بشكل صحيح ، فسترى هذا في ملف .entitlements
:
الآن ، نحن مستعدون أخيرًا لإضافة هدف امتداد Intents إلى مشروعنا.
إضافة ملحق النوايا
نحن مستعدون أخيرًا لإضافة الامتداد. في Xcode ، اختر "ملف" ← "هدف جديد". ستظهر هذه الورقة:
اختر "Intents Extension" وانقر على زر "التالي". املأ الشاشة التالية:
يجب أن يتطابق اسم المنتج مع كل ما قمت بإنشائه لاحقة في معرّف تطبيق intents على موقع ويب مطور Apple.
نحن نختار عدم إضافة امتداد intents UI. لم يتم تناول هذا في هذه المقالة ، ولكن يمكنك إضافته لاحقًا إذا كنت بحاجة إلى واحد. في الأساس ، إنها طريقة لوضع علامتك التجارية الخاصة ونمط العرض في نتائج Siri المرئية.
عند الانتهاء ، سينشئ Xcode فئة معالج النوايا التي يمكننا استخدامها كجزء بداية لتطبيق Siri الخاص بنا.
معالج النوايا: الحل والتأكيد والتعامل
أنشأ Xcode هدفًا جديدًا له نقطة انطلاق لنا.
أول شيء عليك القيام به هو إعداد هذا الهدف الجديد ليكون في نفس مجموعة التطبيقات مثل التطبيق. كما في السابق ، انتقل إلى علامة التبويب "القدرات" في الهدف ، وقم بتشغيل مجموعات التطبيقات ، وقم بتكوينها باستخدام اسم مجموعتك. تذكر أن التطبيقات في نفس المجموعة لديها وضع حماية يمكنهم استخدامه لمشاركة الملفات مع بعضهم البعض. نحتاج إلى هذا حتى تصل طلبات Siri إلى تطبيقنا.
List-o-Mat لها وظيفة تقوم بإرجاع مجلد مستند المجموعة. يجب أن نستخدمه متى أردنا القراءة أو الكتابة إلى ملف مشترك.
func documentsFolder() -> URL? { return FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.com.app-o-mat.ListOMat") }
على سبيل المثال ، عندما نحفظ القوائم ، نستخدم هذا:
func save(lists: Lists) { guard let docsDir = documentsFolder() else { fatalError("no docs dir") } let url = docsDir.appendingPathComponent(fileName, isDirectory: false) // Encode lists as JSON and save to url }
أنشأ قالب امتداد Intent ملفًا يسمى IntentHandler.swift
، بفئة تسمى IntentHandler
. كما أنه قام بتكوينه ليكون نقطة دخول النوايا في قائمة plist
.
في نفس plist
، سترى قسمًا للإعلان عن المقاصد التي ندعمها. سنبدأ بالقائمة التي تسمح بالبحث عن القوائم ، والتي تسمى INSearchForNotebookItemsIntent
. قم بإضافته إلى المصفوفة ضمن IntentsSupported
.
الآن ، انتقل إلى IntentHandler.swift
واستبدل محتوياته بهذا الرمز:
import Intents class IntentHandler: INExtension { override func handler(for intent: INIntent) -> Any? { switch intent { case is INSearchForNotebookItemsIntent: return SearchItemsIntentHandler() default: return nil } } }
يتم استدعاء وظيفة handler
للحصول على كائن للتعامل مع هدف معين. يمكنك فقط تنفيذ جميع البروتوكولات في هذه الفئة والعودة self
، لكننا سنضع كل نية في فئتها الخاصة لإبقائها منظمة بشكل أفضل.
نظرًا لأننا نعتزم الحصول على عدد قليل من الفئات المختلفة ، فلنمنحهم فئة أساسية مشتركة للتعليمات البرمجية التي نحتاج إلى مشاركتها بينهم:
class ListOMatIntentsHandler: NSObject { }
يتطلب إطار عمل النوايا أن نرث من NSObject
. سنملأ بعض الطرق لاحقًا.
نبدأ تنفيذ البحث الخاص بنا بهذا:
class SearchItemsIntentHandler: ListOMatIntentsHandler, INSearchForNotebookItemsIntentHandling { }
لتعيين معالج الهدف ، نحتاج إلى تنفيذ ثلاث خطوات أساسية
- حل المعلمات.
تأكد من تقديم المعلمات المطلوبة ، وقم بإلغاء غموض أي معلمات لا تفهمها تمامًا. - تأكد من أن الطلب قابل للتنفيذ.
غالبًا ما يكون هذا اختياريًا ، ولكن حتى إذا كنت تعلم أن كل معلمة جيدة ، فقد تظل بحاجة إلى الوصول إلى مورد خارجي أو لديك متطلبات أخرى. - تعامل مع الطلب.
افعل الشيء المطلوب.
INSearchForNotebookItemsIntent
، وهو الهدف الأول الذي سننفذه ، كبحث عن مهمة. أنواع الطلبات التي يمكننا التعامل معها هي ، "في List-o-Mat ، اعرض قائمة متجر البقالة" أو "In List-o-Mat ، اعرض قائمة المتاجر."
جانباً: يعد "List-o-Mat" في الواقع اسمًا سيئًا لتطبيق SiriKit لأن Siri يواجه صعوبة في استخدام الواصلات في التطبيقات. لحسن الحظ ، يسمح لنا SiriKit بالحصول على أسماء بديلة وتقديم النطق. في Info.plist
الخاص بالتطبيق ، أضف هذا القسم:
يتيح ذلك للمستخدم أن يقول "list oh mat" وأن يُفهم ذلك على أنه كلمة واحدة (بدون واصلات). لا يبدو الأمر مثالياً على الشاشة ، ولكن بدونه ، يعتقد Siri أحيانًا أن "List" و "Mat" كلمات منفصلة ويصبح مرتبكًا للغاية.
الحل: معرفة المعلمات
للبحث عن عناصر دفتر الملاحظات ، هناك عدة معلمات:
- نوع العنصر (مهمة أو قائمة مهام أو ملاحظة) ،
- عنوان العنصر ،
- محتوى العنصر ،
- حالة الإكمال (سواء تم وضع علامة على المهمة أم لا) ،
- الموقع المرتبط به ،
- التاريخ المرتبط به.
نحتاج إلى أول اثنين فقط ، لذا سنحتاج إلى كتابة وظائف حل لهما. INSearchForNotebookItemsIntent
له طرق يمكننا تنفيذها.
نظرًا لأننا نهتم فقط بإظهار قوائم المهام ، فسنقوم بتشفير ذلك في حل نوع العنصر. في SearchItemsIntentHandler
، أضف هذا:
func resolveItemType(for intent: INSearchForNotebookItemsIntent, with completion: @escaping (INNotebookItemTypeResolutionResult) -> Void) { completion(.success(with: .taskList)) }
لذلك ، بغض النظر عما يقوله المستخدم ، سنبحث عن قوائم المهام. إذا أردنا توسيع دعم البحث لدينا ، فسنسمح لـ Siri بمحاولة اكتشاف ذلك من العبارة الأصلية ثم استخدم فقط completion(.needsValue())
إذا كان نوع العنصر مفقودًا. بدلاً من ذلك ، يمكننا محاولة التخمين من العنوان من خلال رؤية ما يطابقه. في هذه الحالة ، سنكمل بالنجاح عندما يعرف Siri ما هو عليه ، وسنستخدم completion(.notRequired())
عندما سنحاول عدة احتمالات.
دقة العنوان أصعب قليلاً. ما نريده هو أن يستخدم Siri قائمة إذا عثرت على واحدة مطابقة تمامًا لما قلته. إذا لم يكن الأمر متأكدًا أو إذا كان هناك أكثر من احتمال ، فنحن نريد من Siri أن يطلب منا المساعدة في اكتشاف ذلك. للقيام بذلك ، يوفر SiriKit مجموعة من تعدادات الدقة التي تتيح لنا التعبير عما نريد أن يحدث بعد ذلك.
لذا ، إذا قلت "متجر بقالة" ، فسيكون لدى Siri مطابقة تامة. ولكن إذا قلت "متجر" ، فإن Siri سيقدم قائمة بالقوائم المتطابقة.
سنبدأ بهذه الوظيفة لإعطاء البنية الأساسية:
func resolveTitle(for intent: INSearchForNotebookItemsIntent, with completion: @escaping (INSpeakableStringResolutionResult) -> Void) { guard let title = intent.title else { completion(.needsValue()) return } let possibleLists = getPossibleLists(for: title) completeResolveListName(with: possibleLists, for: title, with: completion) }
سنقوم بتنفيذ getPossibleLists(for:)
و completeResolveListName(with:for:with:)
في فئة ListOMatIntentsHandler
الأساسية.
getPossibleLists(for:)
إلى محاولة التطابق الغامض مع العنوان الذي يمررنا إليه Siri مع أسماء القائمة الفعلية.
public func getPossibleLists(for listName: INSpeakableString) -> [INSpeakableString] { var possibleLists = [INSpeakableString]() for l in loadLists() { if l.name.lowercased() == listName.spokenPhrase.lowercased() { return [INSpeakableString(spokenPhrase: l.name)] } if l.name.lowercased().contains(listName.spokenPhrase.lowercased()) || listName.spokenPhrase.lowercased() == "all" { possibleLists.append(INSpeakableString(spokenPhrase: l.name)) } } return possibleLists }
نحن نمرح من خلال جميع قوائمنا. إذا حصلنا على تطابق تام ، فسنعيده ، وإذا لم نحصل على تطابق تام ، فسنقوم بإرجاع مجموعة من الاحتمالات. في هذه الوظيفة ، نتحقق ببساطة لمعرفة ما إذا كانت الكلمة التي قالها المستخدم موجودة في اسم القائمة (أي تطابق بسيط جدًا). يتيح ذلك لـ "البقالة" مطابقة "متجر البقالة". قد تحاول الخوارزمية الأكثر تقدمًا المطابقة بناءً على الكلمات التي تبدو متشابهة (على سبيل المثال ، مع خوارزمية Soundex) ،
completeResolveListName(with:for:with:)
مسؤول عن تحديد ما يجب فعله بقائمة الاحتمالات هذه.
public func completeResolveListName(with possibleLists: [INSpeakableString], for listName: INSpeakableString, with completion: @escaping (INSpeakableStringResolutionResult) -> Void) { switch possibleLists.count { case 0: completion(.unsupported()) case 1: if possibleLists[0].spokenPhrase.lowercased() == listName.spokenPhrase.lowercased() { completion(.success(with: possibleLists[0])) } else { completion(.confirmationRequired(with: possibleLists[0])) } default: completion(.disambiguation(with: possibleLists)) } }
إذا حصلنا على تطابق تام ، فإننا نخبر Siri بأننا نجحنا. إذا حصلنا على تطابق واحد غير دقيق ، فإننا نخبر Siri أن نسأل المستخدم ما إذا كنا قد خمناها بشكل صحيح.
إذا حصلنا على العديد من التطابقات ، فإننا نستخدم completion(.disambiguation(with: possibleLists))
لإخبار Siri بإظهار قائمة والسماح للمستخدم باختيار واحدة.
الآن بعد أن عرفنا ما هو الطلب ، نحتاج إلى النظر في الأمر برمته والتأكد من قدرتنا على التعامل معه.
تأكيد: تحقق من كل التبعيات الخاصة بك
في هذه الحالة ، إذا حللنا جميع المعلمات ، فيمكننا دائمًا معالجة الطلب. قد تتحقق عمليات confirm()
النموذجية من توفر الخدمات الخارجية أو تتحقق من مستويات التفويض.
نظرًا لأن confirm()
اختياري ، لا يمكننا فعل أي شيء ، ويفترض Siri أنه يمكننا التعامل مع أي طلب بمعلمات تم حلها. لنكون صريحين ، يمكننا استخدام هذا:
func confirm(intent: INSearchForNotebookItemsIntent, completion: @escaping (INSearchForNotebookItemsIntentResponse) -> Void) { completion(INSearchForNotebookItemsIntentResponse(code: .success, userActivity: nil)) }
هذا يعني أنه يمكننا التعامل مع أي شيء.
المقبض: افعلها
الخطوة الأخيرة هي معالجة الطلب.
func handle(intent: INSearchForNotebookItemsIntent, completion: @escaping (INSearchForNotebookItemsIntentResponse) -> Void) { guard let title = intent.title, let list = loadLists().filter({ $0.name.lowercased() == title.spokenPhrase.lowercased()}).first else { completion(INSearchForNotebookItemsIntentResponse(code: .failure, userActivity: nil)) return } let response = INSearchForNotebookItemsIntentResponse(code: .success, userActivity: nil) response.tasks = list.items.map { return INTask(title: INSpeakableString(spokenPhrase: $0.name), status: $0.done ? INTaskStatus.completed : INTaskStatus.notCompleted, taskType: INTaskType.notCompletable, spatialEventTrigger: nil, temporalEventTrigger: nil, createdDateComponents: nil, modifiedDateComponents: nil, identifier: "\(list.name)\t\($0.name)") } completion(response) }
أولاً ، نجد القائمة بناءً على العنوان. في هذه المرحلة ، resolveTitle
بالفعل من أننا سنحصل على تطابق تام. ولكن إذا كانت هناك مشكلة ، فلا يزال بإمكاننا إرجاع الفشل.
عندما يكون لدينا فشل ، لدينا خيار تمرير نشاط المستخدم. إذا كان تطبيقك يستخدم Handoff ولديه طريقة للتعامل مع هذا النوع الدقيق من الطلبات ، فقد يحاول Siri التأجيل إلى تطبيقك لتجربة الطلب هناك. لن يفعل ذلك عندما نكون في سياق صوتي فقط (على سبيل المثال ، لقد بدأت بـ "يا Siri") ، ولا يضمن أنه سيفعل ذلك في حالات أخرى ، لذلك لا تعتمد عليه.
هذا الآن جاهز للاختبار. اختر امتداد النية في قائمة الهدف في Xcode. ولكن قبل تشغيله ، قم بتحرير المخطط.
يُظهر ذلك طريقة لتقديم استعلام مباشرةً:
ملاحظة ، أنا أستخدم "ListOMat" بسبب مشكلة الواصلات المذكورة أعلاه. لحسن الحظ ، يتم نطقه بنفس اسم تطبيقي ، لذا لا ينبغي أن يكون مشكلة كبيرة.
مرة أخرى في التطبيق ، قمت بإنشاء قائمة "متجر بقالة" وقائمة "متجر أجهزة". إذا طلبت من Siri الحصول على قائمة "المتجر" ، فسوف تمر عبر مسار التوضيح ، والذي يبدو كالتالي:
إذا قلت "متجر بقالة" ، فستحصل على المطابقة التامة ، والتي تظهر مباشرة في النتائج.
إضافة العناصر عبر Siri
الآن بعد أن عرفنا المفاهيم الأساسية للحل والتأكيد والتعامل ، يمكننا بسرعة إضافة نية لإضافة عنصر إلى القائمة.
أولاً ، أضف INAddTasksIntent
إلى قائمة الملحق:
بعد ذلك ، قم بتحديث وظيفة handle
IntentHandler
الخاصة بنا.
override func handler(for intent: INIntent) -> Any? { switch intent { case is INSearchForNotebookItemsIntent: return SearchItemsIntentHandler() case is INAddTasksIntent: return AddItemsIntentHandler() default: return nil } }
أضف كعبًا للفصل الجديد:
class AddItemsIntentHandler: ListOMatIntentsHandler, INAddTasksIntentHandling { }
تحتاج إضافة عنصر إلى resolve
مماثل للبحث ، باستثناء قائمة المهام المستهدفة بدلاً من العنوان.
func resolveTargetTaskList(for intent: INAddTasksIntent, with completion: @escaping (INTaskListResolutionResult) -> Void) { guard let title = intent.targetTaskList?.title else { completion(.needsValue()) return } let possibleLists = getPossibleLists(for: title) completeResolveTaskList(with: possibleLists, for: title, with: completion) }
CompleteResolveTaskList هو completeResolveTaskList
مثل completeResolveListName
، ولكن مع أنواع مختلفة قليلاً (قائمة مهام بدلاً من عنوان قائمة المهام).
public func completeResolveTaskList(with possibleLists: [INSpeakableString], for listName: INSpeakableString, with completion: @escaping (INTaskListResolutionResult) -> Void) { let taskLists = possibleLists.map { return INTaskList(title: $0, tasks: [], groupName: nil, createdDateComponents: nil, modifiedDateComponents: nil, identifier: nil) } switch possibleLists.count { case 0: completion(.unsupported()) case 1: if possibleLists[0].spokenPhrase.lowercased() == listName.spokenPhrase.lowercased() { completion(.success(with: taskLists[0])) } else { completion(.confirmationRequired(with: taskLists[0])) } default: completion(.disambiguation(with: taskLists)) } }
لها نفس منطق توضيحها وتتصرف بنفس الطريقة تمامًا. يجب توضيح عبارة "المتجر" ، كما أن قول "متجر البقالة" سيكون مطابقًا تمامًا.
سنترك confirm
دون تنفيذ ونقبل الافتراضي. handle
، نحتاج إلى إضافة عنصر إلى القائمة وحفظه.
func handle(intent: INAddTasksIntent, completion: @escaping (INAddTasksIntentResponse) -> Void) { var lists = loadLists() guard let taskList = intent.targetTaskList, let listIndex = lists.index(where: { $0.name.lowercased() == taskList.title.spokenPhrase.lowercased() }), let itemNames = intent.taskTitles, itemNames.count > 0 else { completion(INAddTasksIntentResponse(code: .failure, userActivity: nil)) return } // Get the list var list = lists[listIndex] // Add the items var addedTasks = [INTask]() for item in itemNames { list.addItem(name: item.spokenPhrase, at: list.items.count) addedTasks.append(INTask(title: item, status: .notCompleted, taskType: .notCompletable, spatialEventTrigger: nil, temporalEventTrigger: nil, createdDateComponents: nil, modifiedDateComponents: nil, identifier: nil)) } // Save the new list lists[listIndex] = list save(lists: lists) // Respond with the added items let response = INAddTasksIntentResponse(code: .success, userActivity: nil) response.addedTasks = addedTasks completion(response) }
نحصل على قائمة بالعناصر وقائمة الأهداف. نحن نبحث عن القائمة ونضيف العناصر. نحتاج أيضًا إلى إعداد استجابة لـ Siri لإظهارها مع العناصر المضافة وإرسالها إلى وظيفة الإكمال.
يمكن لهذه الوظيفة التعامل مع عبارة مثل ، "في ListOMat ، أضف التفاح إلى قائمة البقالة." يمكنه أيضًا التعامل مع قائمة من العناصر مثل ، "الأرز والبصل والزيتون".
أوشكت على الانتهاء ، فقط عدد قليل من الإعدادات الأخرى
سيعمل كل هذا في جهاز المحاكاة أو الجهاز المحلي ، ولكن إذا كنت تريد إرسال هذا ، فستحتاج إلى إضافة مفتاح NSSiriUsageDescription
إلى قائمة تطبيقك ، مع سلسلة تصف plist
الذي تستخدم Siri من أجله. هناك شيء مثل "سيتم إرسال طلباتك حول القوائم إلى Siri".
يجب عليك أيضًا إضافة مكالمة إلى:
INPreferences.requestSiriAuthorization { (status) in }
ضع هذا في عرض عرض وحدة التحكم في العرض الرئيسي الخاص بك viewDidLoad
المستخدم بوصول Siri. This will show the message you configured above and also let the user know that they could be using Siri for this app.
Finally, you'll need to tell Siri what to tell the user if the user asks what your app can do, by providing some sample phrases:
- Create a
plist
file in your app (not the extension), namedAppIntentVocabulary.plist
. - Fill out the intents and phrases that you support.
There is no way to really know all of the phrases that Siri will use for an intent, but Apple does provide a few samples for each intent in its documentation. The sample phrases for task-list searching show us that Siri can understand “Show me all my notes on <appName>,” but I found other phrases by trial and error (for example, Siri understands what “lists” are too, not just notes).
ملخص
As you can see, adding Siri support to an app has a lot of steps, with a lot of configuration. But the code needed to handle the requests was fairly simple.
There are a lot of steps, but each one is small, and you might be familiar with a few of them if you have used extensions before.
Here is what you'll need to prepare for a new extension on Apple's developer website:
- Make an app ID for an Intents extension.
- Make an app group if you don't already have one.
- Use the app group in the app ID for the app and extension.
- Add Siri support to the app's ID.
- Regenerate the profiles and download them.
And here are the steps in Xcode for creating Siri's Intents extension:
- Add an Intents extension using the Xcode template.
- Update the entitlements of the app and extension to match the profiles (groups and Siri support).
- Add your intents to the extension's
plist
.
And you'll need to add code to do the following things:
- Use the app group sandbox to communicate between the app and extension.
- Add classes to support each intent with resolve, confirm and handle functions.
- Update the generated
IntentHandler
to use those classes. - Ask for Siri access somewhere in your app.
Finally, there are some Siri-specific configuration settings:
- Add the Siri support security string to your app's
plist
. - Add sample phrases to an
AppIntentVocabulary.plist
file in your app. - Run the intent target to test; edit the scheme to provide the phrase.
OK, that is a lot, but if your app fits one of Siri's domains, then users will expect that they can interact with it via voice. And because the competition for voice assistants is so good, we can only expect that WWDC 2018 will bring a bunch more domains and, hopefully, much better Siri.
قراءة متعمقة
- “SiriKit,” Apple
The technical documentation contains the full list of domains and intents. - “Guides and Sample Code,” Apple
Includes code for many domains. - “Introducing SiriKit” (video, Safari only), WWDC 2016 Apple
- “What's New in SiriKit” (video, Safari only), WWDC 2017, Apple
Apple introduces lists and notes - “Lists and Notes,” Apple
The full list of lists and notes intents.