I have an Xcode project in Swift with the following targets:
iOS App
WatchKit Extension / WatchKit App
"Common" Project, used by the "main" project and by the extension
In the common project I have the following code:
public class func scheduleNotification(seconds: Int) {
var notification = UILocalNotification()
notification.fireDate = NSDate().dateByAddingTimeInterval(NSTimeInterval(seconds))
notification.alertBody = "item"
notification.alertAction = "open"
notification.soundName = UILocalNotificationDefaultSoundName
notification.userInfo = ["UUID": "XX", ]
notification.category = "CATEGORY"
UIApplication.sharedApplication().scheduleLocalNotification(notification)
}
I'm able to call this method AND fire a notification from the iOS APP:
#IBAction func XXX(sender: AnyObject) {
NotificationHelper.scheduleNotification(100)
}
But the same code executed from the WatchKit Extension doesn't fire any notification. The method IS called but then nothing happens on the iPhone or on the Apple Watch, no notifications are fired.
Can someone help me?
How can I schedule notification from the WatchKit Extension logic?
App extensions are not allowed to access sharedApplication. I'm guessing that sharedApplication is returning nil in your case, which would explain why the notification is not scheduled.
I'd suggest using something like openParentApplication:reply: to open your host iOS app in the background and schedule the notification from there.
I suppose you do NOT yet have the real device, but are using the simulator? Then you will probably not be able to send notifications to the Watch at all. This is because Apple has not (yet) added this feature to XCode.
To view and test your notification interface on the watch simulator, use a payload file as described here (paragraph "Specifying a Notification Payload for Testing"):
https://developer.apple.com/library/ios/documentation/General/Conceptual/WatchKitProgrammingGuide/ConfiguringYourXcodeProject.html
And do not have much confidence in a change of that coming soon/anymore. As it is described somewhere in the Watch Documentation, iOS will decide itself where to send your notification, anyway (when you finally made it to the hardware release...)
First of all run iOS app from WatchKit app:
NSDictionary *userInfo = #{#"text":#"Hello world!",
#"delay":#10.0};
[WKInterfaceController openParentApplication:userInfo reply:^(NSDictionary *replyInfo, NSError *error) {
// Completion
}];
Then handle on iOS device:
- (void)application:(UIApplication *)application handleWatchKitExtensionRequest:(NSDictionary *)userInfo reply:(void (^)(NSDictionary *))reply
{
UIBackgroundTaskIdentifier tid = [application beginBackgroundTaskWithExpirationHandler:nil];
UILocalNotification *note = [[UILocalNotification alloc] init];
note.alertBody = userInfo[#"text"];
note.fireDate = [NSDate dateWithTimeIntervalSinceNow:[userInfo[#"delay"] doubleValue]];
note.timeZone = [NSTimeZone systemTimeZone];
note.userInfo = userInfo;
[application scheduleLocalNotification:note];
reply(nil); // Completion callback
[application endBackgroundTask:tid];
}
And of course do not forget to register iOS app for notifications:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[application registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeAlert) categories:nil]];
return YES;
}
Related
I'm using firebase to implement push notifications in iOS, with objective c.
I have the method application:didReceiveRemoteNotification:fetchCompletionHandler, which should be triggered when the app is in background and the user taps the notification and also when the app is in foreground, according to its description. Thing is it only works in background (or when the app is no running).
Am I forgetting something?
Thanks for the help.
You can use this below code:
-(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
UIApplicationState state = [application applicationState];
if (state == UIApplicationStateActive)
{
//app is in foreground
//the push is in your control
UILocalNotification *localNotification =
[[UILocalNotification alloc] init];
localNotification.userInfo = userInfo;
localNotification.soundName =
UILocalNotificationDefaultSoundName;
localNotification.alertBody = message;
localNotification.fireDate = [NSDate date];
[[UIApplication sharedApplication]
scheduleLocalNotification:localNotification];
}
else
{
//app is in background:
//iOS is responsible for displaying push alerts, banner etc..
}
}
For iOS 10 and above below method is called when application is in foreground:
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
willPresentNotification:(UNNotification *)notification
withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler
So you need to observe this method to handle notification while app is in foreground
The standart example for iOS Firebase Notifications is implemented in AppDelegate like completionHandler(UIBackgroundFetchResultNewData);
If you like in foreground you have to implemt it.
I am making an app in which user select time and video. When notification fire than I want that the select video should be played . How can i achieve that?
Here is my notification code.
-(void) scheduleLocalNotificationWithDate:(NSDate *)fireDate
{
UILocalNotification *localNotif = [[UILocalNotification alloc] init];
localNotif.fireDate = fireDate;
localNotif.timeZone = [NSTimeZone localTimeZone];
localNotif.alertBody = #"Time to wake Up";
localNotif.alertAction = #"Show me";
localNotif.soundName = #"Tick-tock-sound.mp3";
localNotif.applicationIconBadgeNumber = 1;
localNotif.repeatInterval = NSCalendarUnitDay;
NSLog(#" date %lu",kCFCalendarUnitDay);
[[UIApplication sharedApplication] scheduleLocalNotification:localNotif];
}
Any Syggestion?
Your code for scheduling a local notification looks good. Probably you should also add some data in the userInfo property of the local notification, so that when it fires, you can check that property and do something different (play a specific video) according to the data inside the userInfo.
Example:
NSDictionary *infoDict = [NSDictionary dictionaryWithObject:#"video1" forKey:#"videoName"];
localNotif.userInfo = infoDict;
Make sure that you also request user permission for using local notifications, otherwise the local notification will not fire.
Example:
UIUserNotificationType types = UIUserNotificationTypeBadge | UIUserNotificationTypeSound | UIUserNotificationTypeAlert;
UIUserNotificationSettings *mySettings = [UIUserNotificationSettings settingsForTypes:types categories:nil];
[[UIApplication sharedApplication] registerUserNotificationSettings:mySettings];
Now you need to handle the firing of the local notification when your app is in the 3 states: foreground, background/suspended and not running.
The app is running in the foreground. The local notification fires at the date you set it to. The following delegate method will get called by the system in AppDelegate:
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification {
NSString *videoName = [notification.userInfo objectForKey:#"videoName"];
//do something, probably play your specific video
}
The app is running in the background or is suspended. The local notification is shown to the user (it fired at the date you set it to) and the user taps on it. The same delegate method as above (didReceiveLocalNotification) will get called by the system in AppDelegate:
The app is not running, the local notification is shown to the user (it fired at the date you set it to) and the user taps on it. The following delegate method will get called by the system in AppDelegate:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
UILocalNotification *localNotif = [launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey];
if (localNotif)
{
//the app was launched by tapping on a local notification
NSString *videoName = [localNotif.userInfo objectForKey:#"videoName"];
// play your specific video
} else {
// the app wasn't launched by tapping on a local notification
// do your regular stuff here
}
}
I would recommend reading Apple's documentation regarding working with local notifications.
You can use the Media Player framework as recommended in Glorfindel's answer and you can find an example for playing a video in this StackOverflow answer.
Once the user opens your local notification, your app will be launched, and the - application:didFinishLaunchingWithOptions: of your UIApplicationDelegate will be called. The options dictionary will contain a UIApplicationLaunchOptionsLocalNotificationKey key, which contains the UILocalNotification. That should give you enough information to determine which video needs to be played, which you can do with the Media Player framework.
I have this weird situation, my app support iOS7 and above. what it does, it's enabled Remote notifications by using silent notifications.
I know that iOS7 and iOS8 above has different logic to handle notification. I did this :
if ([[UIApplication sharedApplication] respondsToSelector:#selector(isRegisteredForRemoteNotifications)])
{
[[UIApplication sharedApplication] registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeSound | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge) categories:nil]];
[[UIApplication sharedApplication] registerForRemoteNotifications];
}
else {
[[UIApplication sharedApplication] registerForRemoteNotificationTypes:
(UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound | UIRemoteNotificationTypeAlert)];
}
here's notification receives
{
aps = {
"content-available" = 1;
};
}
So what it does is, app receive silent notification, and then set localNotification, see below :
UILocalNotification *notification = [[UILocalNotification alloc] init];
notification.fireDate = [NSDate dateWithTimeIntervalSinceNow:5];
notification.soundName = UILocalNotificationDefaultSoundName;
notification.alertBody = #"testing";
notification.timeZone = [NSTimeZone defaultTimeZone];
[[UIApplication sharedApplication] scheduleLocalNotification:notification];
All it works in iOS8 and iOS9 in background mode and foreground mode. When app is in foreground, it will trigger didReceiveLocalNotification.
But when I was testing in iOS7, it doesn't work if the app is in background mode. I'm trying to figure out how this happen, while other OS working fine. I did test while app is open, it did receive push notification, and didReceiveLocalNotification get triggered. but when goes to background, nothing happen (no local push notification).
You didn't mention about enabling Background Fetch. Have you enabled it in your App Capabilities?
and set this in your AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[application setMinimumBackgroundFetchInterval:UIApplicationBackgroundFetchIntervalMinimum];
return YES;
}
and this as your delegate method
-(void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler{
// do your thing here. maybe calling a view controller class or stuff
}
But if you're not into background fetch, try this instead:
{
aps = {
"content-available" : 1,
"sound" : ""
};
}
Good luck!
Im not exactly sure about the issue, but maybe you can try to change respondsToSelector. Refer to the following:
if ([UIApplication instancesRespondToSelector:#selector(registerUserNotificationSettings:)])
As pointed in this tread, aps need to include priority in order to work in iOS7.
aps = {
badge = 7;
"content-available" = 1;
priority = 5;
};
check this : https://stackoverflow.com/a/23917593/554740
I send a LocalNotification like this:
UILocalNotification *notification = [UILocalNotification new];
notification.alertBody = #"Hello, open me";
notification.userInfo = #{#"TheKey": #"www.stackoverflow.com"};
notification.soundName = UILocalNotificationDefaultSoundName;
[[UIApplication sharedApplication] presentLocalNotificationNow:notification];
and in AppDelegate I do use:
-(void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
{
NSDictionary *userInfo = notification.userInfo;
NSURL *siteURL = [NSURL URLWithString:[userInfo objectForKey:#"TheKey"]];
[[UIApplication sharedApplication] openURL:siteURL];
}
It opens the URL in new broswer window but it takes like 5-10 seconds for the app to take that action. Is it possible to open browser immediatly when LocalNotification is opened?
On iOS 7 and prior your approach would indeed be the only one. This is because UILocalNotification is only used to open your app, i.e. call the AppDelegate's method application:didReceiveLocalNotification:. So, the first place indeed for you to perform any actions would be that method.
However, with iOS 8 interactive notifications have been introduced. These allow the user to perform a specified action without opening the app.
In the screenshot you see that the user just received a notification and now has the option to perform certain actions on it, without actually going back into the app. You can find a great tutorial on how to implement interactive notifications here!
Hello everyone,
I am trying how to implement pushnotification.For this i have read apple official document for push notification and also read raywenderlich blog and i understand the flow of pushnotication very well. I have created development and production certificate,profile and its working fine and push was successfully sent and receiving in -
-(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo{
*** display message in uialertview******
}
but my problem is how can i display push in my device like other push notification on top up side for both when my application is foreground and background too.
currently i am trying for IOS 7.0 and XCode Version 5.1.1 (5B1008)
Thanks In advance.
First of all check via these methods in App Delegate that if your registered successfully to APNS.
-(void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
}
-(void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
{
}
then in
-(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
{
NSDictionary *Notification = userInfo;
NSString *title = [(NSDictionary*)[(NSDictionary*)[Notification valueForKey:#"aps"] valueForKey:#"alert"] valueForKey:#"title"];
NSString *body = [(NSDictionary*)[(NSDictionary*)[Notification valueForKey:#"aps"] valueForKey:#"alert"] valueForKey:#"body"];
if ([[UIApplication sharedApplication] applicationState] == UIApplicationStateActive)
{
}
else if ([[UIApplication sharedApplication] applicationState] == UIApplicationStateInactive || [[UIApplication sharedApplication] applicationState] == UIApplicationStateBackground)
{
UILocalNotification *localNotification = [[UILocalNotification alloc] init];
localNotification.userInfo = userInfo;
localNotification.soundName = UILocalNotificationDefaultSoundName;
localNotification.alertBody = body;
localNotification.fireDate = [NSDate date];
[[UIApplication sharedApplication] scheduleLocalNotification:localNotification];
}
}
If your application is in active state show UIAlertView. if its not you need to show a UILocalNotification.
When you are in Background mode then push notification will display as per your application notification settings from notification centre. you dont have to display ios will do that.
when you are in Foreground mode then notification will receive and -(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo method will be called. so using this method you get userinfo. just NSLog userinfo dictionary and then you can create customview with label at top and animate it like ios default banner.
Hope this will help you.
As per Apple's note push/local notification will be display only when app is background mode. If notification is arrive at the time of app is on foreground/active then you need to manually manage it because iOS won't show a notification banner/alert That's default design.
I just put my logic here for manage notification when app in foreground mode:
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
{
// check the application state for app is active or not.
if (application.applicationState == UIApplicationStateActive)
{
// Nothing to do if applicationState is Inactive, the iOS already displayed an alert view.
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Receive a Remote Notification" message:[NSString stringWithFormat:#"Your App name received this notification while it was running:\n%#",[[userInfo objectForKey:#"aps"] objectForKey:#"alert"]]delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alertView show];
}
}
If you want to receive notification in top of the device like banner style then follow these steps.
First, you need to launch the 'Settings' app on your iOS device.
Once you're in, choose 'Notifications' from the list of options.
Here's a list of every app that supports push notifications. The ones at the top have been granted permission by you
You can also set the alert style. You can choose the banners that conveniently appear at the top of the screen, or full-on pop-ups that force you to take action before they go away. Or you can just choose 'None'.
For more detail check this link.
Hope you will get something from my answer.