99

I have got push notification working and managed to update icon badge count when app is brought to foreground.

I am a bit confused about this though... the iPhone receives the notification and the pop up message appears to activate my app, and the badge only updates after me starting the app.

This does not sound right in terms of user experience. My understanding is that the badge count should notify the user of what needs action, through incremented count, but this does not happen until a later stage when the app is live.

So is there a way to tell the app to update its badge count when it receives push notifications and whilst being in the background?

Note that my app does not use location and that I have UIRemoteNotificationTypeBadge in the notification registration request.

Pang
  • 9,564
  • 146
  • 81
  • 122
Abolfoooud
  • 2,663
  • 2
  • 27
  • 27

12 Answers12

97

Since push notification are handled by iOS and not your app you can't change the application badge on receiving a push notification.

But you can send the badge number in the payload of the push notification, but the you will have to do the calculation server side.

You should read Local and Push Notification Programming Guide and especially the The Notification Payload.

The payload could look like this:

{
    "aps" : {
        "alert" : "You got your emails.",
        "badge" : 9
    }
}

Now the app application badge icon will show 9.

limon
  • 3,222
  • 5
  • 35
  • 52
rckoenes
  • 69,092
  • 8
  • 134
  • 166
  • 4
    I am already doing that...but this value is retrieved only when my app is brought to the foreground, which i later use to update the badge value using [UIApplication sharedApplication].applicationIconBadgeNumber = count; – Abolfoooud Jan 10 '13 at 11:27
  • 7
    No you need to set the badge number in the push notification payload. – rckoenes Jan 10 '13 at 11:28
  • 13
    I had another look at my payload structure and it seems I was passing the value of the badge parameter as a string '4' rather than an int 4!!! Now i got it working...thanks for the help – Abolfoooud Jan 10 '13 at 11:47
  • I tested it doesn't work when change on payload structure badge – wod Feb 13 '13 at 11:40
  • 1
    @wod what do you mean with: change on payload structure badge. – rckoenes May 15 '13 at 13:52
  • @rckoenes Is it correct to handle push notification count in server side ? Ideally it is the count of unread notifications right ? As seen in gmail and other apps it shows the count of unread messages. How to handle this in front end ? – subin272 Jul 02 '18 at 02:39
  • Since the push notification is send from the server it is better to do it server side. – rckoenes Jul 02 '18 at 11:23
  • 2
    Actually in iOS 10 this is fully supported from app side. See my answer down below. https://stackoverflow.com/a/53159748/2223756 – Gkiokan Nov 05 '18 at 18:06
28

Actually in iOS 10 a remote Notification will call automatically didReceiveRemoteNotification Method in your AppDelegate.

You have 2 ways of updating the badge count in the background.
I've done this for my current app also. You don't need a Notification Service Extension either.

1st Way:

Send APS badge key with your payload to APN.
This will update the badge count according to your Integer value in your payload of badge. e.x.:

// Payload for remote Notification to APN
{
    "aps": {
        "content-available": 1,
        "alert": "Hallo, this is a Test.",
        "badge": 2, // This is your Int which will appear as badge number,
        "sound": default
    }
}

2nd Way:

You can switch your application.applicationState and update your badges Count when the applicationState is in .background. BUT you have to take care not to set the badge key parameter in your Notification payload when sending to APN e.x.

// Payload to APN as silent push notification
{
    "aps": {
        "content-available": 1
    }
}

Handle the badge Update accordingly to the application state:

Here is my working code for badge count update without badge key in the payload for APN.

func application(_ application: UIApplication, didReceiveRemoteNotification 
   userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
    print("APN recieved")
    // print(userInfo)
    
    let state = application.applicationState
    switch state {
        
    case .inactive:
        print("Inactive")
        
    case .background:
        print("Background")
        // update badge count here
        application.applicationIconBadgeNumber = application.applicationIconBadgeNumber + 1
        
    case .active:
        print("Active")

    }
}

Reset badge count:

Don't forget to reset your badge count when your app gets back to active state.

func applicationDidBecomeActive(_ application: UIApplication) {
    // reset badge count
    application.applicationIconBadgeNumber = 0
}
Community
  • 1
  • 1
Gkiokan
  • 3,492
  • 2
  • 22
  • 24
  • How do you update badge if the app is not running in background? – Nagendra Rao Jan 08 '19 at 08:26
  • 3
    If you send the APS with content-available key it will run the didReceiveRemoteNotification Method regardless of the state of the app. Just try it out with a native Device. If you are on iOS you can use this tool to send your JSON Object to APN: https://github.com/noodlewerk/NWPusher – Gkiokan Jan 08 '19 at 18:53
  • 1
    wow, great, and what if i am sending a notification without content-available? (which is about any non-background notification). `didReceiveRemoteNotification` is not called – StackExploded Mar 24 '20 at 15:23
  • In second way I can still use sound and alert? – submariner Sep 24 '22 at 08:32
  • @submariner It's been a while but as I remember no. You have to do that on your state case yourself, in this case `.background`. This was the case for badge. It may work out if you send within the aps object. If not, you just define it yourself as stated. – Gkiokan Sep 28 '22 at 20:36
