Access Universal Link app-argument in AppDelegate - ios

I'm working on implementing Universal Links on our website, and I've run into a seemingly simple issue that I can't figure out. Searching Google and Stack Overflow hasn't helped, which leads me to believe I might just be missing something.
Basically, I'm adding the following to my page header (which has a URL of "https://www.website.com/slug").
<meta name="apple-itunes-app" content="app-id=xxx, app-argument=https://www.website.com/1" />
Note that the app-argument has a different URL than the actual page.
I've setup my apple-app-site-association file properly. When I try and handle the Universal Link in our iOS app, I do this:
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: #escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
guard
userActivity.activityType == NSUserActivityTypeBrowsingWeb,
let url = userActivity.webpageURL,
let components = URLComponents(url: url, resolvingAgainstBaseURL: true) else
{
return false
}
// Set breakpoint here.
return false
}
I set the breakpoint at the last return false, and when I inspect components, it contains the actual page URL ("https://www.website.com/slug") and not the URL passed in via app-argument ("https://www.website.com/id").
So my question is: How do I access the app-argument in my AppDelegate?

Related

Universal link not performing action despite url being hit

I've successfully implemented universal links in to my app. The AASA is live on my website, and running the following command in my terminal successfully boots the app as expected:
xcrun simctl openurl booted https://websiteurl.com
Note - the url is different in practice.
For reference, this is my AASA structure:
{
"applinks": {
"apps": [],
"details": [
{
"appID": "identifier.bundle_id",
"paths": ["*"]
}
]
}
}
However, my eventual aim with these universal links is to integrate it into an API which requires a redirect URI, where I send users back to the app.
As a result, I've created a ViewController, imaginatively called CallbackViewController. I will redirect users to this screen when the app is ready.
For now, I've introduced the following code into my AppDelegate file. The aim here is to print 'callback' when a universal link with the phrase 'callback' in is hit. However, when running the initial command in my terminal and adding /callback onto the end, nothing happens.
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: #escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
guard userActivity.activityType == NSUserActivityTypeBrowsingWeb,
let url = userActivity.webpageURL, let components = URLComponents(url: url, resolvingAgainstBaseURL: true) else {
return false
}
if (components.path.contains("callback")) {
print("callback hit...")
}
return true
}
Does anyone know what I'm doing wrong in this function? I'm wondering if it's related to my AASA but unsure.
Thanks!

Firebase dynamic link short url returns nil ios

I am facing an issue with Firebase dynamic link. The link is generated from the backend. and it's working perfectly in android. but in iOS when I shared the dynamic link on whatsapp or messages app on iPhone. and tap on the link app is opened. and our following function is called in Appdelegate. and after that firebase function is called with the received URL. and the firebase functions return nil. But when I open that link from the safari app is opened and that same firebase function returns the deeplink URL. I have searched alot but did not find the solution. I am using firebase 7.11 version pods.
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: #escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
if let incomingUrl = userActivity.webpageURL{
print("Incoming URL is \(incomingUrl)")
let linkHandled = DynamicLinks.dynamicLinks().handleUniversalLink(incomingUrl) { (dynamicLink, error) in
// guard error == nil else{
// print("Found an error! \(error!.localizedDescription)")
// return
// }
print("error \(error)")
print("dynamicLink \(dynamicLink)") // It returns nil when app open from whatsapp or any 3rd party app.
}
if linkHandled{
return true
}else{
return false
}
}
return false
}
I have also received the following error in both cases.
Error Domain=com.firebase.dynamicLinks Code=403 "(null)" UserInfo={code=403, message=Requests to this API firebasedynamiclinks.googleapis.com method google.firebase.dynamiclinks.v1.DynamicLinksService.GetIosReopenAttribution are blocked., status=PERMISSION_DENIED, details=(
{
"#type" = "type.googleapis.com/google.rpc.ErrorInfo";
domain = "googleapis.com";
metadata = {
consumer = "projects/37866531840";
service = "firebasedynamiclinks.googleapis.com";
};
reason = "API_KEY_SERVICE_BLOCKED";
}
)}
Did you add your custom domains in your info.plist file with FirebaseDynamicLinksCustomDomains parameter like image below.
Have you set AppBundle id as deepLinkURLScheme as like below in AppDelegate class under function didFinishLaunchingWithOptions
let kAppBundleID = Bundle.main.bundleIdentifier
FirebaseOptions.defaultOptions()?.deepLinkURLScheme = kAppBundleID
Please try with this.
Reference:
https://firebase.google.com/docs/dynamic-links/ios/create
By default, Dynamic Links uses your app's bundle identifier as the URL scheme needed to open up your application. We recommend staying with this default value to keep your implementation simple.

