URL Scheme 就像“http://…”或“ftp://…”。这些看起来像是非常底层的概念,您对其几乎没有控制权,但实际上,您确实可以!听起来像是异国情调的话题其实一点也不奇特:我们每天都在不断地使用不同的 URL Scheme。例如,当我们点击指向启动 AppStore 的 iPhone 应用的链接时。或者当朋友向我们发送播放列表链接并在 Spotify 桌面应用中打开时。
在以下简短教程中,我们将了解自定义 URL Scheme 在 macOS 和 iOS 上的工作原理。
URL Scheme 和文档类型
任何 macOS 或 iOS 应用程序都可以将自身注册为任何 URL Scheme(如“http”或“https”)或文档类型(如“txt”文件)的处理程序。但是,除了这些经典方案之外,应用程序还可以注册自己的自定义 URL Scheme 或文档格式。
如果应用程序想要表明它支持某种文档类型或 URL Scheme,则必须适当地配置其“Info.plist”文件:CFBundleDocumentTypes
键列出了应用程序支持的文档类型,而 CFBundleURLTypes
用于支持的 URL Scheme。
在您自己的应用程序中,您可以通过 Xcode 的项目设置轻松配置此项: “Info”选项卡提供了“文档类型”和“URL 类型”这两个部分。URL Scheme 可以是任何我们喜欢的字符串(只要它保持有效的 URL 格式)。

这使应用程序能够使用配置的类型,例如,当使用“打开方式”从 Finder 打开文件或在 iOS 上将文档从一个应用程序传递到另一个应用程序时。
自定义 URL Scheme 的用例
通常,注册您自己的自定义 Scheme 允许您将事件直接路由到您的应用程序。当用户打开使用此 Scheme 的 URL 时。例如,让我们看看 Tower,这是我的团队制作的 Git 桌面客户端:在您的机器上打开链接“gittower://openRepo/http://github.com/jquery/jquery.git”将启动 Tower 并打开“克隆”对话框,并预先填充相应的克隆 URL

另一个用例是为我们的用户简化 Tower 的注册流程。购买许可证后,我们的客户会收到一封包含如下链接的电子邮件:“gittower://activateLicense/CODE/NAME”
这将启动 Tower(或将其置于前台)并打开注册对话框,其中预先填充了许可证信息。这比笨拙地复制粘贴(然后发现您错过了字符或包含了不需要的字符……)要舒服得多。

