27

I have implemented a URL Scheme and use it to pass data to my app by calling method. The entire code is shown as below

- (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url{
    // Check the calling application Bundle ID
    if ([[url scheme] isEqualToString:@"yuvitime"])
    {
        NSLog(@"URL scheme:%@", [url scheme]);
        NSString * yuvitimeRequestValue = [url query];
        NSDictionary * userInfor = [[NSDictionary alloc]initWithObjectsAndKeys:yuvitimeRequestValue, @"YuvitimeRequest", nil];
        NSNotificationCenter * notificationCentre = [NSNotificationCenter defaultCenter];
        [notificationCentre postNotificationName:@"URLSCHEMEACTIVATEDNOTIFICATION" object:self userInfo:userInfor];

        return YES;
    }
    else
        return NO;
}

If my app is in the background, everything works fine. When you click a URL, the app is brought back to Foreground and the URL is handled as coded in the above function.

However, if the app is terminated (app not launched yet), by clicking the URL, it only launches the app without calling the handling function that is shown above.

After searching through, the best result i manage to get is this

application:WillFinishLaunchingWithOptions: When asked to open a URL, the return result from this method is combined with the return result from the application:didFinishLaunchingWithOptions: method to determine if a URL should be handled. If either method returns NO, the system does not call the application:openURL:options: method. If you do not implement one of the methods, only the return value of the implemented method is considered.

- application:didFinishLaunchingWithOptions: This method represents your last chance to process any keys in the launchOptions dictionary. If you did not evaluate the keys in your application:willFinishLaunchingWithOptions: method, you should look at them in this method and provide an appropriate response. Objects that are not the app delegate can access the same launchOptions dictionary values by observing the notification named UIApplicationDidFinishLaunchingNotification and accessing the notification’s userInfo dictionary. That notification is sent shortly after this method returns. The return result from this method is combined with the return result from the application:willFinishLaunchingWithOptions: method to determine if a URL should be handled. If either method returns NO, the URL is not handled. If you do not implement one of the methods, only the return value of the implemented method is considered.

Despite the explanation, i still do not know how to do it and i couldn't find anything else concrete online.

Thanks

Regards

Mina
  • 63
  • 1
  • 14
progammingBeignner
  • 936
  • 1
  • 8
  • 19
  • try - (BOOL)application:openURL:sourceApplication: annotation: – larva Oct 09 '15 at 09:40
  • I tried both, i think the problem lies with configuration in the LaunchOptions like how the documentation suggested "This is the last chance you process any keys".. But i couldn't figure out how. – progammingBeignner Oct 09 '15 at 09:42
  • are you sure - (BOOL)application:handleOpenURL: is not called? problem seem not any object listen to notification when app init. you can show an alert here to confirm again – larva Oct 09 '15 at 09:56
  • I did want to try that, but how do you call an alertViewController in appDelegate? Sorry for that. – progammingBeignner Oct 09 '15 at 10:01
  • @ABáo if we assume the problem lies with project not listening to notification. How do i check and resolve it? Thank you! – progammingBeignner Oct 09 '15 at 10:06
  • - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation { [[[UIAlertView alloc] initWithTitle:@"did handleOpenURL" message:@"" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil, nil] show]; return YES; } – larva Oct 09 '15 at 10:25
  • in above code, you need to call UIAlertView not alertViewController. Try it and confirm again – larva Oct 09 '15 at 10:26
  • @ABáo, you are right!!!! the alert was called! ahaha. OMG thank you!.. Btw, do you have any suggestion in resolving the object not listening to the notification then? I guess i post the notification too early? Way before my viewController could receive it (set up), so when appDelegate posts the notification, my ViewController doesn't receive it at all. – progammingBeignner Oct 09 '15 at 10:32
  • I am facing this issue.Any help? – Naren Jul 31 '18 at 18:37

9 Answers9

29

I agree with Kaloyan, "handleOpenURL" is never called at application launch. So you have to check for URL in "launchOptions" in didFinishLaunchingWithOptions.

HOWEVER

I adopted the same solution as Apple example code for QuickActions (3D Touch). I keep the URL at launch in a variable, and I handle it in applicationDidBecomeActive:.

@interface MyAppDelegate ()
@property (nonatomic, strong) NSURL *launchedURL;
@end

@implementation MyAppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.launchedURL = [launchOptions objectForKey:UIApplicationLaunchOptionsURLKey];
    ...
}

- (void)applicationDidBecomeActive:(UIApplication *)application
{
    if (self.launchedURL) {
        [self openLink:self.launchedURL];
        self.launchedURL = nil;
    }
}

- (BOOL)  application:(UIApplication *)application
          openURL:(NSURL *)url
sourceApplication:(NSString *)sourceApplication
       annotation:(id)annotation
{
    NSURL *openUrl = url;

    if (!openUrl)
    {
        return NO;
    }
    return [self openLink:openUrl];
}

- (BOOL)openLink:(NSURL *)urlLink
{
    ...
}

@end
kokoko
  • 2,705
  • 3
  • 17
  • 28
vmeyer
  • 1,998
  • 21
  • 23
16

