一个微型库,使用 Swift 的字符串插值来解析 Deeplink 及其参数。
移动应用的深层链接(也简称为“深层链接”)是指向应用内部内容的 URL。 如果你想从亚马逊分享一双鞋子给朋友,你可以发送一个深层链接,直接将你的朋友带到亚马逊应用中的那双鞋子。 如果没有深层链接,你的朋友必须在 App Store 或 Play Store 上找到亚马逊应用,打开应用到主页,找到搜索功能,然后尝试找到你找到的同一双鞋子。
这个软件包提供了定义特定 Deeplink 模板、将 URL 与 Deeplink 模板进行模式匹配,以及将 URL 中包含的信息解析为对象的工具。 它负责解析,因此您无需自己处理!
该库仅支持 Swift Package Manager。 要安装 Deeplink
以便在应用程序、命令行工具或服务器端应用程序中使用,请将 Deeplink 作为依赖项添加到你的 Package.swift
文件中。 有关更多信息,请参阅 Swift Package Manager 文档。
.package(url: "https://github.com/TizianoCoroneo/Deeplink", .upToNextMinor(from: "0.3.0"))
除了下面的示例,还有 DocC 文档,其中包含一些很棒的教程。 您可以在线浏览文档或在 Xcode 中浏览:只需选择选项 Product > Build Documentation
即可开始(如果您有 Xcode 13 或更高版本)。
你可以通过定义你期望在你的应用程序中接收到的 Deeplink 来开始使用这个库。 您可以通过创建模板来完成此操作。
let mySellDeeplink: Deeplink<Void> = "/sell/ticket"
let myEventDeeplink = "/event/recommended" as Deeplink<Void>
请注意,Deeplink
类型具有一个泛型类型变量 Value
:如果需要从收到的 URL 中提取数据,可以使用字符串插值定义 Deeplink,以指定 URL 的哪些部分应分配给 Value
实例的哪个属性。 这将尝试模式匹配插值的字符串组件(如果字符串不匹配,则会抛出错误),同时将 URL 的额外部分分配给由参数组件的键路径指定的属性。
示例
struct TestData {
var id: String?
var text: String?
}
let myTestDeeplink: Deeplink<TestData> = try! "/test/\(\.id)/\(\.text)"
插值部分需要一个 WritableKeyPath<Value, String?>
,其中 Value
是你的自定义类型。 该属性需要是 String?
类型,并且需要是 var
,以便库将新值写入其中。
此 Deeplink 模板能够匹配如下格式的 URL: https://ticketswap.com/test/123/abc
https://ticketswap.com/test/123/abc?some=else
https://ticketswap.com/test/123/abc#fragment
ticketswap:///test/123/abc#fragment
并且能够提取路径组件 123
和 abc
,并将它们分配给 TestData
实例上的属性 id
和 text
,如下所示
// Define the deeplink
let myTestDeeplink: Deeplink<TestData> = try! "/test/\(\.id)/\(\.text)"
// Example URL
let url = URL(string: "https://ticketswap.com/test/123/abc")!
// Object to write data into
var result = TestData(id: "", text: "")
// Use the deeplink to parse the URL, extracting its data into `result`
try myTestDeeplink.parse(url, into: &result)
// Check result's content
print(result.id) // Will print `123`
print(result.text) // Will print `abc`
在第一个示例中,我们使用了 Deeplink<Void>
来表明 Deeplink 模板中没有参数:如果只需要匹配不包含可变数据的 URL,则可以使用这些类型的 Deeplink。
// Define the deeplink
let myTestDeeplink: Deeplink<TestData> = try! "/test/ticket"
// Example URL
let url = URL(string: "https://ticketswap.com/test/ticket")!
// Use the deeplink to parse the URL. Will throw if the URL relative part doesn't match the deeplink template.
try myTestDeeplink.parse(url)
如果 TestData
中的属性在 Deeplink 模板中未使用,则该属性将保留其初始值,除非您自定义了 TestData
的 init 方法,否则该值为 nil
。 否则,如果模板中存在参数,但 URL 不包含该值,则该属性将采用 ""
值。
移动应用程序的典型用例是通过 AppDelegate
的 application(_: open: options:)
方法接收 URL,尝试识别链接的类型(通常使用 URLComponents
),并逐个匹配,同时提取所需的数据。
此库让您采用更具声明性的方法:只需定义您需要能够匹配的 Deeplink 列表,以及在识别到其中一个链接时要采取的相应操作。 收到 URL 时,将逐个尝试每个 Deeplink 模板,直到找到正确的模板,提取数据并触发相应的操作。
// This is the object that holds the list of deeplinks to try.
let center = DeeplinksCenter()
// Data types where to store parsed values
struct Artist: Equatable {
var id: String?
var slug: String?
}
struct Location: Equatable {
var id: String?
var slug: String?
var period: String?
}
struct Event: Equatable {
var id: String?
var slug: String?
}
// Instances where to put the parsed data
var artist = Artist()
var location = Location()
var event = Event()
// URLs to parse
let artistURL = URL(string: "https://ticketswap.com/artist/metallica/123456")!
let locationURL = URL(string: "https://ticketswap.com/location/amsterdam/1234567/24-06-2019")!
let eventURL = URL(string: "https://ticketswap.com/event/awakenings/123")!
// Deeplink templates
let artistDeeplink = try! "/artist/\(\.slug)/\(\.id)"
as Deeplink<Artist>
let locationDeeplink = try! "/location/\(\.slug)/\(\.id)/\(\.period)"
as Deeplink<Location>
let eventDeeplink = try! "/event/\(\.slug)/\(\.id)"
as Deeplink<Event>
// Registering a deeplink template into the center, using the `artistDeeplink` to parse data into the `artist` var, and run the `ifMatching` closure if the template matches a URL.
center.register(
deeplink: artistDeeplink,
assigningTo: artist,
ifMatching: { url, newArtist in
// The parsed artist info is available here
if let id = newArtist.id {
print(id)
}
// Indicates that we successfully handled the URL.
// Returning `false` makes the center evaluate the rest of the list of registered deeplinks.
return true
})
// Same registration for the location deeplink
.register(
deeplink: locationDeeplink,
assigningTo: location,
ifMatching: { url, newLocation in
// The parsed location info is available here
if let id = newLocation.id {
print(id)
}
return true
})
// Same registration for the event deeplink
.register(
deeplink: eventDeeplink,
assigningTo: event,
ifMatching: { url, newEvent in
// The parsed event info is available here
if let id = newEvent.id {
print(id)
}
return true
})
// This will match the artist deeplink, extract the data and print "123456".
try center.parse(url: artistURL)
// This will match the location deeplink, extract the data and print "1234567".
try center.parse(url: locationURL)
// This will match the event deeplink, extract the data and print "123".
try center.parse(url: eventURL)
您可以利用闪亮的新结果构建器来注册您的 Deeplink 模板
fileprivate struct TestData: DefaultInitializable {
var arg1: String?
var arg2: String?
}
fileprivate struct TestData2 {
var arg1: String?
var arg2: String?
}
let link1 = "/test/1" as Deeplink<Void>
let link2 = try "/test/\(\.arg1)/\(\.arg2)" as Deeplink<TestData>
let link3 = try "/test2/\(\.arg1)/\(\.arg2)" as Deeplink<TestData2>
let center = DeeplinksCenter {
link1 { url in
XCTAssertEqual(url.absoluteString, "https://apple.com/test/1")
expectSimpleLink.fulfill()
return true
}
link2 { url, value in
XCTAssertEqual(url.absoluteString, "https://apple.com/test/a/b")
XCTAssertEqual(value.arg1, "a")
XCTAssertEqual(value.arg2, "b")
expectInitializableDataLink.fulfill()
return true
}
link3(
assigningTo: .init(arg1: "default", arg2: "default")
) { (url, value) -> Bool in
XCTAssertEqual(url.absoluteString, "https://apple.com/test2/a/b")
XCTAssertEqual(value.arg1, "a")
XCTAssertEqual(value.arg2, "b")
expectDataLink.fulfill()
return true
}
}
try center.parse(url: URL(string: "https://apple.com/test/1")!)
try center.parse(url: URL(string: "https://apple.com/test/a/b")!)
try center.parse(url: URL(string: "https://apple.com/test2/a/b")!)