colleagues!
Cant find any additional information for catching correctly notifications if app closed.
In my case, when I receive notification, my code just running 1 VC, and can't do anything more. How I can place delay? I mean: first app launch, and after this my notification will be posted.
I can do it with delay and timer, but im trying to do everything clear and correctly.
So, I've got
- (void)application:(UIApplication *)application
didReceiveRemoteNotification:(NSDictionary *)userInfo
fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
__block AppDelegate * blockSelf = self;
NSString * jsonStrPush = userInfo[#"data"];
NSDictionary *fullDic = [[FrequentRepeateFunc sharedInstance] nsString_to_Dic:jsonStrPush];
NSInteger type = [fullDic[#"push_type"] integerValue];
NSLog(#"PUSH NUMBER %ld", type);
NSDictionary *dataPush = fullDic[#"data"];
NSDictionary *newDataNotification = dataPush [#"notification"];
if(nc != nil){
switch (type) {
They are the same with swift.
and I've got about 50 cases like
case 25: {
NSDictionary *dic = #{#"json": dataPush};
[[NSNotificationCenter defaultCenter] postNotificationName:#"pushFromMap" object:nil userInfo:dic];
}
break;
Next step I got Observer class with NC.Default.addobserver which called some methods like:
#objc func pushFromMap(_ notification: Notification) {
guard let dicData = notification.userInfo?["json"] as? [String: Any] else { return }
guard let name = dicData["user_name"] as? String else { return }
guard let gender = dicData["gender"] as? Int else { return }
let storyboard = UIStoryboard(name: "NewDesign", bundle: nil)
let navContr = UIApplication.shared.windows[0].rootViewController as! UINavigationController
let controller = storyboard.instantiateViewController(withIdentifier: "SameViewController") as! SameViewController
controller.prepareController(parent: navContr, eventId: "", forImage: gender == 1 ? .goodHe : .goodShe, text: name) {}
}
How I can create delay for waiting or conditions to check if app in active mode? THis code works perfect if app is active, or was active 1-2 min ago. But if I close app, this notifications are useless.
In my app, I am downloading data using the Facebook graph api and wish to present a notification when new data is available. I want to download data, compare it with what's already stored in NSUserDefaults and show a notification if it is different.
I allowed background fetch in the info.plist file and in my appDelegate, I have added the following code:
In didFinishLaunchingWithOptions():
UIApplication.sharedApplication().setMinimumBackgroundFetchInterval(30)
In performFetchWithCompletionHandler()
(this doesn't include the code to check if the fetched data is new, it's just a test)
let url = "https://graph.facebook.com/109315262061/posts?limit=20&fields=id,full_picture,picture,from,shares,attachments,message,object_id,link,created_time,comments.limit(0).summary(true),likes.limit(0).summary(true)&access_token=\(API_KEY)"
let task = NSURLSession.sharedSession().dataTaskWithURL(NSURL(string: url)!) { (data, response, error) in
if error == nil
{
dispatch_async(dispatch_get_main_queue(), {
do
{
let jsonData = try NSJSONSerialization.JSONObjectWithData(data!, options: .MutableContainers)
ids.removeAll()
if let items = jsonData["data"] as? [[String:AnyObject]]
{
for item in items
{
if let id = item["id"] as? String
{
ids.append(id)
}
}
if ids.count == 20
{
print(jsonData)
let notification = UILocalNotification()
notification.alertBody = "You have new notifications!"
notification.alertTitle = "NSITConnect"
notification.fireDate = NSDate(timeIntervalSinceNow: 1)
UIApplication.sharedApplication().scheduleLocalNotification(notification)
UIApplication.sharedApplication().presentLocalNotificationNow(notification)
completionHandler(UIBackgroundFetchResult.NewData)
}
}
}
catch
{
}
})
}
}
task.resume()
The downloading operation occurs successfully but I don't see a notification. How can I fix this? Also is there a way to download data when the app is force closed and then display the notification? My apologies if this is a silly question, I am fairly new to this concept!
Notifications aren't shown if the app is already open, try creating an alert to display your message, or create a custom controller/view.
You can check wether your app is currently active or not like this, and then either create the notification or the alert/custom controller.
application.applicationState == UIApplicationState.Active
I'm developing a chat app. I'm using apple push notification service to notify user when he receives new messages. There are two scenarios.
The first when user is chatting and receiving a message, the user shouldn't be notified (meaning that notification shouldn't be shown) and when the app is in background i want to alert user for the messages. Everything is ok except that when app is on background the notification shows the whole JSON object the client is receiving.
The idea is ignore visually notification and if its on background show a local Notification.
This is how i have implemented the notification settings
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject : AnyObject]?) -> Bool {
let types: UIUserNotificationType = [UIUserNotificationType.None]
let settings: UIUserNotificationSettings = UIUserNotificationSettings(forTypes: types, categories: nil)
application.registerUserNotificationSettings(settings)
application.registerForRemoteNotifications()
return true
}
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject])
{
//App handle notifications in background state
if application.applicationState == UIApplicationState.Background {
var login_user = LoginUser();
login_user.loadData();
var username:String!;
var message:String!;
if let msg = userInfo["aps"]as? Dictionary<String,AnyObject>
{
if let alert = msg["alert"] as? String{
if let data = alert.dataUsingEncoding(NSUTF8StringEncoding)
{
do
{
let jsonObject = try NSJSONSerialization.JSONObjectWithData(data,options: [])
username = jsonObject["senderUserName"] as! String;
message = jsonObject["content"] as! String!;
DatabaseOperations().insert(DatabaseOperations().STRING_VALUE_CHATING_USERNAME, value: username);
NSNotificationCenter.defaultCenter().postNotificationName("push_notification", object: self)
}
catch
{
}
}
}
}
let localNotification: UILocalNotification = UILocalNotification()
switch(login_user.privacyLevelId)
{
case 1:
localNotification.alertBody = username + ":" + message;
break;
case 2:
localNotification.alertBody = username;
break;
case 3:
localNotification.alertBody = "New Message";
break;
default:
localNotification.alertBody = "New Message";
break;
}
localNotification.alertAction = "Message"
localNotification.fireDate = NSDate(timeIntervalSinceNow: 5)
localNotification.soundName = UILocalNotificationDefaultSoundName
UIApplication.sharedApplication().scheduleLocalNotification(localNotification)
}
//App is shown and active
else
{
if let msg = userInfo["aps"]as? Dictionary<String,AnyObject>
{
if let alert = msg["alert"] as? String
{
if let data = alert.dataUsingEncoding(NSUTF8StringEncoding)
{
do
{
let jsonObject = try NSJSONSerialization.JSONObjectWithData(data,options: [])
let sender:String = jsonObject["senderUserName"] as! String;
DatabaseOperations().insert(DatabaseOperations().STRING_VALUE_CHATING_USERNAME, value: sender);
NSNotificationCenter.defaultCenter().postNotificationName("push_notification", object: self)
}
catch
{
}
}
}
}
}
}
I set UIUserNotificationType to NONE. Shouldn't by default the notification shows nothing?
I also have read some other posts, but i couldn't find anything to solve the problem.
Why does UIUserNotificationType.None return true in the current settings when user permission is given?
Hide, do not display remote notification from code (swift)
Any help would be appreciated.
application didReceiveRemoteNotification won't be called if the app is closed or in the background state, so you won't be able to create a local notification. So you need to pass the text you want to display in the aps dictionnary, associated with the alert key.
If you want to pass more information for the active state case, you should add them with a custom key to the push dictionnary.
For example :
{"aps": {
"badge": 1,
"alert": "Hello World!",
"sound": "sound.caf"},
"task_id": 1}
I am facing a strange problem with local notification in swift.
I am presenting local notification like this
let notification = UILocalNotification()
var body = "Hi Krishna";
if(region.identifier == "entry1") {
body += " Welcome";
} else {
body += " Bye! Bye!";
}
notification.alertBody = body
notification.soundName = "Default";
notification.userInfo = ["id": "id"];
notification.fireDate = NSDate(timeIntervalSinceNow: 1)
UIApplication.sharedApplication().scheduleLocalNotification(notification)
and how I am handling launch options in my appdelegate
if(launchOptions != nil) {
window?.rootViewController?.view.backgroundColor = UIColor.cyanColor();
if let notification = launchOptions![UIApplicationLaunchOptionsLocalNotificationKey] as? UILocalNotification {
window?.rootViewController?.view.backgroundColor = UIColor.blackColor();
if let userInfo = notification.userInfo {
window?.rootViewController?.view.backgroundColor = UIColor.blueColor();
if let id = userInfo["id"] as? String {
window?.rootViewController?.view.backgroundColor = UIColor.redColor();
}
}
}
}
for debugging purpose I am changing the background color of the view.
when I tap to the notification I get the cyan color that means below line is failing
launchOptions![UIApplicationLaunchOptionsLocalNotificationKey] as? UILocalNotification
because I set cyan color right above this line.
so I am not getting why this is not castable to UILocalNotification?
can somebody help me to get rid from this issue?+
one more thing actually if I am doing it normally its working but I am using geofencing and I am scheduling notification from
locationManager(manager: CLLocationManager, didExitRegion region: CLRegion)
In this case its not working.
You could implement application(_:didReceiveLocalNotification:) (which gives you the notification directly) in your AppDelegate and handle the notification there.
More: https://developer.apple.com/library/prerelease/ios/documentation/UIKit/Reference/UIApplicationDelegate_Protocol/index.html#//apple_ref/occ/intfm/UIApplicationDelegate/application:didReceiveLocalNotification:
Can you please try to cast like this:
if let notification:UILocalNotification = launchOptions?[UIApplicationLaunchOptionsLocalNotificationKey] as? UILocalNotification {
//do stuff with notification
}
I want to open a specific view controller when a user clicks on the received push notification message, but when I receive a push notification message and click the message, only the application opens, but it does not redirect to a specific view controller.
My code is
-(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
if (applicationIsActive) {
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Bildirim"
message:[NSString stringWithFormat:#"%# ",[[userInfo objectForKey:#"aps"] objectForKey:#"alert"]]
delegate:self cancelButtonTitle:#"Ok"
otherButtonTitles:nil];
[alertView show];
UIViewController *vc = self.window.rootViewController;
PushBildirimlerim *pvc = [vc.storyboard instantiateViewControllerWithIdentifier:#"PushBildirimlerim "];
[vc presentViewController:pvc animated:YES completion:nil];
}
}
My question is related with the iOS push notifications.
You may be having issues with the if (applicationIsActive) condition.
Put a breakpoint on -didReceiveRemoteNotification and see whether it executes in different scenarios and see if it goes within the if-condition.
(unrelated to a certain extent but worth checking) this question:
didReceiveRemoteNotification when in background
Note:
-didReceiveRemoteNotification will not execute if your app was (initially) closed and you clicked on the push notification to open the app.
This method executes when a push notification is received while the application is in the foreground or when the app transitions from background to foreground.
Apple Reference: https://developer.apple.com/documentation/uikit/uiapplicationdelegate
If the app is running and receives a remote notification, the app
calls this method to process the notification. Your implementation of
this method should use the notification to take an appropriate course
of action.
...
If the app is not running when a push notification arrives, the method
launches the app and provides the appropriate information in the
launch options dictionary. The app does not call this method to handle
that push notification. Instead, your implementation of the
application:willFinishLaunchingWithOptions: or
application:didFinishLaunchingWithOptions: method needs to get the
push notification payload data and respond appropriately.
So... When the app is not running and a push notification is received, when the user clicks on the push notification, the app is launched and now... the push notification contents will be available in the -didFinishLaunchingWithOptions: method in it's launchOptions parameter.
In other words... -didReceiveRemoteNotification won't execute this time and you'll also need to do this:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
//...
NSDictionary *userInfo = [launchOptions valueForKey:#"UIApplicationLaunchOptionsRemoteNotificationKey"];
NSDictionary *apsInfo = [userInfo objectForKey:#"aps"];
if(apsInfo) {
//there is some pending push notification, so do something
//in your case, show the desired viewController in this if block
}
//...
}
Also read Apple's Doc on Handling Local and Remote Notifications
There is an extra space in the identifier name. Remove it and try:
UIStoryboard *mainstoryboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
PushBildirimlerim* pvc = [mainstoryboard instantiateViewControllerWithIdentifier:#"PushBildirimlerim"];
[self.window.rootViewController presentViewController:pvc animated:YES completion:NULL];
In Swift 4
If you need to achieve the above case you have to handle 2 cases
When your app is in the background/Foreground state(if push
notification is not silenced)
When your app is in the inactive state
Here I am using category(built in parameter in the payload of push notification to identify the type of notification) if there are more than 1 type of notifications. In case you have only 1 type of notification then no need to check for the category.
So for handling the first case, the code is as follows in AppDelegate File
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
let userInfo = response.notification.request.content.userInfo
let title = response.notification.request.content.title
//Method- 1 -- By using NotificationCenter if you want to take action on push notification on particular View Controllers
switch response.notification.request.content.categoryIdentifier
{
case "Second":
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "SecondTypeNotification"), object: title, userInfo: userInfo)
break
case "Third":
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "ThirdTypeNotification"), object: title, userInfo: userInfo)
break
default:
break
}
///Method -2 --- Check the view controller at the top and then push to the required View Controller
if let currentVC = UIApplication.topViewController() {
//the type of currentVC is MyViewController inside the if statement, use it as you want to
if response.notification.request.content.categoryIdentifier == "Second"
{
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc: SecondViewController = storyboard.instantiateViewController(withIdentifier: "SecondViewController") as! SecondViewController
currentVC.navigationController?.pushViewController(vc, animated: true)
}
else if response.notification.request.content.categoryIdentifier == "Third"
{
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc: ThirdViewController = storyboard.instantiateViewController(withIdentifier: "ThirdViewController") as! ThirdViewController
currentVC.navigationController?.pushViewController(vc, animated: true)
}
}
completionHandler() }
For Method 1-
After which you have to add the observers in the default view controller as follows in viewDidLoad
NotificationCenter.default.addObserver(self,selector: #selector(SecondTypeNotification),
name: NSNotification.Name(rawValue: "SecondTypeNotification"),
object: nil)
NotificationCenter.default.addObserver(self,selector:#selector(ThirdTypeNotification),
name: NSNotification.Name(rawValue: "ThirdTypeNotification"),
object: nil)
For Method 1-
And also need two add the Notification observer function for adding actions to be executed with the same name used in Observer.
// Action to be taken if push notification is opened and observer is called while app is in background or active
#objc func SecondTypeNotification(notification: NSNotification){
DispatchQueue.main.async
{
//Land on SecondViewController
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc: SecondViewController = storyboard.instantiateViewController(withIdentifier: "SecondViewController") as! SecondViewController
self.navigationController?.pushViewController(vc, animated: true)
}
}
#objc func ThirdTypeNotification(notification: NSNotification){
DispatchQueue.main.async
{
//Land on SecondViewController
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc: ThirdViewController = storyboard.instantiateViewController(withIdentifier: "ThirdViewController") as! ThirdViewController
self.navigationController?.pushViewController(vc, animated: true)
}
}
So whenever a notification is opened when the app is in the foreground or background the above will execute and move to respective view controller according to the category in the payload.
Now the second case
We know that when the app is inactive the first function that will be called when the push notification is opened is
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
return true
}
So we have to check in this function whether the app is launched by opening push notification or by clicking the app icon. For this, there is a provision provided to us. The function will look as follows after adding the required code.
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
FirebaseApp.configure()
if #available(iOS 10.0, *) {
// For iOS 10 display notification (sent via APNS)
UNUserNotificationCenter.current().delegate = self
let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound]
UNUserNotificationCenter.current().requestAuthorization(
options: authOptions,
completionHandler: {_, _ in })
} else {
let settings: UIUserNotificationSettings =
UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil)
application.registerUserNotificationSettings(settings)
}
// Register the notification categories.
application.registerForRemoteNotifications()
Messaging.messaging().delegate = self
/// Check if the app is launched by opening push notification
if launchOptions?[UIApplication.LaunchOptionsKey.remoteNotification] != nil {
// Do your task here
let dic = launchOptions?[UIApplication.LaunchOptionsKey.remoteNotification] as? NSDictionary
let dic2 = dic?.value(forKey: "aps") as? NSDictionary
let alert = dic2?.value(forKey: "alert") as? NSDictionary
let category = dic2?.value(forKey: "category") as? String
// We can add one more key name 'click_action' in payload while sending push notification and check category for indentifying the push notification type. 'category' is one of the seven built in key of payload for identifying type of notification and take actions accordingly
// Method - 1
if category == "Second"
{
/// Set the flag true for is app open from Notification and on root view controller check the flag condition to take action accordingly
AppConstants.sharedInstance.userDefaults.set(true, forKey: AppConstants.sharedInstance.kisFromNotificationSecond)
}
else if category == "Third"
{
AppConstants.sharedInstance.userDefaults.set(true, forKey: AppConstants.sharedInstance.kisFromNotificationThird)
}
// Method 2: Check top view controller and push to required view controller
if let currentVC = UIApplication.topViewController() {
if category == "Second"
{
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc: SecondViewController = storyboard.instantiateViewController(withIdentifier: "SecondViewController") as! SecondViewController
currentVC.navigationController?.pushViewController(vc, animated: true)
}
else if category == "Third"
{
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc: ThirdViewController = storyboard.instantiateViewController(withIdentifier: "ThirdViewController") as! ThirdViewController
currentVC.navigationController?.pushViewController(vc, animated: true)
}
}
}
return true
}
For Method 1-
After this, check these flags value in the default view controller in viewdidLoad as follows
if AppConstants.sharedInstance.userDefaults.bool(forKey: AppConstants.sharedInstance.kisFromNotificationSecond) == true
{
//Land on SecondViewController
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc: SecondViewController = storyboard.instantiateViewController(withIdentifier: "SecondViewController") as! SecondViewController
self.navigationController?.pushViewController(vc, animated: true)
AppConstants.sharedInstance.userDefaults.set(false, forKey: AppConstants.sharedInstance.kisFromNotificationSecond)
}
if AppConstants.sharedInstance.userDefaults.bool(forKey: AppConstants.sharedInstance.kisFromNotificationThird) == true
{
//Land on SecondViewController
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc: ThirdViewController = storyboard.instantiateViewController(withIdentifier: "ThirdViewController") as! ThirdViewController
self.navigationController?.pushViewController(vc, animated: true)
AppConstants.sharedInstance.userDefaults.set(false, forKey: AppConstants.sharedInstance.kisFromNotificationThird)
}
This will achieve the goal to open a particular view controller when the push notification is opened.
You can go through this blog- How to open a particular View Controller when the user taps on the push notification received? for reference.
I was having same problem that when app is suspended/terminated and push notification arrives my app was only opening and not redirecting to specific screen corresponding to that notification the solution is,
in
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions this method the parameter launchOptions tells us if it has the notification by checking that we need to call the method to redirect to specific screen
the code is as below...
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
//your common or any code will be here at last add the below code..
NSMutableDictionary *notification = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
if (notification)
{
//this notification dictionary is same as your JSON payload whatever you gets from Push notification you can consider it as a userInfo dic in last parameter of method -(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
NSLog(#"%#",notification);
[self showOfferNotification:notification];
}
return YES;
}
then in the method showOfferNotification:notification you can redirect user to corresponding screen like...
//** added code for notification
-(void)showOfferNotification:(NSMutableDictionary *)offerNotificationDic{
//This whole is my coding stuff.. your code will come here..
NSDictionary *segueDictionary = [offerNotificationDic valueForKey:#"aps"];
NSString *segueMsg=[[NSString alloc]initWithFormat:#"%#",[segueDictionary valueForKey:#"alert"]];
NSString *segueID=[[NSString alloc]initWithFormat:#"%#",[offerNotificationDic valueForKey:#"id"]];
NSString *segueDate=[[NSString alloc]initWithFormat:#"%#",[offerNotificationDic valueForKey:#"date"]];
NSString *segueTime=[[NSString alloc]initWithFormat:#"%#",[offerNotificationDic valueForKey:#"time"]];
NSLog(#"Show Offer Notification method : segueMsg %# segueDate %# segueTime %# segueID %#",segueMsg,segueDate,segueTime,segueID);
if ([segueID isEqualToString:#"13"]){
NSString *advertisingUrl=[[NSString alloc]initWithFormat:#"%#",[offerNotificationDic valueForKey:#"advertisingUrl"]];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:segueMsg forKey:#"notificationMsg"];
[defaults setObject:segueDate forKey:#"notifcationdate"];
[defaults setObject:segueTime forKey:#"notifcationtime"];
[defaults setObject:advertisingUrl forKey:#"advertisingUrl"];
[defaults synchronize];
navigationController = (UINavigationController *)self.window.rootViewController;
UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:#"Main_iPhone" bundle: nil];
FLHGAddNotificationViewController *controller = (FLHGAddNotificationViewController*)[mainStoryboard instantiateViewControllerWithIdentifier: #"offerViewController"];
[navigationController pushViewController:controller animated:YES];
}
}
when tap on notification
call notification delegate function
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
let nav = UINavigationController()
nav.navigationBar.isHidden = true
let first = Router.shared.splashVC()
let sceond = Router.shared.CustomTabbarVC()
let third = Router.shared.ProviderDetailsVC()
sceond.selectedIndex = 2
nav.viewControllers = [first,sceond,third]
UIApplication.shared.keyWindow?.rootViewController = nav
UIApplication.shared.keyWindow?.makeKeyAndVisible()
}