11

I have implemented UIApplicationDelegate's

application:didFinishLaunchingWithOptions:

and

application:handleOpenURL:

according to specification, i.e.,

application:didFinishLaunchingWithOptions:
returns YES 

and

application:handleOpenURL: opens the URL. 

The code works under iOS 4 (in both cases, i.e., when the app is launched and when it becomes active from suspended state). However, the code does not work under iOS 3.2.

Priyal
  • 879
  • 1
  • 9
  • 30
Christian Fries
  • 16,175
  • 10
  • 56
  • 67
  • Whats the problem in 3.2? Error messages? The lines of code it breaks on? – Jesse Naugher Aug 31 '10 at 19:26
  • It appears that the delegate does not receive the message handleOpenURL at all if it answered the message didFinishLaunchingWithOptions, even if that method returned YES. The API documentation suggested a different behavior to me. – Christian Fries Aug 31 '10 at 20:09

4 Answers4

33

I give an answer to my own question. Finding out the solution took me a while and was quite frustrating. If you do an internet search you find some partial answers, but it still took me a while to work out the following solution and I do hope it adds some clarity.

So first, the recommended behavior of your app appears to be the following (see Opening Supported File Types in iOS Ref Lib):

  • Do not implement applicationDidFinishLaunching: (see the note at UIApplicationDelegate).
  • Implement application:didFinishLaunchingWithOptions: and check the URL, return YES if you can open it, otherwise NO, but do not open it.
  • Implement application:handleOpenURL: and open the URL, return YES if successful, otherwise NO.

In iOS 4, passing an URL to an app results in one of the following two behaviors:

  • If the app is launched then application:didFinishLaunchingWithOptions: is called and application:handleOpenURL: is called if and application:didFinishLaunchingWithOptions: returned YES.
  • If the app is becoming active from suspended state then application:didFinishLaunchingWithOptions: is not called but application:handleOpenURL: is called.

However, in iOS 3.2 it appears as if application:handleOpenURL: is never called! A hint that the behavior is different under iOS 3.2 can be found in Handling URL Requests. There you find that application:handleOpenURL: is called if application:didFinishLaunchingWithOptions: is not implemented, but applicationDidFinishLaunching: is implemented. But application:handleOpenURL: is not called if application:didFinishLaunchingWithOptions: is implemented.

Hence, one solution to make the code work under 3.2 and 4.0 is:

  • Open the URL in application:didFinishLaunchingWithOptions:, but then return NO to prevent that application:handleOpenURL: is called.
  • Open the URL in application:handleOpenURL:, in case you are under 4.0 and the app was in suspended state.

I found this solution in another post, but I was confused, because it contradicted the recommendation in iOS Ref Lib documentation (namely that we should return YES in application:didFinishLaunchingWithOptions:). (At that point I did not realize that the documentation contradicts it self).

I believe that the current iOS 4.0 behavior will be the future behavior I prefer the following solution:

  • Do not implement applicationDidFinishLaunching:.
  • Implement application:didFinishLaunchingWithOptions: and check the URL, return YES if you can open it, otherwise NO, but do not open it. If we are on 3.2, open the URL.
  • Implement application:handleOpenURL: and open the URL, return YES if successful, otherwise NO.

So in summary, I implement the iOS 4 behavior and added the following line to application:didFinishLaunchingWithOptions:

    if([[[UIDevice currentDevice] systemVersion] hasPrefix:@"3.2"]) {
        [self application:application handleOpenURL:url];
    }

which make the code work under 3.2.

Christian Fries
  • 16,175
  • 10
  • 56
  • 67
  • Thanks, I had the same problem and this answer saved me a lot of time. – bosmacs Dec 09 '10 at 14:23
  • I still couldn't get this to work until I realized that I had my project compiling with a Base SDK of 3.2 but running on a 4.0 simulator. The result? When becoming active, NONE of the above methods are called. Once I switching the base SDK to 4.0, everything worked. Not sure how to make it work in 3.2 ipad tho. – Rob Van Dam Jan 19 '11 at 05:18
  • Good point. Also: Simulator was not a very reliable test environment for me. I found things to work in a simulator that did not on a device and vice versa. One example: http://stackoverflow.com/questions/3616981 - but the simulator has improved now. – Christian Fries Feb 13 '11 at 19:24
  • I would add url&& to the if statement – Casebash May 13 '11 at 06:21
  • Is this behavior also present in iOS 3.2.2 (the latest in 3.2 iOS category)? If so then we can check whether iOS is less than 4.0 (to remain compatible older iPods & iPhones) – Kashif Hisam Jun 30 '12 at 09:05
  • @Kashif Hisam: http://stackoverflow.com/questions/3339722/check-iphone-ios-version – Christian Fries Aug 15 '13 at 09:31
7

application:handleOpenURL: is now DEPRECATED.

As of iOS 4.2, you can use this for opening URLs:

- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url 
        sourceApplication:(NSString *)sourceApplication annotation:(id)annotation

Documentation:

https://developer.apple.com/library/ios/#DOCUMENTATION/UIKit/Reference/UIApplicationDelegate_Protocol/Reference/Reference.html

Emil
  • 7,220
  • 17
  • 76
  • 135
powertoold
  • 1,593
  • 13
  • 19
2

I started writing application which used Dropbox api. To understand concept, I ran a sample application using my Key/secret mentioned at dropbox/developer documentation. Once sample app started working, I used same key/secret values for my application.

For sample app, implementation of handleOpenURL (or openURL on iOS 4.2) gets executed as expected. For some odd reason, it wasn't the case for my app. My app entered background in order to show login screen and authentication page of dropbox. After successful login and authentication, my app never entered foreground. It was true for both platform Simulator and device (iPad)

I tried almost everything listed on internet including this post. Thanks. There was NO success, though.

At last, it STARTED working for my application when I did following:

  • On simulator, select "iOS Simulator --> Reset Content and Settings", and reset.
  • On device, I deleted sample application related executable and which in turn delete cache associated to it.
Hitesh Savaliya
  • 1,336
  • 13
  • 15
0

Add the following to the end of application:DidFinishLaunchingWithOptions:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

    ...    

    NSURL *url = (NSURL *)[launchOptions valueForKey:UIApplicationLaunchOptionsURLKey];
    if (url != nil && [url isFileURL]) {
        return YES;
    }  else return NO;
} // End of application:didFinishLaunchingWithOptions:

// New method starts 
-(BOOL) application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
{
    mvc = [nc.viewControllers objectAtIndex:0];
    if (url != nil && [url isFileURL]) {
        [mvc handleOpenURL:url];
    }
    return YES;
}

where mvc is my main ViewController, and nc my navigation controller.

Then in the MainViewController, do something like this:

- (void)handleOpenURL:(NSURL *)url {
    [self.navigationController popToRootViewControllerAnimated:YES];

    // Next bit not relevant just left in as part of the example
    NSData *jsonData = [NSData dataWithContentsOfURL:url];        
    NSError *error;
    NSDictionary *dictionary = [[NSJSONSerialization JSONObjectWithData:jsonData options:kNilOptions error:&error] objectAtIndex:0];
    [self managedObjectFromStructure:dictionary withManagedObjectContext:self.context];
    ...
}

after declaring handleOpenURL in the .h of course.

Thanks goes to Christian for putting in the effort for this.

Bijoy Thangaraj
  • 5,434
  • 4
  • 43
  • 70
Steve
  • 988
  • 1
  • 12
  • 25