iOS Advanced Features
Manually resolve a MovableInk Link
Common use-cases for manually resolving a MovableInk Link are:
- Clicking on a link
- Opening the app from a push notification
- In-App messages
- Scanning a QR Code
If you need to manually resolve a MovableInk Link into a clickthrough URL, theres are a few ways you can accomplish this.
Note
This requires the SDK. If you're looking to resolve a MovableInk Link without the SDK, you can go to the MovableInk Link Resolution - Without SDK section to setup Link Resolution.
MIClient.handleUniversalLink(url:)
You can call this method with the URL you want to resolve. This will call the handler you created in MIClient.start
.
MIClient.resolve(url:)
This is an async method that will return an optional URL if a resolution was successful.
MIClient.resolve(url:completion:)
The same as the above method, but will call the provided completion handler instead.
Resolving a MovableInk Link from a Push Notification
If you use a MovableInk Link in a push notification, you have to extract it from the payload when your UNUserNotificationCenterDelegates
userNotificationCenter(_,didReceive:,withCompletionHandler:)
method is called and tell the SDK to handle the link:
final class PushNotificationManager: NSObject, UNUserNotificationCenterDelegate {
static let shared = PushNotificationManager()
private override init() {
super.init()
}
func start() {
let center = UNUserNotificationCenter.current()
center.delegate = self
let options: UNAuthorizationOptions = [.alert, .sound, .badge, .provisional]
center.requestAuthorization(options: options) { granted, error in
}
UIApplication.shared.registerForRemoteNotifications()
}
// Handle notifications that arrive when a user taps on the notification
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
handleNotification(response.notification)
completionHandler()
}
// Handle notifications that arrive while the app is opened and in the foreground
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
// Here we only want to show the banner in app, and only handle it if the user taps on it
// Tapping on the notification will call userNotificationCenter(_:didReceive:withCompletionHandler:) for us
completionHandler(.banner)
}
private func handleNotification(_ notification: UNNotification) {
let userInfo = notification.request.content.userInfo
guard let aps = userInfo["aps"] as? [String: Any] else {
return
}
// Example Push Notification payload
/**
{
"aps": {
"alert": {
"title": "Hello, Notification!",
"body": "This is a test notification that should open a jacket page."
},
"url": "https://www.movable-ink-7158.com/p/cpm/f9e1dde60c339938/c?url=https%3A%2F%2Fwww.movable-ink-7158.com%2Fp%2Frpm%2F4ff5ef609123c272%2Furl&url_sig=lyfxiiQP3n12CU"
}
}
*/
if let urlString = aps["url"] as? String,
let url = URL(string: urlString) {
MIClient.handleUniversalLink(url: url)
}
}
}
Then in your AppDelegate
, start this manager before application(_,didFinishLaunchingWithOptions:)
is finished.
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(
_ application: UIApplication, didFinishLaunchingWithOptions
launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
PushNotificationManager.shared.start()
MIClient.start(launchOptions: launchOptions) { result in
switch result {
case let .success(url):
guard let url = URL(string: url) else { return }
DeeplinkManager.route(to: url)
case let .failure(.failure(url, message)):
debugPrint(url, message)
case .failure:
debugPrint("Some unknown error occurred while resolving a deeplink")
}
}
return true
}
...
}
Resolving a MovableInk Link from an In App Message
If you're using something like Braze for In App Messaging, you can still resolve MovableInk Links, but you must use your apps schema in place of the https one.
For example, if your MovableInk Link was
https://mi.example.com/p/rpm/f9e1dde60c339938/url
You should replace the text https with your App Scheme (this example is app-scheme):
app-scheme://mi.example.com/p/rpm/f9e1dde60c339938/url
If you're using Scenes, you can then tell the MovableInk SDK to handle it by implementing
scene(_:,openURLContexts:)
in your SceneDelegate
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
for context in URLContexts {
// Ask MovableInk to handle to link if it can
if MIClient.handleUniversalLink(url: context.url) {
break
}
// Ask the manager to route to that path in the app if it can
guard DeeplinkManager.route(to: context.url) else { continue }
break
}
}
application(_,open:options:)
in your AppDelegate:
func application(
_ app: UIApplication,
open url: URL,
options: [UIApplication.OpenURLOptionsKey : Any] = [:]
) -> Bool {
MIClient.handleUniversalLink(url: url)
}
ESP Link Resolution
If your Email Service Providers (ESP) wraps links in your emails with their own domain, you can tell the MovableInk SDK to also handle those links.
If your ESP allows you to setup Universal Links with them, you should set that up so that you can have a better deeplinking experience i.e opening directly into the app rather than opening Safari, then Safari opening the app.
- Add the ESPs domain they use for wrapping links to the
movable_ink_universal_link_domains
in your plist. - Optional - By default, the MovableInk SDK will handle all urls for this domain. If you want to filter which ones the MovableInk SDK should handle, you can use the
MICLient.addHandleablePaths(_:)
method to do this.
MIClient.start { result in
...
}
// For the domain mycustomdomain.com, handle all urls
// that match the regex /product/NUMBERS
//
// Make sure to only add keys that aren't your MovableInk domain
MIClient.addHandlablePaths(
[
"mycustomdomain.com": ["/product/\d+"]
]
)
Registering Deeplink Domains at runtime
If for some reason you can't register domains at build time with the plist, you can use the MIClient.registerDeeplinkDomains(_:)
method to register domains at runtime.
Accessing a clickthrough link after logging in
If you show a login screen on every launch and need to wait to deeplink a user until after they login,
you can read the MIClient.storedDeeplink
variable when you're ready to handle the navigation for the deeplink.
If you're using Combine
, you can also utilize the MIClient.storedDeeplinkSubject
.
let cancelable = MIClient.storedDeeplinkSubject
.compactMap { $0 }
.sink { deeplink in
// Handle Deeplink
}
We recommend using the storedDeeplinkSubject
over reading the storedDeeplink
variable as the subject's sink will be called even if the MovableInk Link is resolved slowly due to a poor network connection. If you read the variable before the SDK has had time to resolve the link, you may miss the deeplink.
You can also handle this manually instead by reading the value in the MIClient.start
handler and sending the url to your controller in another way such as posting a notification.
Setting content onto the clipboard after deeplinking
If you want to copy content to the users clipboard after resolving a deeplink, for example, you deeplink the user to a product to add to their cart with a coupon code. You can add that coupon code onto your URL and it'll automatically be copied onto the users clipboard for easy access.
By default, the SDK will look for the mi_pv
query parameter, but you can configure this in the SDK if you want to use a different parameter name or an array of them if you have multiple use cases.
You can add your value to an MI Link, where CODE
is the value that will be copied the clipboard:
https://mi.example.com/p/rpm/1234567890/url?url_sig=ASDFGHJKL&mi_pv=CODE"
You can also add it to your clickthrough URL as well:
https://example.com/products/12345?mi_pv=CODE
If you need to use a different key, you can tell the SDK about it:
Now the sdk will search for that key. If you have an array of keys, the first one that's found in the URL will be used.
mi_pv
will always take precedence over any custom keys