在 iOS 上,用例非常相似:应用程序也利用自定义 URL Scheme 来启动应用程序,然后在应用程序内显示特定屏幕。
长话短说:自定义 URL 是深度链接到您的应用程序的好方法!
示例应用
让我们动手创建一个处理自定义 URL Scheme 的应用程序。让我们将应用程序命名为 CustomURLScheme
,并让它处理一个名为(当然!)foo
的 Scheme。
本简短教程的示例代码可以 在这里找到。
注册我们的自定义 URL Scheme
第一步是在项目的 Info.plist 文件中注册应用程序作为我们自定义 URL Scheme 的处理程序。
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>CFBundleURLName</key>
<string>com.example.CustomURLScheme</string>
<key>CFBundleURLSchemes</key>
<array>
<string>foo</string>
</array>
</dict>
</array>
因此,我们郑重其事地提出担任 foo
URL Scheme 的“查看器”角色。
有关所有可能的配置键的更多信息和详细说明,您可以查看 Apple 的属性列表键参考。
处理来自 URL Scheme 的事件
下一步是告诉我们的应用程序如何处理通过我们的 URL Scheme 传入的事件。
对于 iOS 应用程序,这就像实现以下委托一样简单
func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool
对于 macOS 应用程序,我们需要告诉 NSAppleEventManager
我们的应用程序希望接收打开 URL 的事件,并提供一个回调方法来处理该事件。
我们首先在 AppDelegate
类中创建一个具有预期签名的空方法
class AppDelegate: NSObject, NSApplicationDelegate {
func applicationDidFinishLaunching(_ aNotification: Notification) {
}
func applicationWillTerminate(_ aNotification: Notification) {
}
func handleAppleEvent(event: NSAppleEventDescriptor, replyEvent: NSAppleEventDescriptor) {
}
}
然后我们从 applicationDidFinishLaunching
调用 NSAppleEventManager
的 setEventHandler
方法,如下所示
func applicationDidFinishLaunching(_ aNotification: Notification) {
NSAppleEventManager.shared().setEventHandler(self, andSelector: #selector(self.handleAppleEvent(event:replyEvent:)), forEventClass: AEEventClass(kInternetEventClass), andEventID: AEEventID(kAEGetURL))
}
现在,如果您构建并运行应用程序,事件将正确传递到我们的回调方法 - 但它仍然为空并且不会执行任何操作。
回调方法将传入事件作为 event: NSAppleEventDescriptor
接收。NSAppleEventDescriptor
具有许多属性和方法。如果您只关心 URL,则以下实现将可以解决问题
func handleAppleEvent(event: NSAppleEventDescriptor, replyEvent: NSAppleEventDescriptor) {
guard let appleEventDescription = event.paramDescriptor(forKeyword: AEKeyword(keyDirectObject)) else {
return
}
guard let appleEventURLString = appleEventDescription.stringValue else {
return
}
let appleEventURL = URL(string: appleEventURLString)
print("Received Apple Event URL: \(appleEventURL)")
}
因此,macOS 的最终实现如下所示
class AppDelegate: NSObject, NSApplicationDelegate {
func applicationDidFinishLaunching(_ aNotification: Notification) {
NSAppleEventManager.shared().setEventHandler(self, andSelector: #selector(self.handleAppleEvent(event:replyEvent:)), forEventClass: AEEventClass(kInternetEventClass), andEventID: AEEventID(kAEGetURL))
}
func applicationWillTerminate(_ aNotification: Notification) {
}
func handleAppleEvent(event: NSAppleEventDescriptor, replyEvent: NSAppleEventDescriptor) {
guard let appleEventDescription = event.paramDescriptor(forKeyword: AEKeyword(keyDirectObject)) else {
return
}
guard let appleEventURLString = appleEventDescription.stringValue else {
return
}
let appleEventURL = URL(string: appleEventURLString)
print("Received Apple Event URL: \(appleEventURL)")
}
}
构建并运行应用程序,它应该将接收到的 URL 打印到调试控制台。
获得 URL 后,您可以将其转换为您希望应用程序执行的操作。
将应用注册为默认处理程序
除了我们自己的 gittower
Scheme 之外,Tower 还支持另外两个 Scheme:github-mac
和 sourcetree
,因为这些 Scheme 用于 github.com 和 bitbucket.com 在桌面应用程序中打开克隆 URL。当然,我们不会“盲目地”覆盖其他处理程序!用户可以明确选择让 Tower 处理来自 GitHub 和 Bitbucket 的这些 URL。

这是使用 CoreServices 框架的一个有趣部分完成的,即 Launch Services API。虽然 API 是用 C 编写的,但为所需方法编写 Swift 包装器非常容易
import Foundation
import CoreServices
class LaunchServices {
class func applicationsForURLScheme(scheme: String) -> Array<String> {
if let applications = LSCopyAllHandlersForURLScheme(scheme as CFString) {
return applications.takeUnretainedValue() as Array<AnyObject> as! Array<String>
}
return []
}
class func defaultApplicationForURLScheme(scheme: String) -> String? {
if let defaultApplication = LSCopyDefaultHandlerForURLScheme(scheme as CFString) {
return defaultApplication.takeUnretainedValue() as String
}
return nil
}
class func setDefaultApplicationForURLScheme(bundleIdentifier: String, scheme: String) -> Bool {
let status = LSSetDefaultHandlerForURLScheme(scheme as CFString, bundleIdentifier as CFString)
return (status == 0)
}
}
此辅助类提供以下核心功能
applicationsForURLScheme
– 检索已声明支持特定 URL Scheme 的应用程序包标识符列表defaultApplicationForURLScheme
– 返回特定 URL Scheme 的当前默认处理程序的应用程序包标识符setDefaultApplicationForURLScheme
– 将特定 URL Scheme 的默认处理程序设置为新的应用程序包标识符
macOS 示例项目演示了如何使用此类:它显示特定 URL Scheme 的所有应用程序列表,并预先选择了默认应用程序(不用担心:在此示例中,使用选择输入不会更改默认值;它是只读的)。
继续,创建您自己的 Scheme
自定义 URL Scheme 是深度链接到您的应用程序并触发操作的好方法。它们易于设置(尤其是在 iOS 上),并为用户提供来自其他应用程序时的便捷快捷方式。
创建您自己的 URL Scheme,玩得开心!
这篇关于 macOS 应用的文章很棒,但对于 iOS 来说就没那么好了。
iOS 正在逐渐放弃自定义 URL Scheme 并转向通用链接: https://developer.apple.com/ios/universal-links/
主要区别在于,URL Scheme 仅在安装了应用程序时才有效。通用链接作为指向网页的常规链接,无论是否安装了应用程序都适用。这带来了更好的用户体验。
对于那些想知道其他平台的用户,此功能也支持 Windows 和 Android。Android 还支持使用标准 http:// 和 https:// 链接链接到应用程序(与 iOS 上的通用链接相同)。