I landed up with the same issue for an app on iOS 13. Even with the proper implementation of - (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey, id> *)options method, it never got invoked.

From iOS13, the SceneDelegates get invoked instead of the AppDelegate method. Once I implemented the

- (void)scene:(UIScene *)scene openURLContexts:(NSSet<UIOpenURLContext *> *)URLContexts

on the scene delegate, it would work if the app was already in memory. However, for a cold start, I had to implement the call back

-(void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session options:(UISceneConnectionOptions *)connectionOptions

as well.
While implementing

-(void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session options:(UISceneConnectionOptions *)connectionOptions

please remember to handle cases when the app is not launched from an URL.

Here is an useful reference: https://forums.developer.apple.com/thread/124132

Jyotirmoy
  • 710
  • 6
  • 12
9

I believe there is a better answer now as,

  • application:handleOpenURL:
  • application:openURL:sourceApplication:annotation: Both are deprecated in ios 9. Apple suggestion is:

Use application:openURL:options: instead.

application:openURL:options: has different behaviour than the old ones, as it will be executed in case the app was in background or will launch.

So, you need to handle the URL opening within it only. like below:

- (BOOL)application:(UIApplication *)app
        openURL:(NSURL *)url
        options:(NSDictionary<NSString *,id> *)options {

    // Check the calling application Bundle ID
    if ([[url scheme] isEqualToString:@"yuvitime"])
    {
        NSLog(@"URL scheme:%@", [url scheme]);
        NSString * yuvitimeRequestValue = [url query];
        NSDictionary * userInfor = [[NSDictionary alloc]initWithObjectsAndKeys:yuvitimeRequestValue, @"YuvitimeRequest", nil];
        NSNotificationCenter * notificationCentre = [NSNotificationCenter defaultCenter];
        [notificationCentre postNotificationName:@"URLSCHEMEACTIVATEDNOTIFICATION" object:self userInfo:userInfor];

        return YES;
    }
    else
        return NO;
}
Ismail
  • 2,778
  • 2
  • 24
  • 39
  • 8
    this method is not being called if app is killed and app is being opened by url schemes.I am trying it on iOS 9.1 on IOS simulator – Desert Rose Jan 19 '17 at 07:03
8

Hi when the app is not launched before, the method "handleOpenURL" is never called. You have to check "launchOptions" in didFinishLaunchingWithOptions for object with key "UIApplicationLaunchOptionsURLKey"

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
NSURL *url = [launchOptions objectForKey:UIApplicationLaunchOptionsURLKey];
//call function to handle the url like in handleURL, but do not call handleURL directly

}
Kaloyan Dobrev
  • 135
  • 1
  • 3
6

For iOS 10, use

func application(_ app: UIApplication,
open url: URL,
options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool
ycwjjjj
  • 226
  • 4
  • 4
5

I had the same problem when application(_:open:options:) wasn't called when the app was in background. The reason for this was the Firebase SDK which uses method swizzling.

I solved this problem by setting FirebaseAppDelegateProxyEnabled = NO in Info.plist

For more detailed information on how it works and what it affects you can read here https://firebase.google.com/docs/cloud-messaging/ios/client#method_swizzling_in

Nikaaner
  • 1,022
  • 16
  • 19
0

For me, do not make application(_:open options:) outside AppDelegate {} scope

hstdt
  • 5,652
  • 2
  • 34
  • 34
0

I post the notification too early,I solved my problem by adding delays.

`- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options{

  NSString *urlStr = url.absoluteString;
  BOOL ret = [urlStr hasPrefix:@"dxylink://joinCourse"];
  if(ret){
    NSLog(@"url query:%@",[url query]);
    NSArray *paramsArray = [[url query] componentsSeparatedByString:@"&"];
    NSMutableDictionary *params = [[NSMutableDictionary alloc] init];
    for(NSString *param in paramsArray){
      NSArray *curParams = [param componentsSeparatedByString:@"="];
      [params setValue:[curParams lastObject] forKey:[curParams firstObject]];
    }
    NSArray *arr = [NSArray arrayWithArray:params.allValues];
    self.codeStr = arr.firstObject;
    [self performSelector:@selector(postNoti) withObject:nil afterDelay:1];
    NSLog(@"url scheme params:%@",params);
  }
  return YES;
}

- (void)postNoti {
  [[NSNotificationCenter defaultCenter] postNotificationName:@"LinkNoti" object:nil userInfo:@{@"str":self.codeStr}];
}`
darcyjee
  • 1
  • 1
-1

Swift 2.x

func application(app: UIApplication, openURL url: NSURL, options: [String : AnyObject]) -> Bool {

        if (url.scheme == "yuvitime") {
            print("URL scheme:\(url.scheme)")
            let yuvitimeRequestValue = url.query!
            let userInfor = [
                "YuvitimeRequest" : yuvitimeRequestValue
            ]

            let notificationCentre = NSNotificationCenter.defaultCenter()
            notificationCentre.postNotificationName("URLSCHEMEACTIVATEDNOTIFICATION", object: self, userInfo: userInfor)
            return true
        }
        else {
            return false
        }

    }
Dheeraj D
  • 4,386
  • 4
  • 20
  • 34