12

We can alter the badge number when we are in the background state by sending the "badge" parameter in the push notification package. As @rckoenes pointed out the JSON parameter for the badge must be an INTEGER.

Sample PHP code for doing the same

// Create the payload body
$body['aps'] = array(
        'alert' => $message,
        'badge' => 1,
        'sound' => 'default'
        );

badge => 1 where 1 is an integer not a string (i.e. without apostrophes)

B.Adler
  • 1,499
  • 1
  • 18
  • 26
prajul
  • 1,216
  • 2
  • 15
  • 27
8
    **This is the APNS payload get back from server.**

    {
        "aps" : {
            "alert" : "You got your emails.",
            "badge" : 9,
            "sound" : "bingbong.aiff"
        },
        "acme1" : "bar",
        "acme2" : 42
    }

The value for key badge is automatically considered as badge count.On ios app side no need to calculate or handle the count. In above example 9 is the badge count.So your app icon will show 9.

NOTE While your app is close u can't handle badges on your own.Thats why we are using badge key from APNS Payload For better clarification about notification see documentation

if you want to reduce the badge count on your own.Decrement the count and update it yourself.as follows

slawekwin
  • 6,270
  • 1
  • 44
  • 57
Prabhat Pandey
  • 277
  • 10
  • 13
7

If you are using NotificationServiceExtension the you can update badge in that.

var bestAttemptContent : UNMutableNotificationContent? // 
bestAttemptContent.badge = 0//any no you wanna display

Every time your application receive notification your service extension will be called.Save that value in user default and display it. To share user defaults between application and extension you need to enable app group in application. Read more here

Surjeet Rajput
  • 1,251
  • 17
  • 24
5

For Firebase Cloud Messaging (FCM) it should be like this:

{
  "to": "some_token",

  "notification": {
    "body": "this is a body",
    "title": "this is a title",
    "badge" : 1
  }, 

  "priority": "high", 
}
3

Since iOS 10 you can develop a Notification Service extension for your app. It will be started by the system when you receive a notification and you can calculate a valid number for the badge and set it.

Take a look at the documentation: https://developer.apple.com/documentation/usernotifications/unnotificationserviceextension

1

in apns payload have to define "content-available":1 for update badge count in background mode

func application(_ application: UIApplication, didReceiveRemoteNotification 
   userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {


// increase badge count, but no need if you include content-available

application.applicationIconBadgeNumber = application.applicationIconBadgeNumber + 1

}

func applicationDidBecomeActive(_ application: UIApplication) {

// reset badge count

application.applicationIconBadgeNumber = 0

}

for eg.

"aps":{
    "alert":"Test",
    "sound":"default",
    "content-available":1

}
1

in legacy its work, "badge" set count

{
  "to": "token",
  "notification": {
    "title": "Example",
    "body": "Tiene 22 actualizaciones.",
    "badge":278
  },
  "data": {
    "story_id": "story_12345",
    "count_vaca":22
  }
}
0
-(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {

    application.applicationIconBadgeNumber = 0;
    NSLog(@"userInfo %@",userInfo);

    for (id key in userInfo) {
        NSLog(@"key: %@, value: %@", key, [userInfo objectForKey:key]);
    }

    [application setApplicationIconBadgeNumber:[[[userInfo objectForKey:@"aps"] objectForKey:@"badge"] intValue]];
    NSLog(@"Badge %d",[[[userInfo objectForKey:@"aps"] objectForKey:@"badge"] intValue]);
}
suvi
  • 92
  • 3
  • Why are you using intValue instead of integerValue which is an NSInteger? `@property (readonly) NSInteger integerValue NS_AVAILABLE(10_5, 2_0);` – Alex Zavatone Sep 03 '15 at 19:45
0

As @rckoenes said you will have to do the calculation server side, but how could you know when to increment the badge number value you should send in payload?.

Will when you launch the application send a message to your server indicating that the application is have been launched. So, on the server side you start again from badge=0, and while there is no messages received by server increase the badge number with every push notification payload.

mustafa
  • 451
  • 6
  • 15
-2

After receiving remote Notification when you open App,

get current Badge number in "didBecomeActive" Method of your AppDelegate.

File using below code:

int badgeCount = [UIApplication sharedApplication].applicationIconBadgeNumber;
    badgeCount = badgeCount + 1;
Janmenjaya
  • 4,149
  • 1
  • 23
  • 43
Ved Gupta
  • 86
  • 3
  • 3
    That won't work until the user taps on the app or the notification to launch the app. It will not do what the OP wants, to update the badge when the app is in the background and *before* the user has interacted with the app or the notification. – Alex Zavatone Sep 03 '15 at 19:55