I have been searching around for a while trying to find an answer but I cant seem to find one. What I am trying to do is have my app send a local notification when the app is running in the background and when the user opens the notification it will take them to a website. I have it all set up but it keeps opening the app instead of going to the website.
My question is, is this even possible to do? And if so could you please see where i am going wrong with my code below? Thank you for your help.
CODE:
-(void)applicationDidEnterBackground:(UIApplication *)application
{
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
NSDate *date = [NSDate date];
NSDate *futureDate = [date dateByAddingTimeInterval:3];
notifyAlarm.fireDate = futureDate;
notifyAlarm.timeZone = [NSTimeZone defaultTimeZone];
notifyAlarm.repeatInterval = 0;
notifyAlarm.alertBody = #"Visit Our Website for more info";
[app scheduleLocalNotification:notifyAlarm];
if ( [notifyAlarm.alertBody isEqualToString:#"Visit Our Website for more info"] ) {
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:#"https://www.bluemoonstudios.com.au"]];
}
In Your Business Logic
-(void)scheduleNotificationForDate:(NSDate *)fireDate{
UILocalNotification *notification = [[UILocalNotification alloc] init];
notification.fireDate = fireDate;
notification.alertAction = #"View";
notification.alertBody = #"New Message Received";
notification.userInfo = #{#"SiteURLKey": #"http://www.google.com"};
[[UIApplication sharedApplication] scheduleLocalNotification:notification];
}
in your AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
UILocalNotification *notification = [launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey];
if(notification != nil){
NSDictionary *userInfo = notification.userInfo;
NSURL *siteURL = [NSURL URLWithString:[userInfo objectForKey:#"SiteURLKey"]];
[[UIApplication sharedApplication] openURL:siteURL];
}
}
-(void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification{
NSDictionary *userInfo = notification.userInfo;
NSURL *siteURL = [NSURL URLWithString:[userInfo objectForKey:#"SiteURLKey"]];
[[UIApplication sharedApplication] openURL:siteURL];
}
Related
I need to run an NSTimer in an app even after the user minimizes the app. The Timer is used to hit a web service every 15 seconds and after 4 minutes I need to perform certain navigations. How can this be done?
I know this question has been asked earlier too but I have not been able to find an appropriate answer.
You need a reason for it to be possible.
If it's for navigation app, VOIP, or just for background downloads (in some cases), it's possible.
But otherwise, you can't do it.
Before you proceed, think about why it isn't easy to make a timer run when the app is in the background. It's because it empties the phone's battery. You get unhappy customers if you do that. Which Apple doesn't care much about, but Apple gets unhappy customers, which they care about a lot. Under Settings -> Battery Apple will tell the user which app used how much energy in the last 24 hours / 6 days, so people can find your app if it does waste energy in the background and throw it out.
You can configure you app to run in the background, but you have no control over how frequently iOS calls you app in the background.
I know this is a really bad solution and misuse of the service, but you could initiate a background file download and make your server to send data so slowly that it takes about 4 minutes. iOS will report to your app when the transfer is complete. If your app is not terminated iOS also reports the download progress to your app. But you can't open the app ui when the transfer is complete if the app is not in the foreground.
Create the configuration object using the
backgroundSessionConfigurationWithIdentifier: method of
NSURLSessionConfiguration.
Set the value of the configuration object’s
sessionSendsLaunchEvents property to YES.
if your app starts transfers
while it is in the foreground, it is recommend that you also set the
discretionary property of the configuration object to YES.
Configure
any other properties of the configuration object as appropriate.
Use
the configuration object to create your NSURLSession object.
When all of the tasks associated with a background session are
complete, the system relaunches a terminated app (assuming that the
sessionSendsLaunchEvents property was set to YES and that the user did
not force quit the app) and calls the app delegate’s
application:handleEventsForBackgroundURLSession:completionHandler:
method.
https://developer.apple.com/library/ios/documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/BackgroundExecution/BackgroundExecution.html
AppDelegate.h // using local notification to get some function call
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[self getNotificationPermission];
return YES;
}
This method used for permission
-(void)getNotificationPermission{
// Accept Action
UIMutableUserNotificationAction *acceptAction = [[UIMutableUserNotificationAction alloc] init];
acceptAction.identifier = #"Accept";
acceptAction.title = #"Yes i am OK";
acceptAction.activationMode = UIUserNotificationActivationModeBackground;
acceptAction.destructive = NO;
acceptAction.authenticationRequired = NO;
// Reject Action
UIMutableUserNotificationAction *rejectAction = [[UIMutableUserNotificationAction alloc] init];
rejectAction.identifier = #"Reject";
rejectAction.title = #"On i am Not OK";
rejectAction.activationMode = UIUserNotificationActivationModeBackground;
rejectAction.destructive = YES;
rejectAction.authenticationRequired = YES;
// Reply Action
UIMutableUserNotificationAction *replyAction = [[UIMutableUserNotificationAction alloc] init];
replyAction.identifier = #"Reply";
replyAction.title = #"Reply";
replyAction.activationMode = UIUserNotificationActivationModeForeground;
replyAction.destructive = NO;
replyAction.authenticationRequired = YES;
// Email Category
UIMutableUserNotificationCategory *emailCategory = [[UIMutableUserNotificationCategory alloc] init];
emailCategory.identifier = #"Email";
[emailCategory setActions:#[acceptAction,rejectAction,replyAction] forContext:UIUserNotificationActionContextDefault];
[emailCategory setActions:#[acceptAction,rejectAction] forContext:UIUserNotificationActionContextMinimal];
// Download Action
UIMutableUserNotificationAction *downloadAction = [[UIMutableUserNotificationAction alloc] init];
downloadAction.identifier = #"Download";
downloadAction.title = #"Download";
downloadAction.activationMode = UIUserNotificationActivationModeForeground;
downloadAction.destructive = NO;
downloadAction.authenticationRequired = YES;
UIMutableUserNotificationAction *cancelAction = [[UIMutableUserNotificationAction alloc] init];
cancelAction.identifier = #"Cancel";
cancelAction.title = #"Cancel";
cancelAction.activationMode = UIUserNotificationActivationModeForeground;
cancelAction.destructive = YES;
cancelAction.authenticationRequired = YES;
// Image Category
UIMutableUserNotificationCategory *imageCategory = [[UIMutableUserNotificationCategory alloc] init];
imageCategory.identifier = #"Image";
[imageCategory setActions:#[downloadAction,cancelAction] forContext:UIUserNotificationActionContextDefault];
[imageCategory setActions:#[downloadAction,cancelAction] forContext:UIUserNotificationActionContextMinimal];
NSSet *categories = [NSSet setWithObjects:emailCategory,imageCategory, nil];
UIUserNotificationType notificarionType = UIUserNotificationTypeBadge | UIUserNotificationTypeSound | UIUserNotificationTypeAlert;
// Register notification types and categories
UIUserNotificationSettings *notificationSettings = [UIUserNotificationSettings settingsForTypes:notificarionType categories:categories];
[[UIApplication sharedApplication] registerUserNotificationSettings:notificationSettings];
// This line of code is required for remote push notification
//[[UIApplication sharedApplication] registerForRemoteNotifications];
}
-(void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings{
//UIUserNotificationType notificationtype = [notificationSettings types];
}
// You can check the current user notifiartions settings whenever you need it
-(void)getReadyForNotification{
//UIUserNotificationSettings *settings = [[UIApplication sharedApplication] currentUserNotificationSettings];
//[self checkSettings:settings];
}
// When appliation is in foregrounf this methid will get caled if you have scheduled a local notification
-(void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification{
//CLRegion *region = notification.region;
if ([notification.category isEqualToString:#"Image"]) {
NSLog(#"Image category notification is presented to the user");
}else if([notification.category isEqualToString:#"Email"]){
NSLog(#"Email category notification is presented to the user");
}
}
-(void)application:(UIApplication *)application handleActionWithIdentifier:(NSString *)identifier forLocalNotification:(UILocalNotification *)notification completionHandler:(void (^)())completionHandler{
if ([identifier isEqualToString:#"Accept"]) {
[self printAlert:#"Accept"];
}else if ([identifier isEqualToString:#"Reject"]) {
[self printAlert:#"Reject"];
}else if ([identifier isEqualToString:#"Reply"]) {
[self printAlert:#"Reply"];
}else if ([identifier isEqualToString:#"Download"]) {
[self printAlert:#"Download"];
}else if ([identifier isEqualToString:#"Cancel"]) {
[self printAlert:#"Cancel"];
}
completionHandler();
}
-(void)printAlert:(NSString *)title
{
NSLog(#"%# button is selected",title);
if ([title isEqualToString:#"Accept"])
{
}
else if ([title isEqualToString:#"Reject"])
{
}
}
// Callback for remote notification
-(void)application:(UIApplication *)application handleActionWithIdentifier:(NSString *)identifier forRemoteNotification:(NSDictionary *)userInfo completionHandler:(void (^)())completionHandler{
NSLog(#"handleActionWithIdentifier forRemoteNotification");
}
Set Local Notification on ViewController
UILocalNotification* localNotification = [[UILocalNotification alloc] init];
localNotification.fireDate = [NSDate dateWithTimeIntervalSinceNow:10];
localNotification.alertBody = #"How you are Feeling Today ?";
localNotification.category = #"Email"; // This should match categories identifier which we have defined in App delegate
[[UIApplication sharedApplication] scheduleLocalNotification:localNotification];
Using this code you will Get local notification after every cycle of time period if user select notification in foreground then open app in particular stage or screen you want. this code is also not breaching apple guidelines.
I am trying to implement local notification in my application. I don't know how to do properly, below code I am using for new data arrival process, here after how to implement Notification process and I need notifications during both foreground and background modes.
Below I had successfully background fetching process for new data arrival checking method
// Value matching and trying to get new data
[live_array removeObjectsInArray:stored_array];
// if you require result as a string
NSString *result = [stored_array componentsJoinedByString:#","];
NSLog(#"New Data: %#", result); // objects as string:
Above code finally giving some string value...Once the value came I want to show the notification. Everything I am doing is in the App Delegate.
1) When the app is closed, schedule a local notification that will fire in 24 hours
- (void)applicationDidEnterBackground:(UIApplication *)application
{
UILocalNotification *notification = [[UILocalNotification alloc] init];
notification.fireDate = [[NSDate date] dateByAddingTimeInterval:60*60*24];
notification.alertBody = #"24 hours passed since last visit :(";
[[UIApplication sharedApplication] scheduleLocalNotification:notification];
}
2) if the app is opened (before the local notification fires), cancel the local notification
- (void)applicationDidBecomeActive:(UIApplication *)application
{
[[UIApplication sharedApplication] cancelAllLocalNotifications];
}
//For local Notification
first thing we need to do is register the notifications.
// New for iOS 8 - Register the notifications
UIUserNotificationType types = UIUserNotificationTypeBadge | UIUserNotificationTypeSound | UIUserNotificationTypeAlert;
UIUserNotificationSettings *mySettings = [UIUserNotificationSettings settingsForTypes:types categories:nil];
[[UIApplication sharedApplication] registerUserNotificationSettings:mySettings];
Now let’s create the notification itself
UILocalNotification *notification = [[UILocalNotification alloc] init];
if (notification)
{
notification.fireDate = _datePicker.date;
NSDate *fireTime = [[NSDate date] addTimeInterval:10]; // adds 10 secs
notification.fireDate = fireTime;
notification.alertBody = #"Alert!";
notification.timeZone = [NSTimeZone defaultTimeZone];
notification.applicationIconBadgeNumber = 1;
notification.soundName = UILocalNotificationDefaultSoundName;
switch (_frequencySegmentedControl.selectedSegmentIndex) {
case 0:
notification.repeatInterval = NSCalendarUnitDay;
break;
case 1:
notification.repeatInterval = NSCalendarUnitWeekOfYear;
break;
case 2:
notification.repeatInterval = NSCalendarUnitYear;
break;
default:
notification.repeatInterval = 0;
break;
}
notification.alertBody = _customMessage.text;
Once we have the notification created we need to schedule it with the app.
// this will schedule the notification to fire at the fire date
[[UIApplication sharedApplication] scheduleLocalNotification:notification];
// this will fire the notification right away, it will still also fire at the date we set
[[UIApplication sharedApplication] presentLocalNotificationNow:notification];
If we leave things the way they are now a notification will only appear on screen if the app is in the background. In order to display something when the app is in the foreground and a notification fires we need to implement a method in the app delegate.
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
{
UIAlertView *alertView = [[UIAlertView alloc]initWithTitle:#"Notification Received" message:notification.alertBody delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil, nil];
[alertView show];
}
We added a icon badge to our app, and this icon badge will only display when the app is in the background. Generally you want to dismiss the icon once a user has opened the app and seen the notification. We’ll need to handle this in the app delegate as well.
These two methods will take care of it.
- (void)applicationWillEnterForeground:(UIApplication *)application
{
// Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
NSLog(#"%s", __PRETTY_FUNCTION__);
application.applicationIconBadgeNumber = 0;
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
NSLog(#"%s", __PRETTY_FUNCTION__);
application.applicationIconBadgeNumber = 0;
}
iOS 10 by Apple document:
UNMutableNotificationContent* content = [[UNMutableNotificationContent alloc] init];
content.title = [NSString localizedUserNotificationStringForKey:#"Hello!" arguments:nil];
content.body = [NSString localizedUserNotificationStringForKey:#"Hello_message_body"
arguments:nil];
content.sound = [UNNotificationSound defaultSound];
// Deliver the notification in five seconds.
UNTimeIntervalNotificationTrigger* trigger = [UNTimeIntervalNotificationTrigger
triggerWithTimeInterval:5 repeats:NO];
UNNotificationRequest* request = [UNNotificationRequest requestWithIdentifier:#"FiveSecond"
content:content trigger:trigger];
// Schedule the notification.
UNUserNotificationCenter* center = [UNUserNotificationCenter currentNotificationCenter];
[center addNotificationRequest:request withCompletionHandler:nil];
I understand we can check if a user has enabled/disabled Remote Notification with this code:
[[UIApplication sharedApplication] enabledRemoteNotificationTypes]
But what about checking for Local Notification?
I don't find a corresponding property for local notification types, and I have verified that enabledRemoteNotificationTypes is only for remote notifications.
And we all know, users can edit their Notification settings, which will affect both remote and local.
I'm not sure, but I don't think you can access this information.
One way you can check if the user has notifications enabled for your app is to send yourself a local notification with a 1 second delay :
UILocalNotification *testNotification = [[UILocalNotification alloc] init];
localNotification.fireDate = [NSDate dateWithTimeIntervalSinceNow:1];
localNotification.alertBody = #"Test notification";
localNotification.timeZone = [NSTimeZone defaultTimeZone];
[[UIApplication sharedApplication] scheduleLocalNotification:localNotification];
And check if you catch it in :
-(void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification) {
// If you get here, notifications are enabled
}
All there is left is add info (e.g in localNotification.userInfo) so you can know in didReceiveLocalNotification: if you are handling your test notification, or if it's a "real" notification.
- (BOOL) isLocalNotificationsEnable {
if ([[UIApplication sharedApplication] respondsToSelector:#selector(currentUserNotificationSettings)]) {
UIUserNotificationSettings *grantedSettings = [[UIApplication sharedApplication] currentUserNotificationSettings];
return (grantedSettings.types == UIUserNotificationTypeNone) ? NO : YES;
}
return NO;
}
Note: the if block is only require if you're targeting < iOS 8.0.
I have an app where I need to send a UILocationNotification when the app is not in the foreground / Active. If I have my code to do so, it will not send until the app is opened and is active.
Here is my function:
- (void)setLocalNotificationForType:(SPKLocalNotificationType)notificationType fromUser:(NSString *)userName withMessageText:(NSString *)msgText {
UILocalNotification *notif = [[UILocalNotification alloc] init];
notif.userInfo = #{#"handle": _convoHandle, #"room": _convoName};
notif.fireDate = [NSDate date];
notif.applicationIconBadgeNumber = [[UIApplication sharedApplication] applicationIconBadgeNumber] + 1;
notif.soundName = UILocalNotificationDefaultSoundName;
notif.timeZone = [NSTimeZone defaultTimeZone];
if (notificationType == 1) { // message
notif.alertBody = [NSString stringWithFormat:#"#%# said: \"%#\"", userName, msgText];
} else if (notificationType == 2) { // image
notif.alertBody = [NSString stringWithFormat:#"#%# sent an image", userName];
} else {
return;
}
if ([[UIApplication sharedApplication] applicationState] != UIApplicationStateActive) {
[[UIApplication sharedApplication] presentLocalNotificationNow:notif];
}
}
Update:
It now seems that the problem is that the connection to the server is being "paused" while the app is in the background. Once I then open the app all the data comes in at once. I am using SocketRocket for connecting to my Node.js Primus web socket server. Is this something that normally happens? This is my first time using SocketRocket so I'm not sure.
Update:
I have also enabled Remote Notifications for Background Modes, I have also registered for remote notifications, and on the device, I have also made sure that "banners" and "badges" are enabled.
Update:
I have additionally set the web socket to use a background queue.
[webSocket setDelegateOperationQueue:[NSOperationQueue new]];
The connection is still being paused.
Thanks!
Edit: It looks like SocketRocket uses the main dispatch queue by default, which runs on the application's main thread. When the app is backgrounded, processing on the main thread stops, so it would be worth trying to move the work to a background thread in a background operation queue.
On your SRWebSocket object, try calling:
[webSocket setDelegateOperationQueue:[NSOperationQueue new]];
Without checking the docs, perhaps setting timeZone and fireDate on the UILocalNotification is interfering. You don't need those if you're going to pass the notification to presentLocalNotificationNow:.
I have also found that, even if you're using local notifications exclusively, you must:
Enable Remote notifications for your target on the Capabilities tab under Background Modes
Enable either alerts or banners in the Settings app, in Notification Center settings.
A sample event handler for a background fetch:
- (void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
UIBackgroundFetchResult result = UIBackgroundFetchResultNoData;
// ... perform a network request
if (successfulNetworkRequest) {
UILocalNotification* localNotification = [UILocalNotification new];
localNotification.alertAction = #"View";
localNotification.alertBody = #"Stuff";
localNotification.soundName = UILocalNotificationDefaultSoundName;
localNotification.applicationIconBadgeNumber = 1;
localNotification.userInfo = #{#"stuff": #"other stuff"};
[application presentLocalNotificationNow:localNotification];
result = UIBackgroundFetchResultNewData;
} else {
result = UIBackgroundFetchResultFailed;
}
completionHandler(result);
}
I am afraid you are misunderstanding of local notification. I guess what you should do is registering a notification in the future and send it some time after user clicked the home button. But in your code, you registered a alert in CURRENT TIME and did some thing on the notification (sorry but I don't know what you want to do in your code).
You could change it like this to make a sense:
- (void)setLocalNotificationForType:(SPKLocalNotificationType)notificationType fromUser:(NSString *)userName withMessageText:(NSString *)msgText {
UILocalNotification *notif = [[UILocalNotification alloc] init];
notif.userInfo = #{#"handle": _convoHandle, #"room": _convoName};
//Set the fire time 10 seconds later
notif.fireDate = [[NSDate date] dateByAddingTimeInterval:10];
notif.applicationIconBadgeNumber = [[UIApplication sharedApplication] applicationIconBadgeNumber] + 1;
notif.soundName = UILocalNotificationDefaultSoundName;
notif.timeZone = [NSTimeZone defaultTimeZone];
if (notificationType == 1) { // message
notif.alertBody = [NSString stringWithFormat:#"#%# said: \"%#\"", userName, msgText];
} else if (notificationType == 2) { // image
notif.alertBody = [NSString stringWithFormat:#"#%# sent an image", userName];
} else {
return;
}
//Register this notification
[[UIApplication sharedApplication] scheduleLocalNotification:notification];
}
And in your app delegate's -applicationDidEnterBackground:, call your -setLocalNotificationForType:fromUser:withMessageText: method to make and register a notification. Then you can expect a local notification 10s after you click the home button.
What is the difference between presentLocalNotificationNow and scheduleLocalNotification.
For the both following function is showing notification after 1 second
-(void)showLocalNotification:(NSNotification *)notification {
NSString *msg = #"test message";
[[UIApplication sharedApplication] cancelAllLocalNotifications];
UILocalNotification *_localNotification = [[UILocalNotification alloc]init];
_localNotification.fireDate = [NSDate dateWithTimeIntervalSinceNow:1];
_localNotification.timeZone = [NSTimeZone defaultTimeZone];
_localNotification.alertBody = msg;
_localNotification.soundName = UILocalNotificationDefaultSoundName;
_localNotification.applicationIconBadgeNumber = [[UIApplication sharedApplication] applicationIconBadgeNumber]+1;
[[UIApplication sharedApplication] scheduleLocalNotification:_localNotification];
// or
//[[UIApplication sharedApplication] presentLocalNotificationNow:_localNotification];
}
If the application is running in the background, the local notification will not get an alert or sound, as it is directly received by the application. In that case, you need to present the notification using presentLocalNotificationNow.
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
{
UIApplicationState applicationState = application.applicationState;
if (applicationState == UIApplicationStateBackground) {
[application presentLocalNotificationNow:notification];
}
}
From Apple Documentation:
Once you have created an instance of UILocalNotification, you schedule
it using one of two methods of the UIApplication class:
scheduleLocalNotification: or presentLocalNotificationNow:. The former
method use the fire date to schedule delivery; the latter method
presents the notification immediately, regardless of the value of
fireDate. You can cancel specific or all local notifications by
calling cancelLocalNotification: or cancelAllLocalNotifications,
respectively.
There's no difference right here, but using scheduleLocalNotification you can schedule it at whatever time you want, not only in one second.
And, of corse, nobody promises you that presentLocalNotificationNow will show one in exactly one second, not in 0.5 or 2.0 in iOS 8, for example.