I've implemented a Notification Center Extension that provides a button to launch its containing app. Depending on the state of the widget I want to navigate to one of two different locations in the app when this button is pressed. I was able to obtain this behavior and it works well while the app is in memory, however, if the app has to be launched for the first time when this button is pressed it does not navigate to the correct location.
I know the reason why this occurs. When I parse the URL to determine where to go in the app delegate, I post a notification and provide information in the userInfo dictionary. I subscribe to this notification in the View Controller that needs to update its UI based on the information in the userInfo dictionary. The problem is, I subscribe to this notification in viewWillAppear. This notification is posted before viewWillAppear is called on this View Controller when the app is launched for the first time, therefore the UI does not update and it behaves as if the app was launched from the home screen.
My question is, is there a different method I can place the code to subscribe to this notification that's early enough in the lifecycle, or is there a better approach to respond to launching from a URL scheme?
Some simplified code:
//AppDelegate.m:
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
if ([[url scheme] isEqualToString:#"app name"]) {
if ([[url resourceSpecifier] isEqualToString:#"//tab1"]) {
[[NSNotificationCenter defaultCenter] postNotificationName:DidLaunchWithURLScheme object:nil userInfo:#{#"info": #"first"}];
} else if ([[url query] isEqualToString:#"//tab2"]) {
[[NSNotificationCenter defaultCenter] postNotificationName:DidLaunchWithURLScheme object:nil userInfo:#{#"info": #"second"}];
}
return YES;
}
return NO;
}
//viewWillAppear in View Controller:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(didLaunchAppWithURLInfo:)
name:DidLaunchWithURLScheme
object:nil];
- (void)didLaunchAppWithURLInfo:(NSNotification *)notification {
//does get called if app was already in memory
//does not get called if app was fresh launched
//do something here like select a different tab based on the userInfo
}
Related
I want to check whether my app was launched for background fetch in my application delegate's didFinishLaunchingWithOptions. There is nothing in launchOptions dictionary. So is there any way to check it?
I know that I can check applicationState, but for some reason sometimes it returns UIApplicationStateBackground, even if I launch app normally.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
if (application.applicationState != UIApplicationStateBackground) {
// Analytics initialization code
}
}
I've created breakpoint at Analytics initialization code and sometimes it enters to this block even if I launch app normally!
I know that I can detect the state later when applicationDidBecomeActive or applicationDidEnterBackground will be called. If I will use these approach to detect the state I need to move my Analytics initialization code to some other place. If it remain in application:didFinishLaunchingWithOptions: it will be called every time when my app starts background fetch. So maybe I should just move Analytics initialization code to some other method and don't check applicationState in application:didFinishLaunchingWithOptions:? If so which method I can use for this?
Take a look at slides 19-21 of this presentation: http://www.slideshare.net/moliver816/background-fetch
Immediately upon launch, you can check if applicationState equals UIApplicationStateBackground in order to determine whether your app was launched into the background.
Otherwise, if you just want to know when your app is fetching data for background app refresh, you can do so inside the UIApplicationDelegate method application:performFetchWithCompletionHandler:.
You can wrap you analytics initialization code in a singleton using GCD dispatch once. That way you know it only runs one time per application load. Resume from background or any other states won't matter since the lifecycle is still continuous.
+ (Analytics*)sharedInstance
{
static dispatch_once_t onceToken;
static Analytics* sharedInstance;
dispatch_once(& onceToken, ^{
sharedInstance = [[self alloc] init];
//Do analytics initialization code here
});
return sharedInstance;
}
Try this one
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(AppIsInBackground:)
name:UIApplicationDidEnterBackgroundNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(AppIsActive:)
name:UIApplicationDidBecomeActiveNotification
object:nil];
you have to call the selector : NSNotificationCenter is calling AppIsInBackground Selector (App in background)
- (void) AppIsInBackground:(NSNotification *) notification {
//Shut down the memory/processor intensive things and save any states for when the app is reinitialized
}
following is the delegate method which is called when the background fetch is performed by the iOS for our application.
This is a appDelegate method.
func application(_ application: UIApplication, performFetchWithCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
}
Is there any way to find out after notification is been sent, how many users clicked on the notification and how many people didnt click on the noficiation event (badge) when the app is in the background?
I am more interested to find out how many people didnt click, as people who clicked can be tracked as app will go in the foreground and request can be made vs if app is in the background, your http request may get lost.
update your app delegate code to the following code:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{
[super application:application didFinishLaunchingWithOptions:launchOptions];
NSDictionary *remoteNotif = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
if(remoteNotif)
{
//launched from push notification
}else{
//Did not launch from push notification (tapped on app icon, or from multi tasking)
//**Didn't click on notification**
}
}
and this:
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
if([application applicationState] == UIApplicationStateActive) {
// app was open, did not display the push alert/banner/badge
// **Didn't click on notification**
}else{
//launched from push notification
}
}
Its quite self explanatory. you can track when app was opened by tapping on a push notification and when it was opened without tapping on a notification.
I guess the closest you can come to know who didn't click your notification is by checking in your AppDelegate's didFinishLaunchWithOptions method that your app didn't get launched as a result of the user tapping a notification after you send out the notification. In other words, I think you answered your own question in your question.
I have a class which will make NSNotificationCenter to observe whether the app is active or it went to background and it will do a set of actions according to that.
I want to set a NSNotificationCenter to observe whether the app is opened through URL scheme .
This is what I tried:
[[NSNotificationCenter defaultCenter] addObserver:[self class] selector:#selector(appOpenedThroughUrl:) name:UIApplicationLaunchOptionsURLKey object:nil];
But its not getting called when the app opens through URL please provide me some idea to do the same.
I don't want to use the delegate:
- (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url
+(void)appOpenedThroughUrl:(NSNotification *)notification {
NSLog(#"%#",notification.userInfo);
}
you need to post notification whenever it open through custom url scheme
[[NSNotificationCenter defaultCenter] postNotificationName:UIApplicationLaunchOptionsURLKey object:self];
I'm struggling with following problem:
I am using the Method
(BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
to set a Serverstring from extern. So when i do call myapp://serverurl/url the string url is set correctly to a NSUSerDefaults Key. That works fine so far.
But when I am currently in the Settingsview of my App, where you can set this variable manually and i call the above url for example from the Mail-App, it sets the variable, but the corresponding Textfield of the View is not updated. So the old string is still written in the Textfield but in NSUSerDefaults its updated correctly.
I tried to work with the Notification Center. I bound the the following Method:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(updateServerURLTextField) name:UIApplicationWillEnterForegroundNotification object:nil];
- (void)updateServerURLTextField
{
NSLog(#"Updating Serveraddress label");
NSString* serverFullURL = [[NSUserDefaults standardUserDefaults] objectForKey:wServerFullURLKey];
self.serverURLTextField.text = serverFullUrl;
}
The Notification gets fired so does updateServerURLTextField but its not updated. I assume some race time conditions?
- (void)applicationDidBecomeActive:(UIApplication *)application
gets called after the Notification is fired. Maybe the GUI cant be updated before the App is active?
Anyone got some hints?
You can't update the UI this way. All changes to UI need to happen on the main thread. You might consider using: PerformSelectorOnMainThread:withObject:waitUntilDone:
In UIViewController I register for UIApplicationWillEnterForegroundNotification notification.
-(void)viewDidAppear:(BOOL)animated {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(btnContinuePressed:)
name:UIApplicationWillEnterForegroundNotification
object:nil];
}
I want to execute btnContinuePressed: only if the application was resumed in a normal way - clicking the icon or opening via multitasking menu.
Method btnContinuePressed: should not be executed when the application was opened using URL scheme. Opening via URL scheme is handled in AppDelegate using custom notification:
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
[[NSNotificationCenter defaultCenter] postNotificationName:#"didOpenViaUrl"
object:url];
return YES;
}
Bottom line: notification UIApplicationWillEnterForegroundNotification should not be triggered if didOpenViaUrl was.
In simple word you can not make difference them but you can do a tricky way to achieve by creating a BOOL property in your appDelegate code which will tell you from where the application has been opened I think!