I working on Universal links implementation for our iOS app.
Here is my small piece of AppDelegate:
private func application(_ application: UIApplication, openURL url: URL, sourceApplication: String?, annotation: AnyObject) -> Bool {
DeepLinkHelpers.handleUniversalLink(url.absoluteString)
return true
}
private func application(application: UIApplication, continueUserActivity userActivity: NSUserActivity, restorationHandler: ([AnyObject]?) -> Void) -> Bool {
DeepLinkHelpers.handleUniversalLink(userActivity.webpageURL?.absoluteString)
return true
}
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
signalRConnector.launch()
NotificationCenter.default.addObserver(self, selector: #selector(AppDelegate.processRestartSignalRNotification(_:)), name: NSNotification.Name(rawValue: "restartSignalR"), object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(AppDelegate.reachabilityChanged(_:)),
name: ReachabilityChangedNotification,
object: reachability)
do {
try reachability.startNotifier()
} catch {
Logger.save(text: "Unable to start notifier")
}
return true
}
I have processed all other steps for universal links integration:
Published apple-app-site-association file in our Web application
Switched on Associated domains feature on developers.apple.com
Specified associated domains in xcode
Checked Target Membership for entitlements file
Said xcode to wait until app will be started manually (instead of
autostart)
I doing following to debug:
Connecting ipad
Starting project in xcode
In ipad opening Calendar and click on link contained in some event.
Link has following format: app.domain.com/#/123456789
Ipad opens app but continueUserActivity not calling and i can't
handle code from the url for navigate to exact state within an app.
According documentation continueUserActivity should be executed. It is not executing both when app is running in background and when app not running.
Thank you in advance! Any help appreciated.
As of iOS 12, Swift 4.2, and Xcode 10 Apple has again changed method signature (type of restorationHandler) to
func application(_ application: UIApplication, continue userActivity:
NSUserActivity, restorationHandler: #escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
I suspect this is because you've marked the continueUserActivity function as private. I've never seen that, and in fact Swift 3 actually gives an error when I try. There don't appear to be any examples of code structured that way on all of GitHub.
I'd suggest removing the private scope.
after ios 13 you should use ScenDelegate:
//when app is running
func scene(_ scene: UIScene, continue userActivity: NSUserActivity)
//when app is not running
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions)
In 2019, this is what helped me (from this guide: https://medium.com/#abhimuralidharan/universal-links-in-ios-79c4ee038272 ):
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: #escaping ([Any]?) -> Void) -> Bool {
print("Continue User Activity called: ")
if userActivity.activityType == NSUserActivityTypeBrowsingWeb {
let url = userActivity.webpageURL!
print(url.absoluteString)
//handle url and open whatever page you want to open.
}
return true
}
Related
Minimum iOS version is 13, Scene delegate file is completely deleted, only using appdelegate. But open url, continue userActivity and openURLContexts methods are not calling in when app opened using deep link.
func application(_ application: UIApplication, open url: URL, sourceApplication: String?, annotation: Any) -> Bool {
print("app opened using deep link from \(sourceApplication)")
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "DeepLink"), object: nil, userInfo: nil)
return true
}
func application(_ application: UIApplication, continue userActivity:
NSUserActivity, restorationHandler: #escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
print("app opened using deep link ")
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "DeepLink"), object: nil, userInfo: nil)
return true
}
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
if let url = URLContexts.first?.url{
print(url)
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "DeepLink"), object: nil, userInfo: nil)
}
}
Edit - 1
Invalid redeclaration of 'application(_:open:options:)'
Here is a Sample App I created to test deep links in minimum iOS 13 without SceneDelegate
This is because you might be using wrong open URL method to receive deep links:
The correct one is:
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
print("Deep link received \(url)")
return true
}
continue userActivity is generally called in the case of universal links
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: #escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
}
So, AppDelegate looks like:
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
return true
}
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
print("Deep link received \(url)")
return true
}
}
And in Info.plist make sure to add URL Schemes.
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLName</key>
<string></string>
<key>CFBundleURLSchemes</key>
<array>
<string>deepLinks</string>
</array>
</dict>
</array>
It looks something like this:
Then to test your deep links, go to your browser and write your uri scheme with ://
Example in this case:
deepLinks://mydeeplinkurl
It will ask you to open your app.
And you will get a call back in open URL method
I using Universal linking in my application.
When the user pick on link directly move into root view controller.
But I want to change the rootviewcontroller when user entering through Universal link.
please help to those delegates methods.
There is 2 delegate method that will fire when application is open from link or user tap link as per below.
Swift
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
// handler for URL Scheme "This will called for URL schema"
return true
}
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: #escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
// handler for Universal Links "This will called for Universal Link"
return true
}
Use this delegate method:
func application(_ application: UIApplication,
continue userActivity: NSUserActivity,
restorationHandler: #escaping ([Any]?) -> Void) -> Bool
I have successfully integrated Firebase dynamic links and when I click on dynamic link then my app is opening.
The issues I'm facing is after opening app from dynamic links, continue userActivity: method should be called, but nothing happens.
I've checked the all the possible thing but didn't recognised the issue.
I've searched the SO for this but none of the answer helped me.
My Code:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
GIDSignIn.sharedInstance().clientID = kGoogleSignInClientId
FBSDKApplicationDelegate.sharedInstance().application(application, didFinishLaunchingWithOptions: launchOptions)
// DynamicLinks.performDiagnostics(completion: nil)
FirebaseApp.configure()
return true
}
func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
if url.absoluteString.contains(kFBAppId) {
return FBSDKApplicationDelegate.sharedInstance().application(app, open: url, options: options)
}
if let dynamicLink = DynamicLinks.dynamicLinks().dynamicLink(fromCustomSchemeURL: url) {
print(dynamicLink.url ?? URL(string: "test") as Any)
return true
}
return GIDSignIn.sharedInstance().handle(url, sourceApplication: options[.sourceApplication] as? String, annotation: options[.annotation])
}
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: #escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
//This method is not getting called
}
Additional answer for those using separate SceneDelegate.swift apart from AppDelegate.swift:
This method in AppDelegate >
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: #escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
if userActivity.activityType == CSSearchableItemActionType {
}
is now in SceneDelegate >
func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
}
I don't know whether they keep their doc. up to date or not.
I have just copy-pasted the code from the Google's official Firebase dynamic link document.
Why was the continue userActivity: method is not called?
The reason is (See the difference in following method)
Copy pasted from google doc. - Wrong one
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: #escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
}
I wrote this (without copy paste from google doc.) - Correct one
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: #escaping ([Any]?) -> Void) -> Bool {
}
I've made bold the difference.
This is what I was trying for many hours. It is really very frustrating for me to blindly trust on google doc.:-|
Hope this may help other.
You need to check two times.
When app. is running in the background and is opening from Link:
Delegate method is:func checkForTransferedTicket(_ userActivity: NSUserActivity) { }
When app. is NOT running in background and is opening from Link:
func scene(_ scene: UIScene, willConnectTo session: UISceneSession,options connectionOptions: UIScene.ConnectionOptions) { if let userActivity = connectionOptions.userActivities.first {
print(userActivity)
}
}
I am trying to setup Firebase Dynamic Links in my iOS project using Swift 2.3.
When I add this function in the AppDelegate (as reported at the bottom of this page), I get the error:
Unknown attribute 'escaping'
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: #escaping ([Any]?) -> Void) -> Bool {
guard let dynamicLinks = FIRDynamicLinks.dynamicLinks() else {
return false
}
let handled = dynamicLinks.handleUniversalLink(userActivity.webpageURL!) {
(dynamiclink, error) in
// ...
}
return handled
}
any idea which is the correct syntax for Swift 2.3?
The method which you are using is for swift3, here is the method for swift2.3
func application(application: UIApplication, continueUserActivity userActivity: NSUserActivity, restorationHandler: ([AnyObject]?) -> Void) -> Bool {
}
if you want to add restriction on deep link then implement bellow method like this
this delegate method call first.
func application(application: UIApplication, willContinueUserActivityWithType userActivityType: String) -> Bool {
return userActivityType == NSUserActivityTypeBrowsingWeb ? true : false
}
if you want to link which user have click or if you have multiple deep link in single app then want to identify then you can get like this.
func application(application: UIApplication, continueUserActivity userActivity: NSUserActivity, restorationHandler: ([AnyObject]?) -> Void) -> Bool{
// pass the url to the handle deep link call
print(userActivity.webpageURL)
//NSURLComponents
return true
}
I am trying to use core spotlight to open a view controller from the spotlight search results.
- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void(^)(NSArray *restorableObjects))restorationHandler
{
if(self.window.rootViewController){
[self.window.rootViewController restoreUserActivityState:userActivity];
}
return YES;
}
This seems to work when the app is already running in background, however when it is closed and I tap on the spotlight search result it seems that this method gets not called and the behavior I get is that my application simply starts in the main interface.
Do you have any suggestion for making it work also when my app is closed?
Is there a way to debug what is happening (since I need to run the app to get the debugger attached I don't know how to simulate the app opening from the search result)?.
Niko,
first of all: there's a way to start your app from Xcode and not opening it immediately: open your scheme properties, go to the "run" section, and under "info", there's a switch that will help you to debug what's happening:
"Wait for executable to be launched".
If you activate this switch, you can launch the app from Xcode, Xcode will wait until the app is opened from search and then it will attach the debugger to it.
Hope that helps!
Ivan
In the new Swift 5 there is a new new file called SceneDelegate.swift.
Use the method scene(_ scene: UIScene, continue userActivity: NSUserActivity)
If you are using the Facebook SDK, and in didfinishlaunching your are returning FBSDK, instead of plain text, and returning true at the end, it can cause problems hitting continueuseractivity.
After searching a lot, and trying different ways, I just had to
return true and comment this:
FBSDKApplicationDelegate.sharedInstance().application(application, didFinishLaunchingWithOptions: launchOptions)
didFinishLaunchingWithOptions needs to return YES so continueUserActivity will be called.
Add to End of application: didFinishLaunchingWithOptions:
NSDictionary *activityDictionary = launchOptions[UIApplicationLaunchOptionsUserActivityDictionaryKey];
if (activityDictionary) {
NSUserActivity *userActivity = activityDictionary[UIApplicationLaunchOptionsUserActivityTypeKey];
if (userActivity) {
return YES;
}
}
return NO;
continue userActivity is changed in the latest swift version. Changing the func worked for me.
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: #escaping ([Any]?) -> Void) -> Bool
to
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: #escaping ([UIUserActivityRestoring]?) -> Void) -> Bool.
If the app was closed, application: continueUserActivity: is not called. Instead, you get all the information launchOptions dictionnary in application: didFinishLaunchingWithOptions:
// In application: didFinishLaunchingWithOptions:
NSDictionary *activityDic = [launchOptions objectForKey:UIApplicationLaunchOptionsUserActivityDictionaryKey];
if (activityDic) {
// Continue activity here
}
If your app uses Scenes, make sure you have implemented both delegates:
this one for the case when the app is not running at the moment of clicking the link:
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
// the url is expected to be in connectionOptions.userActivities.first?.webpageURL in case if its type is NSUserActivityTypeBrowsingWeb
}
AND this one for the case when the app is in background or in foreground:
func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
// the url is going to be in userActivity.webpageURL
}
If your app does NOT use Scenes:
Double-check this method call carefully, because it has changed some time ago and legacy projects can still have old code:
func application(_ application: UIApplication,
continue userActivity: NSUserActivity,
restorationHandler: #escaping ([Any]?) -> Void) -> Bool
This is the call which worked in my case:
func application(_ application: UIApplication,
continue userActivity: NSUserActivity,
restorationHandler: #escaping ([UIUserActivityRestoring]?) -> Void) -> Bool
(Don't be deceived by Xcode's warning suggesting to add private - it won't help)
Check Apple's documentation about Universal Links for more details.
Here it follows the complete answer following your advices.
// In application: didFinishLaunchingWithOptions:
NSDictionary *activityDic = [launchOptions objectForKey:UIApplicationLaunchOptionsUserActivityDictionaryKey];
if (activityDic) {
if(self.window.rootViewController){
NSUserActivity * userActivity = [activityDic valueForKey:#"UIApplicationLaunchOptionsUserActivityKey"];
[self.window.rootViewController restoreUserActivityState:userActivity];
}
}