-
Notifications
You must be signed in to change notification settings - Fork 5
Deeplinking
MERLin' provides an easy way to make a page deeplinkable. In fact to make that happen it is as easy as make the module conforming to the Deeplinkable
protocol, or to the DeeplinkContextUpdatable
protocol.
There is no more work involved in deeplinks if you are using the default implementation of the Router and the ModuleManager. MERLin' will do its magic when the router is asked to route to a deeplink.
Deeplinkable
: Means that your module can be deeplinked but its content will never be updated. This means that any new deeplink that should open the same module will create a new instance of it instead of updating the current one.
DeeplinkContextUpdatable
: Means that your module can be deeplinked, and if a new deeplink occurs that should generate an instance of the same module type currently on screen, this can be recycled to accommodate the new deeplink instead of pushing on screen a new instance.
To conform Deeplinkable
all it's requested is to provide
- the schemas the module can respond to (ex.:
["myapp", "my_app", ...]
- the regexes that the module will use to parse the deeplink
- the class of the ViewController that corresponds to the deeplink
- a function that will build the module from the deeplink
- a function that given a module instance, can return a deeplink url that represents the module in its current state
A DeeplinkContextUpdatable
needs to conform Deeplinkable
plus it requires the updateContext(fromDeeplink:)
function to be defined.
This function will have the duty to decide if the context can be updated, and how to update the context to reflect the changes in the module required by the deeplink.
Example: A ProductDetailModule
will have to update the product id variable of the context to show the details of the new product.
If the return of this update is false, the default implementation of the route(toDeeplink:)
function of the router will cause the creation of a new module through the module(fromDeeplink:)
function.
An example of DeeplinkContextUpdatable
conformance:
ProductDetailPageModule+Deeplinkable.swift
extension ProductDetailPageModule: DeeplinkContextUpdatable {
public static var deeplinkSchemaNames: [String] = ["thebay"]
public static func deeplinkRegexes() -> [NSRegularExpression]? {
guard deeplinkSchemaNames.count > 0 else { return nil }
var regexString = "\\b("
regexString += deeplinkSchemaNames.joined(separator: "|") + ")\\:\\/\\/"
regexString += "pdp\\/([0-9]+)"
let regex = try! NSRegularExpression(pattern: regexString, options: .caseInsensitive)
return [regex]
}
public static func classForDeeplinkingViewController() -> UIViewController.Type {
return PDPViewController.self
}
public func deeplinkURL() -> URL? {
guard let schema = ProductDetailPageModule.deeplinkSchemaNames.first else { return nil }
return URL(string: "\(schema)://pdp/\(pdpContext.productId)")!
}
//An helper function to build a context from a depplink
private static func context(fromDeeplink deeplink: String) -> PDPContext? {
guard let match = deeplinkRegexes()?
.compactMap({
$0.firstMatch(in: deeplink, range: NSRange(location: 0, length: deeplink.count))
}).first,
let idRange = Range(match.range(at: match.numberOfRanges-1), in: deeplink) else { return nil }
let id = String(deeplink[idRange])
return PDPContext(routingContext: Module.deeplinkRoutingContext, productId: id)
}
static public func module(fromDeeplink deeplink: String) -> (Module, UIViewController)? {
guard let context = ProductDetailPageModule.context(fromDeeplink: deeplink) else { return nil }
let module = ProductDetailPageModule(usingContext: context)
return (module, module.buildRootViewController())
}
@discardableResult public func updateContext(fromDeeplink deeplink: String) -> Bool {
guard let context = ProductDetailPageModule.context(fromDeeplink: deeplink) else { return false }
updateBuildContext(context)
return true
}
}
//Adding deeplink knowledge for any product
public extension ProductProtocol {
public var deeplinkURL: URL? {
guard let schema = ProductDetailPageModule.deeplinkSchemaNames.first else { return nil }
return URL(string: "\(schema)://pdp/\(remoteId)")!
}
}
With the default router implementation MERLin' offers also the possibility to concatenate multiple routes in the same deeplink.
Example: myapp://productlist/123/productdetail/1234
will cause the product list module to appear modally in navigationController represeniting the list with id 123
and then will push the product Detail module with id 1234
. There are no dependencies between deeplinked modules therefore it is possible virtually to use any combination of deeplink.