SiriKit 教程 Part 1:在 iOS 10 里集成 Siri

原文链接 http://jamesonquave.com/blog/adding-siri-to-ios-10-apps-in-swift-tutorial/
作者 Jameson Quave
原文日期 2016-06-14
译者 Crystal Sun

这篇教程写于 2016 年 6 月 13 日,使用 Xcode 8 Beta 1 和 Swift 3.0 toolchain(工具链)。

下载 Xcode 8,配置 iOS 10 和 Swift 3

如果你还没有下载 Xcode 8 Beta 1,请到这里下载。

(可选)命令行编译

不需要改变什么设置就能够加入 Swift 3.0 工具链,除非你想用命令行编译工程。如果你想使用这个功能,先打开 Xcode beta,然后从顶部菜单栏中选择 Xcode > Preferences,接着选择 Location tab,在页面的底部,你会看到 “Command Line Tool” 这行设置,请在这里选择 Xcode 8.0。

现在,在 Terminal 使用命令行找到工程所在的文件夹,调用 xcodebuild 命令行就可以编译工程了。

(可选)移植现有的 Swift 2 应用

如果你想对一个已使用 Swift 2.0 开发的工程引入 Siri 功能,需要点击工程,选择 Build Settings,在 Swift Compiler - Version 下面,找到 Use �Legacy Swift Language Version 选项,设置成 No。这会造成编译器报错,然后你可以根据这些报错信息来修改代码,推荐你使用这个设置来跟进更新代码,以适应 Swift 不断进化的语义。

开始使用 SiriKit

首先,在你的 App(或者是新建一个 single-view Swift 模板工程),点击顶部的工程,然后点击左侧下方的 + 按钮,在这里

(译者注:我在这里添加了一张图片,能够说的更明白),弹出窗口,选择 iOS > Application Extension,接着选择 Intents Extension

这样就给工程添加了一个新的 intent,用于监听 Siri 的命令。其中的 Product Name 应该和你的工程文件名字相似,比如,你的 App 名为 MusicMatcher,你可以把这个 intent 的名字命名为 MusicMatcherSiriIntent。一定要选中 Include UI Extension 选项,我们之后会用到,这也是添加额外扩展的最简单的方法。


我刚刚创建了两个新的 target,你能从工厂的文件层级上看出来。找到 Intent 文件夹下的 IntentHandler.swift 文件,看一下这里面的样本代码。默认会提供一些示例代码,允许用户说一下诸如“使用 MusicMatcher 开始锻炼”的命令,MusicMatcher 是 App 的名字。

运行模板应用

这个时候最好编译一下代码,然后在 iOS 真机上试一下命令。继续,编译应用的 target,从 Scheme 下拉菜单里选择 MusicMatcher,然后选择真机,点击 Run。

你看你会看到一个空白的应用出现,你使用的扩展这时会在后台加载到设备的系统文件里,现在点击 Stop 按钮来关闭应用。

接下来,找到你的 scheme,选择 Intent target,点击 Run。

这时会出现一个弹出框,问你需要连接哪个应用,选择你刚刚运行的应用:MusicMatcher。这会让真机上再次出现这个应用(还是一个空白的应用),不过这次调试台(debugger)中会出现连接的 Intent 扩展。

现在点击 home 按钮回到首屏,或者应用可能自己就退出了,因为你正在运行的是 Intent,不是应用本身(这不是崩溃!!!)。

启用扩展

扩展都已安装就位了,但是作为一个 iOS 用户,仍然需要进行 Siri 设置才能使用扩展。点击测试设备里的设置,选择 Siri 菜单,你会看到 MusicMatcher 出现在清单里,激活允许 MusicMatcher 使用 Siri。

测试我们第一个 Siri 命令

尝试一下 Siri 命令,长按 Home 键或者说出“Hey Siri”来激活Siri。(当然需要你已经激活“Hey Siri”功能)

试一下命令,比如“使用 MusicMatcher 开始锻炼”。

“对不起,你需要在应用里继续”

如果你像我一样遇到了这样的错误信息:“Sorry, you'll need to continue in the app.”(不知道什么原因,偶尔地这不算是一个问题,什么鬼?)

在 console 你可能会看到类似的信息:(这原文里的代码还有各种颜色,请校对定稿的时候留意一下)

dyld: Library not loaded: @rpath/libswiftCoreLocation.dylib
  Referenced from: /private/var/containers/Bundle/Application/CC815FA3-EB04-4322-B2BB-8E3F960681A0/LockScreenWidgets.app/PlugIns/JQIntentWithUI.appex/JQIntentWithUI
  Reason: image not found
Program ended with exit code: 1

我们还需要在工程里添加 CoreLocation 库,确保能够复制我们编译过 Swift 代码。(复制编译的代码?感觉翻译的不对)

再次选择工程根目录,选择 MusicMatcher target。在 General 底下找到 Linked Frameworks and Libraries。点击 + 按钮,添加 CoreLocation.framework。现在可以再次编译在真机上运行,接着照着上面相同的步骤再次编译运行 intent target。

最后,从手机桌面激活 Siri。

“Hey Siri!”
“Start my workout using MusicMathcer”(这应该是固定的命令,换成中文就不能用了吧)

Siri 这时候应该会回应:“OK.exercise started on MusicMathcer”(OK,开始用 MusicMathcer 锻炼身体),会出现一个 UI 界面说 “Workout Started”(锻炼开始)

