Hello! I just open-sourced a library that I've been developing and using for the last few months: it provides a declarative(ish) way of defining deeplink templates using Swift's String interpolation, so that you don't have to manually parse URLs in order to extract the data you need from them.
Few examples:
Parsing a single deeplink
struct TestData {
let id: String?
let text: String?
}
let myTestDeeplink: Deeplink<TestData> = try! "/test/\(\.id)/\(\.text)"
let url = URL(string: "https://ticketswap.com/test/123/abc")!
// Object to write data into
var result = TestData()
// Use the deeplink to parse the URL, extracting its data into `result`
try myTestDeeplink.parse(url, into: &result)
print(result.id) // Will print `.some(123)`
print(result.text) // Will print `.some(abc)`
Defining a list of deeplink templates to try out in order
// 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)
}
})
.register(
deeplink: locationDeeplink,
assigningTo: location,
ifMatching: { url, newLocation in
if let id = newLocation.id {
print(id)
}
})
.register(
deeplink: eventDeeplink,
assigningTo: event,
ifMatching: { url, newEvent in
if let id = newEvent.id {
print(id)
}
})
try center.parse(url: artistURL) // prints "123456"
try center.parse(url: locationURL) // prints "1234567"
try center.parse(url: eventURL) // prints "123"
Defining a list of templates using a result builder
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")!)
The library is fairly well-tested, but not very flexible (no regex based parsing, no scheme taken into account). I'm looking for ways to improve on it; all feedback is appreciated!
I'm also looking for some well-deserved bike shedding: the library is currently named Deeplink
, and this obviously cannot continue, so please help me with a new name for it.