从 URL 打开应用程序是一项非常强大的 iOS 功能。它将用户吸引到您的应用程序,并可以创建特定功能的快捷方式。本周,我们将深入探讨 iOS 上的深度链接以及如何为您的应用创建 URL 方案。
当我们谈论移动应用程序的深度链接时,它意味着创建一个特定的 URL 来打开移动应用程序。它分为两种格式:
- 您的应用注册的自定义 URL 方案:
scheme://videos
- 从注册域打开您的应用程序的通用链接:
mydomain.com/videos
今天,我们将专注于前者。
我将主要关注 UIKit 实现的代码,但如果您也在寻找它,我还将简要介绍 SwiftUI。
设置 URL 方案
无论您使用的是 SwiftUI 还是 UIKit,为 iOS 设置自定义 URL 方案都是相同的。在 Xcode 中,在您的项目配置下,选择您的目标并导航到Info选项卡。您会URL Types
在底部看到一个部分。
单击+
,我可以创建一个新类型。对于标识符,我经常重复使用 app bundle。对于 URL 方案,我建议使用应用程序名称(或缩短)尽可能短。它不应包含任何自定义字符。例如,我将使用deeplink
.
而已。该应用程序已准备好识别新 URL,现在我们需要在收到新 URL 时对其进行处理。
SwiftUI 深度链接。
如果你没有任何AppDelegate
和SceneDelegate
文件,这是 SwiftUI 实现的大多数情况,我们没有太多工作要做。
在 App 实现中,我们可以捕获从onOpenURL(perform:)
操作中打开的 url。
import SwiftUI
@main
struct DeeplinkSampleApp: App {
var body: some Scene {
WindowGroup {
ContentView()
.onOpenURL { url in
print(url.absoluteString)
}
}
}
}
为了测试它,我可以在模拟器上安装应用程序并从终端应用程序启动给定的 url
xcrun simctl openurl booted "deeplink://test"
很酷!让我们看看 UIKit 的实现有何不同。
UIKit 深层链接
在纸面上,UIKit 或 SwiftUI 不应该对我们处理深度链接的方式产生影响。然而,它主要归结为对于 UIKit 应用程序更常见的一个AppDelegate
或一个。SceneDelegate
对于只有 的旧应用AppDelegate
,该应用通过以下方法捕获深度链接打开。
extension AppDelegate {
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any]) -> Bool {
print(url.absolueString)
return true
}
}
如果应用程序可以处理给定的 url,则该函数返回一个布尔值。
对于包含 的较新应用程序,SceneDelegate
回调将在那里。重要的是要注意AppDelegate
,即使您实现它,也不会调用它。
extension SceneDelegate {
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
guard let firstUrl = URLContexts.first?.url else {
return
}
print(firstUrl.absoluteString)
}
}
在这个实现中,我们可以注意到我们不再需要返回任何结果。但是,现在传递的参数是 aSet<>
而不仅仅是 a URL
,它是打开一个或多个 URL。我没有一个用例,我们会拥有更多的 URL,所以我暂时只保留一个。
与之前一样,我们可以在模拟器上安装应用程序并尝试查看是否所有设置正确。我们应该看到打印我们的深层链接 URL。
xcrun simctl openurl booted "deeplink://test"
设置完成后,我们的想法是创建路线以识别和打开正确的屏幕。让我们潜入水中。
深度链接处理程序实现
这个想法很简单,对于给定的链接,我们需要确定我们应该打开什么用户旅程或屏幕。因为它们可以是整个应用程序的许多功能,并且因为我们希望避免大量switch case
处理它,所以我们将变得更聪明并且分而治之。
对于这个例子,让我们想象一下我们有一个视频编辑应用程序。它们是 3 个主要选项卡,用于编辑新视频、列出已编辑的视频,然后是包含不同应用程序和用户信息的帐户页面。
我们可以想到三个主要路径
-
deeplink://videos/new
- 开始新的视频编辑之旅 -
deeplink://videos
- 登陆视频列表选项卡屏幕 -
deeplink://account
- 登陆帐户屏幕
首先,我将创建一个深度链接处理程序协议来定义任何新处理程序的最低要求。
protocol DeeplinkHandlerProtocol {
func canOpenURL(_ url: URL) -> Bool
func openURL(_ url: URL)
}
我还将定义一个DeeplinkCoordinator
将保留在处理程序上并找到正确使用的处理程序。它还像AppDelegate
has 一样返回一个布尔值,因此我们可以在不同的实现中使用。
protocol DeeplinkCoordinatorProtocol {
@discardableResult
func handleURL(_ url: URL) -> Bool
}
final class DeeplinkCoordinator {
let handlers: [DeeplinkHandlerProtocol]
init(handlers: [DeeplinkHandlerProtocol]) {
self.handlers = handlers
}
}
extension DeeplinkCoordinator: DeeplinkCoordinatorProtocol {
@discardableResult
func handleURL(_ url: URL) -> Bool{
guard let handler = handlers.first(where: { $0.canOpenURL(url) }) else {
return false
}
handler.openURL(url)
return true
}
}
现在我们可以定义单独的处理程序,每个不同的路径一个。让我们首先从最简单的帐户旅程开始。
final class AccountDeeplinkHandler: DeeplinkHandlerProtocol {
private weak var rootViewController: UIViewController?
init(rootViewController: UIViewController?) {
self.rootViewController = rootViewController
}
// MARK: - DeeplinkHandlerProtocol
func canOpenURL(_ url: URL) -> Bool {
return url.absoluteString == "deeplink://account"
}
func openURL(_ url: URL) {
guard canOpenURL(url) else {
return
}
// mock the navigation
let viewController = UIViewController()
viewController.title = "Account"
viewController.view.backgroundColor = .yellow
rootViewController?.present(viewController, animated: true)
}
}
为了简单起见,我只测试匹配的 url 并导航到正确的屏幕。我还设置了背景颜色,看看我的着陆点是什么。在您的情况下,我们可以只设置正确的UIViewController
而不是空的。
我会为不同的视频旅程做同样的事情。
final class VideoDeeplinkHandler: DeeplinkHandlerProtocol {
private weak var rootViewController: UIViewController?
init(rootViewController: UIViewController?) {
self.rootViewController = rootViewController
}
// MARK: - DeeplinkHandlerProtocol
func canOpenURL(_ url: URL) -> Bool {
return url.absoluteString.hasPrefix("deeplink://videos")
}
func openURL(_ url: URL) {
guard canOpenURL(url) else {
return
}
// mock the navigation
let viewController = UIViewController()
switch url.path {
case "/new":
viewController.title = "Video Editing"
viewController.view.backgroundColor = .orange
default:
viewController.title = "Video Listing"
viewController.view.backgroundColor = .cyan
}
rootViewController?.present(viewController, animated: true)
}
}
现在我们可以将它们注入到DeeplinkCoordinator
并让它处理正确的路由。我们将有两个变体,第一个用于AppDelegate
.
class AppDelegate: UIResponder, UIApplicationDelegate {
lazy var deeplinkCoordinator: DeeplinkCoordinatorProtocol = {
return DeeplinkCoordinator(handlers: [
AccountDeeplinkHandler(rootViewController: self.rootViewController),
VideoDeeplinkHandler(rootViewController: self.rootViewController)
])
}
var rootViewController: UIViewController? {
return window?.rootViewController
}
// ...
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any]) -> Bool {
return deeplinkCoordinator.handleURL(url)
}
}
第二个为SceneDelegate
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
lazy var deeplinkCoordinator: DeeplinkCoordinatorProtocol = {
return DeeplinkCoordinator(handlers: [
AccountDeeplinkHandler(rootViewController: self.rootViewController),
VideoDeeplinkHandler(rootViewController: self.rootViewController)
])
}()
var rootViewController: UIViewController? {
return window?.rootViewController
}
// ...
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
guard let firstUrl = URLContexts.first?.url else {
return
}
deeplinkCoordinator.handleURL(firstUrl)
}
我们可以像目前一样再次测试它,希望能落在正确的屏幕上(期待橙色背景)。
xcrun simctl openurl booted "deeplink://videos/new"
深度链接-ios
总而言之,一旦设置了 URL 方案,我们就定义了一个漏斗来捕获用于打开应用程序的所有深层链接,并利用面向协议的编程来创建处理程序的多个实现,每个特定路径一个。
此实现可针对较新的路径进行扩展,并且可以轻松进行单元测试以确保每个部分都按预期运行。
话虽如此,为了更安全的行为,可能会有一些改进,比如验证完整路径而不是相对路径。仅导航present
,但它专注于处理程序而不是转换本身。
在安全说明中,如果您还在深度链接中传递参数,请确保验证预期的类型和值。如果我们不小心,它可能会暴露不同的注入漏洞。
从那里,您应该很好地了解如何使用和处理深度链接来打开您的应用程序并跳转到特定屏幕。此代码可在Github上找到。
这里也推荐一些面试相关的内容,祝各位网友都能拿到满意offer!
GCD面试要点
block面试要点
Runtime面试要点
RunLoop面试要点
内存管理面试要点
MVC、MVVM面试要点
网络性能优化面试要点
网络编程面试要点
KVC&KVO面试要点
数据存储面试要点
混编技术面试要点
设计模式面试要点
UI面试要点
网友评论