SiriKit'in Amaçları Uygulamanıza Uyacak mı? Öyleyse, Bunları Nasıl Kullanacağınız İşte
Yayınlanan: 2022-03-10iOS 5'ten beri Siri, iPhone kullanıcılarının Apple uygulamalarıyla mesaj göndermesine, hatırlatıcı ayarlamasına ve restoran aramasına yardımcı oldu. iOS 10'dan başlayarak, Siri'yi bazı uygulamalarımızda da kullanabiliyoruz.
Bu işlevi kullanabilmek için uygulamanızın Apple'ın önceden tanımlanmış Siri "etki alanları ve amaçları"na uyması gerekir. Bu makalede, bunların ne olduğunu öğreneceğiz ve uygulamalarımızın bunları kullanıp kullanamayacağını göreceğiz. Yapılacaklar listesi yöneticisi olan basit bir uygulamayı ele alacağız ve Siri desteğinin nasıl ekleneceğini öğreneceğiz. Ayrıca, SiriKit ile tanıtılan yeni bir uzantı türü olan Intents uzantısı için Apple geliştirici web sitesinin yapılandırma ve Swift koduyla ilgili yönergelerini inceleyeceğiz.
Bu makalenin kodlama kısmına geldiğinizde, Xcode'a (en azından sürüm 9.x) ihtiyacınız olacak ve Swift'de iOS geliştirme hakkında bilgi sahibi olmanız iyi olur çünkü Siri'yi küçük bir çalışmaya ekleyeceğiz. uygulama. Apple'ın geliştirici web sitesinde bir uzantı kurma ve uygulamaya Siri uzantı kodunu ekleme adımlarını inceleyeceğiz.
"Hey Siri, Sana Neden İhtiyacım Var?"
Bazen telefonumu kanepemdeyken iki elim boşta kullanıyorum ve tüm dikkatimi ekrana verebiliyorum. Belki annemin doğum gününü planlamak için kız kardeşime mesaj atarım ya da Trello'daki bir soruyu yanıtlarım. Uygulamayı görebiliyorum. Ekrana dokunabilirim. yazabilirim.
Ama saatime bir mesaj geldiğinde kasabamda dolaşıp bir podcast dinliyor olabilirim. Telefonum cebimde ve yürürken kolayca cevap veremiyorum.
Siri ile kulaklığımın kontrol düğmesini basılı tutup "Kız kardeşime saat ikide orada olacağımı mesaj at" diyebiliyorum. Siri, hareket halindeyken ve telefonunuza tam olarak veremediğinizde veya etkileşim küçük olduğunda harikadır, ancak birkaç dokunuş ve bir sürü yazma gerektirir.
Bu etkileşimler için Apple uygulamalarını kullanmak istersem bu sorun değil. Ancak mesajlaşma gibi bazı uygulama kategorilerinin çok popüler alternatifleri vardır. Yolculuk rezervasyonu yapmak veya bir restoranda masa ayırtmak gibi diğer etkinlikler, Apple'ın yerleşik uygulamalarıyla bile mümkün değildir, ancak Siri için mükemmeldir.
Apple'ın Sesli Asistanlara Yaklaşımı
Üçüncü taraf uygulamalarda Siri'yi etkinleştirmek için Apple, sesi kullanıcının sesinden alacak ve bir şekilde isteği yerine getirebilecek şekilde uygulamaya getirecek bir mekanizmaya karar vermek zorunda kaldı. Bunu mümkün kılmak için Apple, kullanıcının istekte uygulamanın adını belirtmesini ister, ancak isteğin geri kalanıyla ne yapacaklarına dair birkaç seçeneği vardı.
- Uygulamaya bir ses dosyası göndermiş olabilir.
Bu yaklaşımın yararı, uygulamanın, kullanıcının onun için sahip olabileceği herhangi bir isteği tam anlamıyla işlemeye çalışabilmesidir. Amazon veya Google, zaten gelişmiş ses tanıma hizmetlerine sahip oldukları için bu yaklaşımı beğenmiş olabilir. Ancak çoğu uygulama bunu çok kolay halledemez. - Konuşmayı metne çevirebilir ve gönderebilirdi.
Pek çok uygulamada karmaşık doğal dil uygulamaları bulunmadığından, kullanıcının genellikle çok özel ifadelere bağlı kalması gerekir ve İngilizce olmayan desteğin uygulanması uygulama geliştiricisine bağlıdır. - Anladığınız cümlelerin bir listesini vermenizi isteyebilirdi.
Bu mekanizma, Amazon'un Alexa ile yaptığına ("yetenekler" çerçevesinde) daha yakındır ve Alexa'nın şu anda SiriKit'in kaldırabileceğinden çok daha fazla kullanımını sağlar. Bir Alexa becerisinde, Alexa'nın sizin için dolduracağı yer tutucu değişkenlere sahip ifadeler sağlarsınız. Örneğin, "Alexa, bana$TIME$
-$REMINDER$
arasında hatırlat" - Alexa, bu ifadeyi kullanıcının söylediklerine karşı çalıştıracak ve sizeTIME
veREMINDER
için değerleri söyleyecektir. Önceki mekanizmada olduğu gibi, geliştiricinin tüm çeviriyi yapması gerekiyor ve kullanıcı biraz farklı bir şey söylüyorsa çok fazla esneklik yok. - Parametrelerle bir istek listesi tanımlayabilir ve uygulamaya yapılandırılmış bir istek gönderebilir.
Bu aslında Apple'ın yaptığı şeydir ve faydası, çeşitli dilleri destekleyebilmesi ve bir kullanıcının bir isteği ifade edebileceği tüm yolları anlamaya çalışmak için tüm işi yapmasıdır. En büyük dezavantajı, yalnızca Apple'ın tanımladığı istekler için işleyici uygulayabilmenizdir. Bu, örneğin bir mesajlaşma uygulamanız varsa harikadır, ancak bir müzik akışı hizmetiniz veya bir podcast oynatıcınız varsa, şu anda SiriKit'i kullanmanın hiçbir yolu yoktur.
Benzer şekilde, uygulamaların kullanıcıyla konuşmasının üç yolu vardır: sesle, dönüştürülen metinle veya söylemek istediğiniz şeyi ifade ederek ve sistemin onu ifade etmenin tam yolunu bulmasına izin vererek. Son çözüm (Apple'ın yaptığı) çeviri yükünü Apple'a yükler, ancak size bir şeyleri açıklamak için kendi sözcüklerinizi kullanmanın sınırlı yollarını sunar.
İşleyebileceğiniz istek türleri, SiriKit'in etki alanlarında ve amaçlarında tanımlanmıştır. Niyet, bir kişiye mesaj atmak veya bir fotoğraf bulmak gibi kullanıcının yapabileceği bir istek türüdür. Her niyetin bir parametre listesi vardır; örneğin, mesajlaşma bir kişi ve bir mesaj gerektirir.
Bir etki alanı, yalnızca ilgili amaçlardan oluşan bir gruptur. Bir metni okumak ve bir metin göndermek, hem mesajlaşma alanındadır. Bir yolculuk rezervasyonu yapmak ve bir konum almak, yolculuk rezervasyonu alanındadır. VoIP aramaları yapmak, antrenmanlara başlamak, fotoğraf aramak ve birkaç başka şey için alan adları vardır. SiriKit'in belgeleri, etki alanlarının ve amaçlarının tam listesini içerir.
Siri'nin ortak eleştirisi, istekleri Google ve Alexa kadar karşılayamayacak gibi görünmesi ve Apple'ın rakipleri tarafından etkinleştirilen üçüncü taraf ses ekosisteminin daha zengin olmasıdır.
Bu eleştirilere katılıyorum. Uygulamanız mevcut amaçlara uymuyorsa SiriKit'i kullanamazsınız ve yapabileceğiniz hiçbir şey yoktur. Uygulamanız uygun olsa bile, Siri'nin söylediği veya anladığı tüm kelimeleri kontrol edemezsiniz; Bu nedenle, uygulamanızdaki şeyler hakkında belirli bir konuşma tarzınız varsa, bunu her zaman Siri'ye öğretemezsiniz.
iOS geliştiricilerinin umudu, hem Apple'ın amaç listesini büyük ölçüde genişletmesi hem de doğal dil işlemesinin çok daha iyi hale gelmesidir. Bunu yaparsa, geliştiricilerin aynı şeyi söylemenin tüm yollarını tercüme etmek veya anlamak zorunda kalmadan çalışan bir sesli asistanımız olacak. Ve yapılandırılmış istekler için destek uygulamak aslında oldukça basittir - doğal bir dil ayrıştırıcısı oluşturmaktan çok daha kolaydır.
Niyet çerçevesinin bir diğer büyük yararı da Siri ve sesli isteklerle sınırlı olmamasıdır. Şimdi bile Haritalar uygulaması, uygulamanız için amaca dayalı bir istek oluşturabilir (örneğin, bir restoran rezervasyonu). Bunu programlı olarak yapar (ses veya doğal dilden değil). Apple, uygulamaların birbirlerinin açığa çıkan amaçlarını keşfetmesine izin verseydi, uygulamaların birlikte çalışması için çok daha iyi bir yolumuz olurdu (x-geri arama stili URL'lerin aksine).
Son olarak, amaç parametreler içeren yapılandırılmış bir istek olduğundan, bir uygulamanın parametrelerin eksik olduğunu veya bazı seçenekler arasında ayrım yapmak için yardıma ihtiyacı olduğunu ifade etmesinin basit bir yolu vardır. Ardından Siri, uygulamanın konuşmayı yürütmesine gerek kalmadan parametreleri çözmek için takip soruları sorabilir.
Yolculuk-Rezervasyon Alanı
Alan adlarını ve amaçları anlamak için, araç kiralama alanına bakalım. Bu, Siri'den size bir Lyft arabası almasını istemek için kullanacağınız alan adıdır.
Apple, nasıl araç talep edileceğini ve bunun hakkında nasıl bilgi alınacağını tanımlar, ancak aslında bu isteği gerçekten yerine getirebilecek yerleşik bir Apple uygulaması yoktur. Bu, SiriKit özellikli bir uygulamanın gerekli olduğu birkaç alandan biridir.
Amaçlardan birini sesli olarak veya doğrudan Haritalar'dan çağırabilirsiniz. Bu etki alanının amaçlarından bazıları şunlardır:
- Araç talep edin
Bir yolculuk rezervasyonu yapmak için bunu kullanın. Bir alma ve bırakma konumu sağlamanız gerekecek ve uygulamanın ayrıca partinizin boyutunu ve ne tür bir sürüş istediğinizi bilmesi gerekebilir. Örnek bir ifade, "Beni <appname> ile gezdirin" olabilir. - Yolculuğun durumunu al
Bu amacı, talebinizin alınıp alınmadığını öğrenmek ve konumları da dahil olmak üzere araç ve sürücü hakkında bilgi almak için kullanın. Haritalar uygulaması bu amacı, size yaklaşan arabanın güncellenmiş bir görüntüsünü göstermek için kullanır. - sürüşü iptal et
Rezervasyon yaptığınız bir yolculuğu iptal etmek için bunu kullanın.
Bu amaçlardan herhangi biri için Siri'nin daha fazla bilgiye ihtiyacı olabilir. Bir amaç işleyici uyguladığımızda göreceğiniz gibi, Intents uzantınız Siri'ye gerekli bir parametrenin eksik olduğunu söyleyebilir ve Siri, kullanıcıdan bunu ister.
Amaçların Haritalar tarafından programlı olarak çağrılabilmesi, amaçların gelecekte uygulamalar arası iletişimi nasıl mümkün kılabileceğini gösterir.
Not : Alan adlarının ve amaçlarının tam listesini Apple'ın geliştirici web sitesinde bulabilirsiniz. Ayrıca, araç rezervasyonu da dahil olmak üzere birçok alan ve amacın uygulandığı örnek bir Apple uygulaması da bulunmaktadır.
Uygulamanıza Listeler ve Notlar Etki Alanı Desteği Ekleme
Tamam, şimdi SiriKit'in temellerini anladığımıza göre, çok sayıda yapılandırma ve işlemek istediğiniz her amaç için bir sınıf içeren bir uygulamada Siri için nasıl destek ekleyeceğinizi görelim.
Bu makalenin geri kalanı, bir uygulamaya Siri desteği eklemek için ayrıntılı adımlardan oluşur. Yapmanız gereken beş üst düzey şey var:
- Apple'ın geliştirici web sitesinde yeni yetkilendirmelere sahip ön hazırlık profilleri oluşturarak uygulamaya yeni bir uzantı eklemeye hazırlanın.
- Yetkileri kullanmak için uygulamanızı (
plist
aracılığıyla) yapılandırın. - Bazı örnek kodlarla başlamak için Xcode'un şablonunu kullanın.
- Siri amacınızı desteklemek için kodu ekleyin.
- Siri'nin kelime dağarcığını
plist
s ile yapılandırın.
Endişelenmeyin: Yol boyunca uzantıları ve yetkileri açıklayarak bunların her birini inceleyeceğiz.
Yalnızca Siri bölümlerine odaklanmak için basit bir yapılacaklar listesi yöneticisi, List-o-Mat hazırladım.
List-o-Mat örneğinin tam kaynağını GitHub'da bulabilirsiniz.
Bunu oluşturmak için tek yaptığım Xcode Master-Detail uygulama şablonuyla başlamak ve her iki ekranı da UITableView
haline getirmekti. Listeleri ve öğeleri eklemenin ve silmenin bir yolunu ve öğeleri tamamlandı olarak işaretlemenin bir yolunu ekledim. Tüm gezinme şablon tarafından oluşturulur.
Verileri depolamak için, yapıları JSON'a dönüştüren ve onu documents
klasöründeki bir metin dosyasına kaydeden (WWDC 2017'de tanıtılan) Codable
protokolünü kullandım.
Kodu kasten çok basit tuttum. Swift ile herhangi bir deneyiminiz varsa ve görünüm denetleyicileri yapıyorsanız, bununla ilgili bir sorununuz olmamalıdır.
Artık SiriKit desteği ekleme adımlarına geçebiliriz. Üst düzey adımlar, herhangi bir uygulama ve uygulamayı planladığınız etki alanı ve amaç için aynı olacaktır. Çoğunlukla Apple'ın geliştirici web sitesiyle ilgileneceğiz, plist
düzenleyeceğiz ve biraz Swift yazacağız.
List-o-Mat için, not alma uygulamaları ve yapılacaklar listeleri gibi şeylere genel olarak uygulanabilen listeler ve notlar alanına odaklanacağız.
Listeler ve notlar alanında, uygulamamız için anlamlı olabilecek aşağıdaki amaçlara sahibiz.
- Görevlerin bir listesini alın.
- Listeye yeni bir görev ekleyin.
Siri ile etkileşimler aslında uygulamanızın dışında gerçekleştiğinden (belki uygulamanız çalışmıyorken bile), iOS bunu uygulamak için bir uzantı kullanır.
Amaç Uzantısı
Uzantılarla çalışmadıysanız, üç ana şeyi bilmeniz gerekir:
- Uzatma ayrı bir işlemdir. Uygulamanızın paketi içinde teslim edilir, ancak kendi korumalı alanı ile tamamen kendi başına çalışır.
- Uygulamanız ve uzantınız, aynı uygulama grubunda yer alarak birbirleriyle iletişim kurabilir. En kolay yol, grubun paylaşılan sanal alan klasörleridir (böylece, onları oraya koyarsanız aynı dosyaları okuyup yazabilirler).
- Uzantılar kendi uygulama kimliklerini, profillerini ve yetkilendirmelerini gerektirir.
Uygulamanıza bir uzantı eklemek için geliştirici hesabınıza giriş yaparak ve "Sertifikalar, Tanımlayıcılar ve Profiller" bölümüne giderek başlayın.
Apple Geliştirici Uygulaması Hesap Verilerinizi Güncelleme
Apple geliştirici hesabımızda yapmamız gereken ilk şey bir uygulama grubu oluşturmak. "Tanımlayıcılar" altındaki "Uygulama Grupları" bölümüne gidin ve bir tane ekleyin.
group
ile başlamalı ve ardından normal ters etki alanı tabanlı tanımlayıcınız gelmelidir. Ön eki olduğundan, geri kalanı için uygulamanızın tanımlayıcısını kullanabilirsiniz.
Ardından, bu grubu kullanmak ve Siri'yi etkinleştirmek için uygulamamızın kimliğini güncellememiz gerekiyor:
- "Uygulama Kimlikleri" bölümüne gidin ve uygulamanızın kimliğini tıklayın;
- "Düzenle" düğmesini tıklayın;
- Uygulama gruplarını etkinleştirin (başka bir uzantı için etkinleştirilmemişse).
- Ardından "Düzenle" düğmesini tıklayarak uygulama grubunu yapılandırın. Uygulama grubunu önceden seçin.
- SiriKit'i etkinleştirin.
- Kaydetmek için “Bitti”ye tıklayın.
Şimdi, uzantımız için yeni bir uygulama kimliği oluşturmamız gerekiyor:
- Aynı "Uygulama Kimlikleri" bölümünde yeni bir uygulama kimliği ekleyin. Bu, son ekiyle birlikte uygulamanızın tanımlayıcısı olacaktır. Son ek olarak yalnızca
Intents
kullanmayın çünkü bu ad Swift'de modülünüzün adı olacak ve daha sonra gerçekIntents
ile çelişecektir. - Bu uygulama kimliğini uygulama grupları için de etkinleştirin (ve grubu daha önce yaptığımız gibi ayarlayın).
Şimdi, Intents uzantısı için bir geliştirme ön hazırlık profili oluşturun ve uygulamanızın ön hazırlık profilini yeniden oluşturun. Bunları normalde yaptığınız gibi indirin ve kurun.
Artık profillerimiz yüklendiğine göre, Xcode'a gitmemiz ve uygulamanın yetkilerini güncellememiz gerekiyor.
Uygulamanızın Yetkilerini Xcode'da Güncelleme
Xcode'a geri dönün, proje gezgininde projenizin adını seçin. Ardından, uygulamanızın ana hedefini seçin ve “Yetenekler” sekmesine gidin. Orada, Siri desteğini açmak için bir anahtar göreceksiniz.
Listenin aşağısında, uygulama gruplarını açabilir ve yapılandırabilirsiniz.
Ayarı doğru yaptıysanız, uygulamanızın .entitlements
dosyasında şunu görürsünüz:
Şimdi nihayet Intents extension hedefini projemize eklemeye hazırız.
Intens Uzantısını Ekleme
Sonunda uzantıyı eklemeye hazırız. Xcode'da “Dosya” → “Yeni Hedef”i seçin. Bu sayfa açılır:
"Amaç Uzantısı"nı seçin ve "İleri" düğmesini tıklayın. Aşağıdaki ekranı doldurun:
Ürün adının, Apple geliştirici web sitesindeki amaç uygulama kimliğinde son eki yaptığınız şeyle eşleşmesi gerekir.
Bir amaç kullanıcı arayüzü uzantısı eklememeyi seçiyoruz. Bu, bu makalede ele alınmamıştır, ancak ihtiyacınız olursa daha sonra ekleyebilirsiniz. Temel olarak, Siri'nin görsel sonuçlarına kendi markanızı ve görüntü stilinizi koymanın bir yolu.
İşiniz bittiğinde, Xcode, Siri uygulamamız için başlangıç parçası olarak kullanabileceğimiz bir amaç işleyici sınıfı oluşturacaktır.
Intents Handler: Çözümle, Onaylama ve İşleme
Xcode, bizim için bir başlangıç noktası olan yeni bir hedef oluşturdu.
Yapmanız gereken ilk şey, bu yeni hedefi uygulama ile aynı uygulama grubunda olacak şekilde ayarlamaktır. Daha önce olduğu gibi, hedefin “Yetenekler” sekmesine gidin ve uygulama gruplarını açın ve grup adınızla yapılandırın. Aynı gruptaki uygulamaların, birbirleriyle dosya paylaşmak için kullanabilecekleri bir sanal alanı olduğunu unutmayın. Siri isteklerinin uygulamamıza ulaşması için buna ihtiyacımız var.
List-o-Mat, grup belge klasörünü döndüren bir işleve sahiptir. Paylaşılan bir dosyayı okumak veya yazmak istediğimizde kullanmalıyız.
func documentsFolder() -> URL? { return FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.com.app-o-mat.ListOMat") }
Örneğin, listeleri kaydettiğimizde şunu kullanırız:
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 }
Intents uzantısı şablonu, IntentHandler.swift
IntentHandler
bir dosya oluşturdu. Ayrıca bunu, uzantının plist
amaçların giriş noktası olacak şekilde yapılandırdı.
Bu aynı plist
, desteklediğimiz amaçları beyan etmek için bir bölüm göreceksiniz. INSearchForNotebookItemsIntent
adlı listeleri aramaya izin verenle başlayacağız. IntentsSupported
altındaki diziye ekleyin.
Şimdi IntentHandler.swift
gidin ve içeriğini şu kodla değiştirin:
import Intents class IntentHandler: INExtension { override func handler(for intent: INIntent) -> Any? { switch intent { case is INSearchForNotebookItemsIntent: return SearchItemsIntentHandler() default: return nil } } }
handler
işlevi, belirli bir amacı işlemek için bir nesneyi almak için çağrılır. Bu sınıftaki tüm protokolleri uygulayabilir ve self
döndürebilirsiniz, ancak daha iyi organize olması için her amacı kendi sınıfına koyacağız.
Birkaç farklı sınıfa sahip olmayı amaçladığımız için, aralarında paylaşmamız gereken kod için ortak bir temel sınıf verelim:
class ListOMatIntentsHandler: NSObject { }
Amaç çerçevesi, NSObject
devralmamızı gerektirir. Bazı yöntemleri daha sonra dolduracağız.
Arama uygulamamıza şununla başlıyoruz:
class SearchItemsIntentHandler: ListOMatIntentsHandler, INSearchForNotebookItemsIntentHandling { }
Bir amaç işleyicisi ayarlamak için üç temel adımı uygulamamız gerekir.
- Parametreleri çözün .
Gerekli parametrelerin verildiğinden emin olun ve tam olarak anlamadığınız her şeyi netleştirin. - İsteğin yapılabilir olduğunu onaylayın .
Bu genellikle isteğe bağlıdır, ancak her parametrenin iyi olduğunu bilseniz bile, yine de bir dış kaynağa erişmeniz veya başka gereksinimleriniz olabilir. - İsteği ele alın.
Talep edilen şeyi yapın.
Uygulayacağımız ilk amaç olan INSearchForNotebookItemsIntent
görev araması olarak kullanılabilir. Bununla başa çıkabileceğimiz istek türleri, "List-o-Mat'ta bakkal listesini göster" veya "List-o-Mat'te mağaza listesini göster" şeklindedir.
Bir yana: “List-o-Mat” aslında bir SiriKit uygulaması için kötü bir isim çünkü Siri, uygulamalarda kısa çizgilerle zor anlar yaşıyor. Neyse ki, SiriKit alternatif isimlere sahip olmamıza ve telaffuz sağlamamıza izin veriyor. Uygulamanın Info.plist
şu bölümü ekleyin:
Bu, kullanıcının “list oh mat” demesine ve bunun için tek bir kelime (tire olmadan) olarak anlaşılmasına izin verir. Ekranda ideal görünmüyor, ancak onsuz, Siri bazen “Liste” ve “Mat” ın ayrı kelimeler olduğunu düşünüyor ve çok karışıyor.
Çözüm: Parametreleri Bulma
Not defteri öğelerini aramak için birkaç parametre vardır:
- öğe türü (görev, görev listesi veya not),
- öğenin başlığı,
- öğenin içeriği,
- tamamlanma durumu (görevin tamamlandı olarak işaretlenip işaretlenmediği),
- ilişkili olduğu konum,
- ilişkili olduğu tarih.
Yalnızca ilk ikisine ihtiyacımız var, bu yüzden onlar için çözümleme işlevleri yazmamız gerekecek. INSearchForNotebookItemsIntent
, uygulamamız için yöntemlere sahiptir.
Yalnızca görev listelerini göstermeyi önemsediğimiz için, bunu öğe türü çözümüne sabit kodlayacağız. SearchItemsIntentHandler
içinde şunu ekleyin:
func resolveItemType(for intent: INSearchForNotebookItemsIntent, with completion: @escaping (INNotebookItemTypeResolutionResult) -> Void) { completion(.success(with: .taskList)) }
Bu nedenle, kullanıcı ne derse desin, görev listelerini arıyor olacağız. Arama desteğimizi genişletmek isteseydik, Siri'nin bunu orijinal ifadeden çözmeye çalışmasına ve ardından öğe türü eksikse completion(.needsValue())
kullanmasına izin verirdik. Alternatif olarak, neyin uyuştuğunu görerek başlıktan tahmin etmeye çalışabiliriz. Bu durumda, Siri ne olduğunu bildiğinde başarı ile tamamlardık ve birden fazla olasılık deneyeceğimiz zaman completion(.notRequired())
kullanırdık.
Başlık çözünürlüğü biraz daha zor. İstediğimiz şey, Siri'nin söylediklerinizle tam olarak eşleşen bir liste bulursa bir liste kullanmasıdır. Emin değilse veya birden fazla olasılık varsa, o zaman Siri'nin bunu çözmek için bizden yardım istemesini isteriz. Bunu yapmak için SiriKit, daha sonra ne olmasını istediğimizi ifade etmemize izin veren bir dizi çözünürlük sıralaması sağlar.
Bu nedenle, "Bakkal" derseniz, Siri'nin tam bir eşleşmesi olur. Ancak “Mağaza” derseniz, Siri eşleşen listelerden oluşan bir menü sunar.
Temel yapıyı vermek için bu fonksiyonla başlayacağız:
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) }
ListOMatIntentsHandler
temel sınıfında getPossibleLists(for:)
ve completeResolveListName(with:for:with:)
öğelerini uygulayacağız.
getPossibleLists(for:)
, Siri'nin bize ilettiği başlıkla gerçek liste adlarını bulanık eşleştirmeye çalışmalıdır.
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 }
Tüm listelerimizi dolaşıyoruz. Tam bir eşleşme elde edersek, onu döndürürüz ve değilse, bir dizi olasılık döndürürüz. Bu fonksiyonda, kullanıcının söylediği kelimenin bir liste adında yer alıp almadığını kontrol ediyoruz (yani, oldukça basit bir eşleşme). Bu, "Bakkal"ın "Bakkal" ile eşleşmesini sağlar. Daha gelişmiş bir algoritma, kulağa aynı gelen sözcükleri temel alarak eşleştirmeyi deneyebilir (örneğin, Soundex algoritması ile),
completeResolveListName(with:for:with:)
, bu olasılıklar listesiyle ne yapılacağına karar vermekten sorumludur.
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)) } }
Tam bir eşleşme bulursak, Siri'ye başardığımızı söyleriz. Kesin olmayan bir eşleşmemiz varsa, Siri'ye kullanıcıya doğru tahmin edip etmediğimizi sormasını söyleriz.
Birden fazla eşleşmemiz varsa, Siri'ye bir liste göstermesini ve kullanıcının bir liste seçmesine izin vermesini söylemek için completion(.disambiguation(with: possibleLists))
kullanırız.
Artık talebin ne olduğunu bildiğimize göre, her şeye bakmamız ve halledebileceğimizden emin olmamız gerekiyor.
Onaylayın: Tüm Bağımlılıklarınızı Kontrol Edin
Bu durumda, tüm parametreleri çözdüysek, isteği her zaman işleyebiliriz. Tipik confirm()
uygulamaları, harici hizmetlerin kullanılabilirliğini veya yetki düzeylerini kontrol edebilir.
confirm()
isteğe bağlı olduğundan, hiçbir şey yapamayız ve Siri, herhangi bir isteği çözümlenmiş parametrelerle işleyebileceğimizi varsayar. Açık olmak gerekirse, şunu kullanabiliriz:
func confirm(intent: INSearchForNotebookItemsIntent, completion: @escaping (INSearchForNotebookItemsIntentResponse) -> Void) { completion(INSearchForNotebookItemsIntentResponse(code: .success, userActivity: nil)) }
Bu, her şeyi halledebileceğimiz anlamına gelir.
Kolu: Yap
Son adım, talebi işleme koymaktır.
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) }
İlk olarak, başlığa göre listeyi buluyoruz. Bu noktada, resolveTitle
, tam bir eşleşme elde edeceğimizden emin oldu. Ancak bir sorun varsa, yine de bir başarısızlık döndürebiliriz.
Bir hatamız olduğunda, bir kullanıcı etkinliğini geçme seçeneğimiz var. Uygulamanız Handoff kullanıyorsa ve tam olarak bu tür bir istekle başa çıkmanın bir yolu varsa, Siri, isteği orada denemek için uygulamanızı ertelemeyi deneyebilir. Bunu yalnızca ses bağlamındayken yapmayacaktır (örneğin, “Hey Siri” ile başladınız) ve diğer durumlarda yapacağını garanti etmez, bu yüzden buna güvenmeyin.
Bu artık test edilmeye hazırdır. Xcode'daki hedef listesinden amaç uzantısını seçin. Ancak çalıştırmadan önce şemayı düzenleyin.
Bu, doğrudan bir sorgu sağlamanın bir yolunu getirir:
Dikkat, yukarıda bahsedilen tire sorunu nedeniyle “ListOMat” kullanıyorum. Neyse ki, uygulamamın adıyla aynı şekilde telaffuz ediliyor, bu yüzden çok fazla sorun olmamalı.
Uygulamaya geri döndüğümde bir “Bakkal” listesi ve bir “Donanım Mağazası” listesi yaptım. Siri'den "mağaza" listesini istersem, şuna benzeyen anlam ayrımı yolundan geçer:
“Bakkal” derseniz, doğrudan sonuçlara giden tam bir eşleşme elde edersiniz.
Siri Üzerinden Öğe Ekleme
Artık çözümleme, onaylama ve işleme temel kavramlarını bildiğimize göre, listeye bir öğe ekleme niyetini hızla ekleyebiliriz.
İlk olarak, uzantının INAddTasksIntent
ekleyin:
Ardından, IntentHandler
handle
işlevimizi güncelleyin.
override func handler(for intent: INIntent) -> Any? { switch intent { case is INSearchForNotebookItemsIntent: return SearchItemsIntentHandler() case is INAddTasksIntent: return AddItemsIntentHandler() default: return nil } }
Yeni sınıf için bir saplama ekleyin:
class AddItemsIntentHandler: ListOMatIntentsHandler, INAddTasksIntentHandling { }
Bir öğe eklemek, başlık yerine hedef görev listesi dışında, arama için benzer bir resolve
gerektirir.
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
, completeResolveListName
gibidir, ancak biraz farklı türlere sahiptir (görev listesinin başlığı yerine bir görev listesi).
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)) } }
Aynı belirsizlik giderme mantığına sahiptir ve tamamen aynı şekilde davranır. "Mağaza" kelimesinin anlamı netleştirilmeli ve "Bakkal" demek tam bir eşleşme olacaktır.
confirm
uygulamadan bırakacağız ve varsayılanı kabul edeceğiz. handle
için listeye bir öğe eklememiz ve kaydetmemiz gerekiyor.
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) }
Bir öğe listesi ve bir hedef listesi alıyoruz. Listeye bakıp öğeleri ekliyoruz. Ayrıca Siri'nin eklenen öğelerle birlikte göstermesi ve tamamlama işlevine göndermesi için bir yanıt hazırlamamız gerekiyor.
Bu işlev, “ListOMat'ta alışveriş listesine elma ekleyin” gibi bir ifadeyi işleyebilir. Ayrıca "pirinç, soğan ve zeytin" gibi öğelerin bir listesini de işleyebilir.
Neredeyse Tamamlandı, Sadece Birkaç Ayar Daha
Bunların tümü simülatörünüzde veya yerel cihazınızda çalışır, ancak bunu göndermek istiyorsanız, uygulamanızın plist
Siri'yi ne için kullandığınızı açıklayan bir dizeyle bir NSSiriUsageDescription
anahtarı eklemeniz gerekir. “Listelerle ilgili istekleriniz Siri'ye gönderilecek” gibi bir şey sorun değil.
Ayrıca bir arama eklemelisiniz:
INPreferences.requestSiriAuthorization { (status) in }
Kullanıcıdan Siri erişimi istemek için bunu ana görünüm denetleyicinizin viewDidLoad
koyun. 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).
Özet
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.
Daha fazla okuma
- “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.