Today App Extension Widget Tap To Open Containing App
Asked Answered
G

5

59

I've implemented a Today widget for my application +Quotes which displays the day's quote within the notification center with the help of these Apple Docs. What I'd like to accomplish is opening the Containing App, in this case +Quotes, when the user taps the +Quotes widget within their Today notification view, not entirely sure what to call this, as Calendar would if you tapped it in the Today view. I've tried overlaying a button over the label which would call -(void)openURL:(NSURL *)URL completionHandler:(void (^)(BOOL success))completionHandler upon it being tapped, then open the Custom URL Scheme I have declared to open the Containing App. The issue is it doesn't open the Containing App.

+Quotes Today App Extension Widget

-(IBAction)myButton:(id)sender {
    NSURL *customURL = [NSURL URLWithString:@"PositiveQuotes://"];
    [self openURL:customURL completionHandler:nil];
}
Gloam answered 3/6, 2014 at 16:4 Comment(0)
C
63

EDIT: Ok, just a little correction here. I got it working with placing a button over the label just like suggested above and the following code:

- (IBAction) goToApp: (id)sender { 
    NSURL *url = [NSURL URLWithString:@"floblog://"];
    [self.extensionContext openURL:url completionHandler:nil]; 
  }

I linked it to a "Touch Up Inside" event. However, this also causes the app to launch when the user scrolls the Today view.

=======================================

I ran into the same issue. However, it seems that there is no solution for now since the release notes for the first beta of iOS 8 mention:

Known Issues: openURL does not work from an extension.

So I guess we will at least have to wait until beta 2.

Cantillate answered 4/6, 2014 at 13:25 Comment(9)
Ok, just a little correction here. I got it working with placing a button over the label just like suggested above and the following code: - (IBAction) goToApp: (id)sender { NSURL *url = [NSURL URLWithString:@"floblog://"]; [self.extensionContext openURL:url completionHandler:nil]; } I linked it to a "Touch Up Inside" event. However, this also causes the app to launch when the user scrolls the Today view.Cantillate
No problem. Do you have an idea how to solve the behavior I described in my last sentence?Cantillate
Does that only happen when you scroll and your finger is still on your invisible button?Jabberwocky
Yes, but it should also not happen when you scroll outside the invisible button. Basically it shoul only happen on a tap but not on a scroll gesture.Cantillate
sunseeker,it would be great if you could edit your answer so that the right answer is shown to everybody :)Milkweed
This is fixed in Beta 2.Urbina
For future readers, it is worth noting that this method is only allowed in Today Extensions.Hangbird
It works for me too! I spent lot of time even after i used the above code by creating my own instance of 'NSExtensionContext' instead of just using 'self.extensionContext' which is mentioned in the above code Thank god! Finally i realised that i should simply use 'self.extensionContext'Jihad
Instead of adding an empty button over the label to handle the tab, I use Single Tap Gesture Recognizer. And after that add following function which I mentioned in selector. - (void)handleSingleTap:(UITapGestureRecognizer *)recognizer { NSURL *myURL = [NSURL URLWithString:@"AppUrlType://home"]; [self.extensionContext openURL:myURL completionHandler:nil]; }Systematics
D
52

Swift 2 version, according to Apple Doc

extensionContext?.openURL(NSURL(string: "foo://")!, completionHandler: nil)

Swift 3 version

extensionContext?.open(URL(string: "foo://")! , completionHandler: nil)

And don't forget to add Custom URL Schemes into Info.plist

enter image description here

Ditter answered 24/8, 2015 at 17:7 Comment(2)
How would one go about restoring the userActivity like a search being tapped? Do we need to create an NSUserActivity in our TodayViewController as the widget it being tapped (i.e. before the openURL call)?Euryale
Just in case anyone else is as daft as me: this URL Type is in the main app target's info tab - not the widget'sMethacrylate
M
27

The answer by @sunseeker is the good one but it is "hidden" in the comments. And as the accepted answer says that this isn't possible, it may mislead visitors.

this code works:

- (IBAction)launchHostingApp:(id)sender
{
  NSURL *pjURL = [NSURL URLWithString:@"hostingapp://home"];
  [self.extensionContext openURL:pjURL completionHandler:nil];
}

I'm using Xcode 6.0 (6A215l) with Yosemite Beta 1.

And like Apple says in Handling Commons Scenarios :

An extension doesn’t directly tell its containing app to open; instead, it uses the openURL:completionHandler: method of NSExtensionContext to tell the system to open its containing app. When an extension uses this method to open a URL, the system validates the request before fulfilling it.

Milkweed answered 14/6, 2014 at 21:49 Comment(3)
Will this work on simulator.I am getting an alert telling this url is unsupported.'Tradespeople
the "hostingapp" scheme is something you have to set in your Infos.plist. It can be anything. More details here: developer.apple.com/library/ios/documentation/iPhone/Conceptual/… in the section "Implementing Custom URL Schemes"Milkweed
For create in your plist the URL Scheme "hostingapp" you can see #8202224 @Mike S.Metamorphism
L
3

Another way to do this without adding a hidden button is to add a UITapGestureRecognizer on the UILabel (make sure to set userInteractionEnabled to true on the label). Check the recognizer state in the handler to make sure you reached UIGestureReconizerStateEnded (and not Cancelled or Failed) and then run your openUrl code.

Lucianolucias answered 17/6, 2014 at 18:55 Comment(0)
B
3

Just in case, here's the version for Swift 3 with the error handling version:

let myAppUrl = URL(string: "main-screen:")!
extensionContext?.open(myAppUrl, completionHandler: { (success) in
    if (!success) {
        print("error: failed to open app from Today Extension")
    }
})

To make it work you need to open Application's info.plist (open as source code) and in the very top, after this

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>

add the following, so the App will know which URLs it should handle Here's the complete example of how to open the containing app and share User Defaults between app and Extension.

Bristol answered 16/2, 2017 at 5:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.