How to NOT handle some Universal Links programmatically?

My iOS application handles universal links to redirect safari users to my app. So far everything is working great, if a user tap a link to my web site from Google my app is opening instead of my web site like this:
from safari https://my-web-site.com -> my-app
But my app doesn't implement certains features that my web site does, so I would like to programmatically reject some URLs and let my users on safari instead of redirecting him in my app, like this:
from safari https://my-web-site.com -> my-app
OR
from safari https://my-web-site.com?reject -> safari
That should by possible according to Apple documentation:
It’s important to understand that if your app uses openURL: to open a universal link to your website, the link does not open in your app. In this scenario, iOS recognizes that the call originates from your app and therefore should not be handled as a universal link by your app.
Unfortunately that's not what's appending, instead of staying in safari, my user is redirected to my app for a brief moment, then redirected again to safari like this:
from safari https://my-web-site.com?reject -> my-app (briefly) -> safari
here is my code for AppDelegate.swift:
internal func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: #escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
guard let url = userActivity.webpageURL else { return false }
guard let urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: true) else { return false }
let params = urlComponents.queryItems ?? []
if params.contains(where: { $0.name == "reject" }) {
// This line should prevent the app from opening according to Apple's documentation
application.open(url)
return false
} else {
return true
}
}
So anyone have an idea how I could make the rejection of an Universal link the way it's described by Apple's documentation ?
note: the rejection of an universal link need to be done programmatically by the app, according to data stored locally. So adding a path exception in the apple-app-site-association file is not an option in my case :
{
"applinks": {
"apps": [],
"details": [
{
"appID": "XXXXXXXXX.com.my-app",
"paths": [
"*",
"NOT /reject"
]
}
]
}
}
If you prefix an entry in the paths array with "NOT" then you can explicitly prevent a match. So in your example you could do something like
["NOT /settings/*", "NOT /activity/*", "/*"]
which would prevent paths with /settings or /activity prefixes, and then match everything else.
Reference

Get parameters from URL while not launching app for the first time in viewWillAppear

we provide some parameters using URL schema and it works well if we launch app for the first time. I have provided a custom initialization in AppDelegate:
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
if let token = Helper.getQueryStringParameter(url: url.absoluteString, param: AUTH_TOKEN_PARAM) {
tokenFromURL = token
return true
} else {
}
return false
}
However this code does not work if for instance user downloads app, opens it and returns back to the web and presses same button to launch app.
I think I need to check if URL was provided in the viewWillAppear, am I right? How to do that, please help?

iOS: apple universal link if app is not open?

My app can successfully handle apple universal links, if the app is already open (backgrounded). But if the app is not open already, then when I tap such a link in, say, mail, the app opens, but I never get the callback for application:continueUserActivity... (which I do if the app was already open/backgrounded)...
To wit:
If the app is backgrounded, and I click on an apple universal link in, say, the mail app, then this method (which is what apple's documentation says to implement to handle universal links):
optional func application(_ application: UIApplication,
continueUserActivity userActivity: NSUserActivity,
restorationHandler restorationHandler: ([AnyObject]?) -> Void) -> Bool
Gets called. If the app is not running (I force close it), then when I click on the link, that method does NOT get called, but the app DOES open.
Is this supposed to work this way?
Based MCMatan's clue, you have to do something like this in didFinishLaunchingWithOptions, and then continueUserActivity will get called:
if let userActivityDict = launchOptions?[UIApplicationLaunchOptionsUserActivityDictionaryKey] as? NSDictionary,
activityType = userActivityDict[UIApplicationLaunchOptionsUserActivityTypeKey] as? String where activityType == NSUserActivityTypeBrowsingWeb {
return true
}
You are correct. If the app is not in background "continueUserActivity" will not be called.
Instead, application:didFinishLaunchingWithOptions will call with the information:
let activityDic = launchOptions?[UIApplicationLaunchOptionsUserActivityDictionaryKey]
if let isActivityDic = activityDic {
// Continue activity here
}

Resources