IntentKit 是一种更易于处理 iOS 应用中第三方 URL 方案的方式。
在 iOS 中,链接到第三方应用基本上是崩溃的。假设您作为开发者,想要允许用户在使用 Google Maps 打开地图链接而不是内置 Maps.app。现在您需要编写一大堆自定义代码来决定 Google Maps 是否已安装,询问用户他们更喜欢哪个,并且理想情况下记住这个偏好。
如果我们考虑一个更复杂的例子,比如 Twitter 客户端,您现在可能正在管理一 dozen 个截然不同且可能文档记录很差的第三方 URL 方案。
因此,很少有几款应用会链接到外部第三方应用来处理苹果提供的更容易链接的应用所处理的任务,即使用户更喜欢第三方应用。
IntentKit 试图解决这个问题。
对于用户,它提供了一个美观的选择界面来选择在哪个第三方应用中执行操作。
对于开发者,它提供:
基于语义动作的优雅、连贯的 API。您无需手动构建 URL 或创建模态视图控制器来显示,只需告诉它您想要做什么。无需手动检查用户安装了哪些应用程序,IntentKit 会自动为您查询设备以确定可执行特定动作的应用程序。
第三方 URL 方案的统一、可读性强的仓库。每个应用程序的 URL 方案都是一个纯文本 plist。您只需添加一行代码即可在不编写任何代码的情况下支持您的应用程序。
运行 pod install
后,您应该能够 #import 任何 INKHandler 头文件(例如 #import <INKMailHandler.h>
),并开始使用。
如果您担心应用程序包大小的增加,您可以只包含 IntentKit 支持的应用程序的子集。每个处理类都有子规格。
# Only includes web browsers
pod "IntentKit/Browsers"
有关可用的子规格的更多信息,请参阅项目的 Podspec。
截至 iOS 9,苹果要求您将所有计划在应用程序中使用的第三方 URL 方案列入白名单。这意味着使用 IntentKit 时,需要在您的 info.plist
文件中添加它可能使用的所有 URL 方案,作为具有键 LSApplicationQueriesScheme
的数组。例如,在这里添加 Google Maps 支持看起来是这样的。
<key>LSApplicationQueriesSchemes</key>
<array>
<string>comgooglemaps</string>
<string>comgooglemaps-x-callback</string>
</array>
目前,您必须手动查找您使用的IntentKit处理器可能查询的URL方案(检查《Apps》文件夹中嵌套的各种plist
文件。我们明白这很麻烦。将来,IntentKit可能会提供一个针对每个处理器的必选URL方案的固定列表,甚至提供一种方式来自动将其添加到您的Info.plist
中。
IntentKit在Swift和Objective-C中都表现得很出色。
如果您使用Swift,建议您在CocoaPods中启用use_frameworks!
选项。从此处开始,只需从您希望在其中使用IntentKit的任何Swift文件中导入IntentKit
(或等效子规范)。
要使用IntentKit,请首先实例化一个处理器对象。IntentKit自带一些处理器对象,每个对象都具有对特定类型应用程序的领域知识。例如,INKBrowserHandler
、INKMapsHandler
和INKTwitterHandler
分别用于在网页浏览器中打开链接、地图应用程序和Twitter客户端。
创建新的处理器对象后,只需告诉它你想执行的操作。它将返回一个特殊对象,称为呈现器,可以用来实际执行操作。以下是如何在iPhone上打开电子邮件编写屏幕的方法
INKMailHandler *mailHandler = [[INKMailHandler alloc] init];
[[mailHandler sendMailTo:@"[email protected]"] presentModally];
如果用户没有安装任何第三方邮件应用(如Mailbox或Google的Gmail应用),这将显示一个内联MFMailComposeViewController
,就像您自己创建了并呈现一样。如果用户安装了其他邮件应用,这将显示一个类似UIActivityViewController
的模态表单,列出每个可用的应用程序。它还会提供他们可以点击的开关,以记住该类型未来链接的选择。
根据您的应用程序和用户基础,这种用户体验可能不是完美的。如果你的99%的用户想要使用Apple默认的,为什么他们要额外点击一次呢?
每个INKHandler
对象都有一个名为useSystemDefault
的属性。如果您将其设置为YES
,执行INKHandler操作将不会显示自定义UI。相反,系统将静默选择一个应用程序来处理请求。每个处理器类型都选择了合理的默认值:所有具有Apple提供的应用程序的处理程序都将使用该应用程序,而基于第三方服务(例如Twitter)的处理程序则默认使用第一方应用程序。
如果您使用此方法呈现IntentKit,建议您为用户提供设置自己的默认值的方法。IntentKit提供了一个名为INKDefaultsViewController
的视图控制器,允许用户设置首选项。只需创建一个新的INKDefaultsViewController
对象,可选地限制要显示的处理程序类型,然后在屏幕上呈现它。
INKDefaultsViewController *defaultsController = [[INKDefaultsViewController alloc] init];
defaultsController.allowedHandlers = @[[INKBrowserHandler class], [INKMailHandler class]];
[self pushViewController:defaultsController animated:YES];
当您没有手动限制处理器类型时,它看起来像这样
如果您希望对用户体验有更多的控制,IntentKit还提供API挂钩以设置自己的默认值。每个INKHandler对象都有一个promptToSetDefault
方法,它将返回一个处理提示用户选择应用程序的INKActivityPresenter
对象。为了实现更底层的控制,可以使用INKApplicationList
和INKDefaultsManager
类来获取可用应用程序的列表并手动设置默认值。
一些处理器具有可选配置参数。例如,在链接到地图应用程序时,您可以指定地图应居中何处以及应缩放到什么程度;无论您是在搜索地点、获取逐个转折的路线还是进行由处理器支持的其他操作,这些选项都将生效。
INKMapsHandler *mapsHandler = [[INKMapsHandler alloc] init];
mapsHandler.center = CLLocationCoordinate2DMake(42.523, -73.544);
mapsHandler.zoom = 14;
[mapsHandler directionsFrom:@"Washington Square Park" to:@"Lincoln Center"];
这正是 IntentKit 显示其实力的地方。这为您提供了一个干净、语义化的 API 来构建链接,而不是手动拼凑 URL,无论用户是想使用 Apple Maps 还是第三方应用程序。
项目文档中提供了可用处理器及其方法的最新列表和配置选项。
如果您使用 presentModally
,它将尝试智能地确定要呈现哪个视图控制器。可能它不会自动选择正确的选项;如果是这样,您可能需要显式指定正确的视图控制器。
NSURL *url = [NSURL URLWithString:@"http://www.google.com/"]
INKBrowserHandler *browserHandler = [[INKBrowserHandler alloc] init];
INKActivityPresenter *presenter = ][browserHandler openURL:url];
[presenter presentModalActivitySheetFromViewController:self];
如果您的应用程序是通用应用程序或仅适用于 iPad,如果您要在 IntentKit 的 INKActivityViewController
中显示内容,可能希望将其显示为弹出视图而不是模态表单。以下代码将在 iPhone 上以模态方式显示,并在 iPad 上作为 UIPopoverController
显示。
NSURL *url = [NSURL URLWithString:@"http://www.google.com/"]
INKBrowserHandler *browserHandler = [[INKBrowserHandler alloc] init];
INKActivityPresenter *presenter = [browserHandler openURL:url];
[presenter presentActivitySheetFromViewController:self
popoverFromRect:someRect
inView:self.view
permittedArrowDirections:UIPopoverArrowDirectionAny
animated:YES];
所有这些选项都将直接传递到一个 UIPopoverController 中。类似地,也存在一个 presentActivitySheetFromViewController:popoverFromBarButtonItem:permittedArrowDirections:animated:
方法,如果适用,将调用等效的 UIPopoverController 方法。
如果用户没有安装任何可执行操作的适当应用程序,IntentKit 会尝试使用网页浏览器作为后备。例如,如果用户尝试执行涉及 Twitter 的操作但没有安装 Twitter 客户端,IntentKit 将尝试加载相应的 twitter.com
URL。通过呈现一个 INKBrowserHandler
来实现这一点,以便用户可以选择他们首选的网页浏览器。
如果您不想这种行为,可以在调用操作之前将处理器的 useFallback
属性设置为 NO
来禁用它。
值得关注的是,IntentKit 的默认网页浏览器是一个应用内的模态 UIWebView。这既适用于由 INKBrowserHandler
触发的操作,也适用于其他处理器回退到网页 URL 的操作。如果您不想这样做,而是想要在网页操作中回退到 Safari,可以将处理器的 disableInAppOption
属性设置为 NO
。
文档可以在线查看,请访问 CocoaDocs。
或者,您可以在 docs
目录中找到文档,通过从根目录运行 script/generate-docs.sh
实现。如果您这样做,请注意,文档将从您的当前代码副本生成,它可能不同于 CocoaPods 上的最新标记版本。
已提供演示应用程序,供您查看 IntentKit 的使用情况。
pod install
。Example/IntentKitDemo.xcworkspace
。此示例允许您执行 IntentKit 所支持的所有操作。
如果您只安装了一个可以执行任务的 App,IntentKit 默认会直接打开该 App,而不是提示用户选择。在示例应用程序中,有一个切换器,如果至少有一个应用程序可用,它会始终显示选择用户界面。建议您在实际安装了第三方应用的 iOS 设备上运行示例,但如果必须在使用模拟器,该切换器将允许您看到选择用户界面是什么样子的。
扩展 IntentKit 很简单。
在 IntentKit/Apps/
目录内,创建一个以您的应用命名的新的子目录。
在该目录内,创建一个 plist 文件。它的名称应该是应用的名字(英文),其根对象应为字典。
在此字典中,应该存在一个代表应用本地化名称的 name
键。如果您的应用名称在不同地区没有变化,它的值应该是一个包含该名称的字符串。
<key>name</key>
<string>Safari</string>
如果它改变了,值应该是将 IETF BCP 47 语言标识符映射到本地化名称的字典。
<key>name</key>
<dict>
<key>en</key>
<string>Sina Weibo</string>
<key>zh-Hans</key>
<string>新浪微博</string>
</dict>
另外,应该有一个包含您的应用程序可以执行的所有操作的 actions
字典。此字典应将表示 INKHandler
方法的字符串映射到用于生成这些方法 URL 的模板字符串。在这些模板字符串中,双大括号包裹的变量(如 Mustache 风格的 {{name}}
)会在运行时进行插值。与 Mustache 一样,双大括号包裹的变量(如 {{{url}}}
)会被 URL 编码,而三重大括号包裹的变量则不会被。例如
<key>actions</key>
<dict>
<key>searchForLocation:</key>
<string>comgooglemaps://?q={{query}}</string>
</dict>
通常,模板变量键的名称与相应处理方法参数名称相同,但目前没有任何强制要求这样做。建议查看响应同一类操作的其他应用的 plist 文件,以了解正确的模板键。
如果您应用程序支持在处理程序中未表示的操作,或将包含在一个当前没有处理程序的类应用程序中,您将必须编写代码来添加支持。当前的处理程序代码易于阅读;参考现有处理程序子类可作为创建自己的处理方法或 INKHandler
子类的模板。
您的应用程序图标应放在同一目录中。您需要一个与 plist 文件相同的根文件名的四个图标副本
AppName.png
: 60x60AppName~ipad.png
: 76x76[email protected]
: 120x120AppName@2x~ipad.png
: 152x152请使用在应用程序的 Xcode 项目中使用的相同方形图标;IntentKit 将负责对其进行遮罩,以便它们显示为 iOS 风格的圆角矩形/超椭圆形。文件名的基本名(如示例中的 "AppName")必须与 plist 文件的文件名完全匹配。
在 IntentKit 代码库的根目录中,运行 pod install
。这将导致 Xcode 捕获您添加的所有新文件。接下来,运行 rake
来运行测试套件,该套件包括检查器以确保您在 plist 中定义的每个操作都对应于有效的处理程序操作。您还可能希望在真实 iOS 设备上运行示例应用程序,以确保所有链接按预期工作。
在 IntentKit.podspec
中,将您的应用程序添加到对应于您的应用程序响应的处理程序的 subspec 中。只需将您的应用程序文件夹名称添加到适当资源包文件的全局列表中的其他应用程序文件夹名称即可。
提交拉取请求!
添加您自己的模态操作类似于添加您自己的URL方案,但也有几个例外。
所有可显示的IntentKit活动都必须遵守INKPresentable
协议,该协议定义了两种方法:一个用于返回是否可以执行指定操作,另一个用于执行操作。后一个方法传递一个视图控制器来以模态方式呈现您的视图控制器;它应该呈现您的视图控制器,并在操作完成后妥善关闭它。
还必须在您应用的国际意图工具包plist文件中进行一些其他更改
actions
应是一个操作名的数组,而不是一个字典。
应该有一个名为className
的字段,列出您的INKPresententable
类的名称。
name
字段应指您希望活动在应用程序内列表中显示的内容。例如,INKMailSheet
(显示MFMailComposeViewController的活动)的name
为“应用内”。
IntentKit需要Xcode 5、针对iOS 7.0及以上。
欢迎所有贡献!如果您想帮忙但不知道从何开始,添加对新第三方应用程序的支持可以是一个很好的起点(通常不需要编写任何代码)。
强烈鼓励对新增代码进行测试和编写文档。我们使用appledoc进行文档和Specta)进行测试。
IntentKit初始版本的目标仅仅是创建一种简单的方式来集成第三方应用链接,无需大量的样板代码。以下是其未来可扩展的多种方法(非完列表)。
Mike Walker
IntentKit的初始版本是在黑客学校构建的。
IntentKit可以在MIT许可下使用。有关更多信息,请参阅LICENSE文件。