I create a handoff request for my apple watch app with the following code:
[self updateUserActivity:#"..." userInfo:selectedTopic webpageURL:[NSURL URLWithString:nil]];
And I handle the request in the phone AppDelegate with the following code:
- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray *))restorationHandler
{
BOOL handled = NO;
[userActivity becomeCurrent];
NSString *type = [userActivity activityType];
NSDictionary *userInfo = [userActivity userInfo];
if ([type isEqualToString:#"..."]) {
//does action
[userActivity invalidate];
handled = YES;
}
return handled;
}
The issue is the handoff icon does not go away even though this code is executed. What am I doing wrong here? Why will the handoff icon not disappear?
I've seen this behaviour on other applications. The only applications I see with the correct behaviour are native apps.
Related
I am trying to use Universal Links. On clicking the link app launches on the iPad but in the delegate call continueUserActivity: userActivity variable is nil. Here is my code in AppDelegate.
- (BOOL)application:(UIApplication *)application willContinueUserActivityWithType:(NSString *)userActivityType
{
if ([userActivityType isEqualToString: NSUserActivityTypeBrowsingWeb])
{
}
return true;
}
-(BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray * _Nullable))restorationHandler
{
if ([userActivity.activityType isEqualToString: NSUserActivityTypeBrowsingWeb]) {
NSURL *url = userActivity.webpageURL;
NSString *msg = [self getQueryComponentWithName:#"msg" fromURL:url];
}
return YES;
}
I was using Debug build. It works when I use release mode. To build in release mode please use following steps:
Go to Product menu
Select Scheme
Edit Scheme
In Build Configuration Select Release.
I'm building SDK for my product and there will be no app delegate class. I used Dropbox Integration in my SDK, so I'm bound to use following method.
- (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url {
NSString *stringUrl = [url absoluteString];
if ([stringUrl containsString:#"cancel"]) {
// Handle if user cancelled the login
[[NSNotificationCenter defaultCenter] postNotificationName:#"dropboxRegistrationCancel" object:self];
return NO;
}
if ([[DBSession sharedSession] handleOpenURL:url]) {
if ([[DBSession sharedSession] isLinked]) {
NSLog(#"App linked successfully!");
// At this point you can start making API calls
[[NSNotificationCenter defaultCenter] postNotificationName:ApplicationComeBackFromDropBoxLoginPage object:self];
}
return YES;
}
// Add whatever other url handling code your app requires here
return NO;
}
So Issue is there is no app delegate than how can use above method?
Or any alternate solution for this to use this in another class (like to inherit UIResponder class).
Looking for help.
If you are building a SDK with no appDelegate, then well, you have nothing to do. It will be in your project where you will be using your sdk that you will have to add this line.
By the way, the line NSString *stringUrl = [url absoluteString]; is absolutly useless since you are not using stringUrl later on, and the
- (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url {
// NSString *stringUrl = [url absoluteString];
return YES;
}
just means: yes you can quit my app to open another app when the user click on a link.
No biggy....
I have implemented Handoff in our App and it is working fine for web to app handoff and vice versa, when the app is running in foreground or in the background.
However if the app is not running, then if the user launches the app from a web to app handoff, in the launchOptions dictionary, I get the UIApplicationLaunchOptionsUserActivityDictionaryKey, but the reference to the activity is missing.
See screenshot:
As you can see I'm getting only the ID for the NSUserActivity.
Is this a bug in iOS 9 ?
Is there a way to get a reference to the activity by using the id?
Edit, here is the code, although I don't think this is relevant
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
if (launchOptions && [[launchOptions allKeys] containsObject:UIApplicationLaunchOptionsUserActivityDictionaryKey]) {
__block NSUserActivity *activity;
NSDictionary *userActivityDictionary = [launchOptions objectForKey:UIApplicationLaunchOptionsUserActivityDictionaryKey];
if (userActivityDictionary) {
[userActivityDictionary enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) {
if ([obj isKindOfClass:[NSUserActivity class]]) {
activity = obj;
}
}];
}
//app was started by URL (deep linking), check parameters
if (activity) {
NSURL *url = activity.webpageURL;
//resume from URL
}
}
return YES;
}
Ok,
I've submitted a TSI about this to Apple, and it seems that this is not a bug, but by design.
You can resume your activity in the application:continueUserActivity:restorationHandler delegate, which in my case was not called.
Well, my mistake was that you need to return YES in the application:didFinishLaunchingWithOptions: method, else if you return NO, the application:continueUserActivity:restorationHandler is not called.
We had implemented FB in our app, so we where returning [[FBSDKApplicationDelegate sharedInstance] application:application didFinishLaunchingWithOptions:launchOptions] which would return NO.
I have changed our code in the application:didFinishLaunchingWithOptions: function to this
if (launchOptions && [[launchOptions allKeys] containsObject:UIApplicationLaunchOptionsUserActivityDictionaryKey]) {
return YES;
}
else {
return [[FBSDKApplicationDelegate sharedInstance] application:application didFinishLaunchingWithOptions:launchOptions];
}
This way the application:continueUserActivity:restorationHandler delegate is being successfully called and the activity can be resumed successfully.
i have implemented URL Scheme in my App so that whenever i click the URL, it will open up my app. I have also inserted some request in the URL such as
Yuvtime//:?registerName=Puppy&Passwords=67825
and i use the openUrl in AppDelegate to handle the processing of the URL and pass the data (registerName=Puppy&Passwords=67825`) to my rootViewController which is the registration page of my app, the code is as shown below.
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation{
// Check the calling application Bundle ID
if ([[url scheme] isEqualToString:#"yuvitime"])
{
NSLog(#"Calling Application Bundle ID: %#", sourceApplication);
NSLog(#"URL scheme:%#", [url scheme]);
self.yuvitimeRequest = [url query];
NSLog(#"URL query: %#", yuvitimeRequest);
return YES;
}
else
return NO;
}
And in the RegistrationviewController, I implemented the code as follows.
-(void)viewWillAppear:(BOOL)animated{
[super viewDidAppear:animated];
AppDelegate * myAppDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
self.requestFromURL = myAppDelegate.yuvitimeRequest;
NSLog(#"%# hello google", self.requestFromURL);
if (self.requestFromURL.length > 0){
self.isRequestFromURL = YES;
}
if(self.isRequestFromURL == YES){
self.mainViewController.joinRoomName = self.requestFromURL;
self.isRequestFromURL = NO;
self.requestFromURL = #"";
[self presentViewController:self.mainViewController animated:YES completion:nil];
}
}
However, the problem i encounter is that:
If the user already has its app opened, this viewWilAppear in the RegistrationViewController will not be called again, so the data will not be passed automatically from the URL. (Only by killing the app, and click the URL will the data be passed as what we initially desired).
I also understand that when an App is already open and called again, only
(void)applicationWillEnterForeground:(UIApplication *)application &
(void)applicationDidBecomeActive:(UIApplication *)application
will be called in sequence in the app delegate. But somehow i stuck on how do i pass data to my registrationViewController from these two methods and if i can manage to do it in these two application methods, that would mean that my app checks every time on whether there is any registration data available from URL when the app comes to the foreground.
Since we already have the
(BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
to handle the URL scheme, is it possible that we only check once without using the foreground and didBecomeActive app delegate functions when we click the URL?
EDIT: After implementing URL scheme, whenever you click a URL, a delegate method in AppDelegate.m will be triggered.
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation;
This is also the function that will enable us to process the URL in our app and pass it around to other viewControllers.
I was kinda thinking, is there a way that we can implement a notification/function such that only when this openURL function is called, we then call the triggerURL function in registrationViewController.
Just out of curiosity!
Thanks
Regards
In your RegistrationViewController, you can register for UIApplicationDidBecomeActiveNotification and get notified of application being awakened even when your view controller was already loaded.
Add this line in your loadView:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(triggerURL) name:UIApplicationDidBecomeActiveNotification object:[UIApplication sharedApplication]];
This will allow the function updateSettings to be called every time your app wakes. Also remember to remove this listener at the end by:
[[NSNotificationCenter defaultCenter] removeObserver:self];
in your dealloc method.
Post that, this is how your viewWillAppear & triggerURL will look like:
- (void)viewWillAppear:(BOOL)animated{
[super viewDidAppear:animated];
[self triggerURL];
}
- (void)triggerURL {
AppDelegate * myAppDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
self.requestFromURL = myAppDelegate.yuvitimeRequest;
NSLog(#"%# hello google", self.requestFromURL);
if (self.requestFromURL.length > 0){
self.isRequestFromURL = YES;
}
if(self.isRequestFromURL == YES){
self.mainViewController.joinRoomName = self.requestFromURL;
self.isRequestFromURL = NO;
self.requestFromURL = #"";
[self presentViewController:self.mainViewController animated:YES completion:nil];
}
}
I am trying to check emails on gmail while app is in background.
I have to deal with OAuth2 to get to the gmail, so 1st I am refreshing the token. Then in finishedRefreshWithFetcher, if no error I am checking emails using Mailcore2.
It all works fine if app is in foreground, or in -(void) applicationDidBecomeActive: (UIApplication *) application {...}.
But in background fetch it never gets to -(void)auth:finishedRefreshWithFetcher:error:
-(void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
[self startOAuth2];
completionHandler(UIBackgroundFetchResultNewData);
}
- (void) startOAuth2{
static NSString *const kKeychainItemName = #"Google OAuth2 For MyApp";
static NSString *const kClientID = #"***";
static NSString *const kClientSecret = #"***";
GTMOAuth2Authentication * auth = [GTMOAuth2ViewControllerTouch authForGoogleFromKeychainForName:kKeychainItemName clientID:kClientID clientSecret:kClientSecret];
if ([auth refreshToken] != nil) {
[auth beginTokenFetchWithDelegate:self didFinishSelector:#selector(auth:finishedRefreshWithFetcher:error:)];
}
}
- (void)auth:(GTMOAuth2Authentication *)auth finishedRefreshWithFetcher:(GTMHTTPFetcher *)fetcher error:(NSError *)error {
if (error != nil) {// Refresh failed
return;
}
[self retrieveEmails];
}
I can store token and just reuse it w/o refresh, i.e. just skip startAuth2 and go straight to retriveEmails and it all works fine too, but not sure if it is safe.
Thanks
Because you're calling completionHandler(UIBackgroundFetchResultNewData); (which tells that your background fetch is done) immediatly after [self startOAuth2]; and thus without waiting for your delegate to be called.
You need to find a way to call completionHandler(UIBackgroundFetchResultNewData); in the - (void)auth:(GTMOAuth2Authentication *)auth finishedRefreshWithFetcher:(GTMHTTPFetcher *)fetcher error:(NSError *)error method.