它是如何工作的呢?

模板中的 IntentHandler 类使用了一长串的协议:

首先最主要的就是 INExtension,允许我们一开始就把类当作一个 intent extension 来用。剩下的协议都是 intent handler 类型,在类里能够回调:

INStartWorkoutIntentHandling
INPauseWorkoutIntentHandling
INResumeWorkoutIntentHandling
INCancelWorkoutIntentHandling
INEndWorkoutIntentHandling

第一个就是我们刚刚测试过的,INStartWorkoutIntentHandling

按住 Command 键鼠标点击这些协议的名字,会看到苹果文档:

/*!
 @brief Protocol to declare support for handling an INStartWorkoutIntent 
 @abstract By implementing this protocol, a class can provide logic for resolving, confirming and handling the intent.
 @discussion The minimum requirement for an implementing class is that it should be able to handle the intent. The resolution and confirmation methods are optional. The handling method is always called last, after resolving and confirming the intent.
 */

换句话说,这协议告诉 SiriKit 我们准备处理英文句子 “Start my workout with AppName Here.”

根据用户使用的不同语言,这句话翻译成对应的语言,不过最终的目的都是开始锻炼。INStartWorkoutIntentHandling 协议调用几个方法,在示例代码里执行这些方法。如果你想创建一个锻炼应用,我会让你更多地了解他们,不过我更愿意做的是,在本教程的剩余部分,添加一个新的 intent handler,来处理发送消息。

加一个新的信息 Intent

现在我们确信刚刚的设置工作完成了,让我们继续往下走,添加一个新的 intent 类型,用于发送消息,这里的文档说明了下列信息:

Send a message
Handler:INSendMessageIntentHandling protocol
Intent:INSendMessageIntent
Response:INSendMessageIntentResponse

在类里添加 INSendMessageIntentHandling 协议,首先要明确,我们把它添加到类协议清单里,也就是在 IntentHandler.swift 文件里。由于实际上我不想使用这些 intent,所以我会删除它们,只留下这一个:

class IntentHandler: INExtension, INSendMessageIntentHandling {
    ...

如果这时候编译,是不会通过编译的,因为我们还需要实现一些遵守 INSendMessageIntentHandling 协议所必需的方法。

另外,如果你需要核对具体是哪些方法,只需要按住 Command 键然后鼠标点击 INSendMessageIntentHandling,然后看一下哪些方法前面没有 optional 关键词即可。

在这里,我们发现只有一个必需要有的方法:

/*!
 @brief handling method
 
 @abstract Execute the task represented by the INSendMessageIntent that's passed in
 @discussion This method is called to actually execute the intent. The app must return a response for this intent.
 
 @param  sendMessageIntent The input intent
 @param  completion The response handling block takes a INSendMessageIntentResponse containing the details of the result of having executed the intent
 
 @see  INSendMessageIntentResponse
 */
public func handle(sendMessage intent: INSendMessageIntent, completion: (INSendMessageIntentResponse) -> Swift.Void)

连接新的协议

回到 IntentHandler.swift 文件,添加一行分隔符(借助 jump bar,在导航查找代码时这个分隔符会非常有用)

// MARK: - INSendMessageIntentHandling

在 MARK 底下,我们实现方法。我发现 Xcode 8 非常有帮助,主要敲击方法名字的开头,剩下的都交给自动补全来完成了,选择对应的方法。

在 handerl 里,我们需要创建一个 INSendMessageIntentResponse,来回调闭包。先假设所有的信息发送都很成功,在 INSendMessageIntentResponse 里返回一个用户活动的成功值,和模板中如何实现的非常类似。还需要添加一个 print 方法,当 handler 方法被 Siri 事件触发后我们就能知晓啦:

func handle(sendMessage intent: INSendMessageIntent, completion: (INSendMessageIntentResponse) -> Void) {
    print("Message intent is being handled.")
    let userActivity = NSUserActivity(activityType: NSStringFromClass(INSendMessageIntent))
    let response = INSendMessageIntentResponse(code: .success, userActivity: userActivity)
    completion(response)
}

把这个 intent 类型添加到 Info.plist

在具备处理 INSendMessageIntent 方法之前,我们需要在 Info.plist 文件里添加一些值,就当作是应用的授权吧。

intent 的 Info.plist 文件里,找到并扩展 NSExtension 键。接着扩展 NSExtensionAttributes,然后是 IntentsSupported,我们需要给 INSendMessageIntent 新添加一行,允许应用处理信息 intents。

测试新的 intent

现在我们已经设置好了新的 intent,来测试一下。记住,你必须先编译 App,在真机上运行,接着运行扩展进行调试,如果你不这样做,扩展要么不会工作,要么不会在 Xcode 的控制台中打印日志。

调用 Siri 的intent,你现在可以看到会出现一个新的信息窗口,这个窗口目前还是空的,毕竟我们还没有给应用编写什么逻辑,我们需要实现剩下的调用,还要添加一些信息的逻辑,实现更好的用户体验。我们会在 Part 2 (Part 2 已经发布了)里解决这些事情。

本文由 SwiftGG 翻译组翻译,已经获得作者翻译授权。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,425评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,058评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,186评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,848评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,249评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,554评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,830评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,536评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,239评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,505评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,004评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,346评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,999评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,060评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,821评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,574评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,480评论 2 267

推荐阅读更多精彩内容