didReceiveRemoteNotification not called in iOS 13.3 when app is in background
Asked Answered
B

9

21

I am banging my head. I am implementing push notification. Everything is working fine (push is received, badge is updated) but under iOS 13.3 the method application(_:didReceiveRemoteNotification:fetchCompletionHandler:) is not called when the app is in the background. If the app is in the foreground or using an iOS 12 device the method is called. I register for push notification in the following way:

[[UNUserNotificationCenter currentNotificationCenter] requestAuthorizationWithOptions:(UNAuthorizationOptionBadge | UNAuthorizationOptionSound | UNAuthorizationOptionAlert) completionHandler:^(BOOL granted, NSError * _Nullable error) {
    if (granted) {
        dispatch_async(dispatch_get_main_queue(), ^{
            [[UIApplication sharedApplication] registerForRemoteNotifications];
        });
    }
}];

The payload is set to the following

{"aps": {
    "badge": 10,
    "alert": "test",
    "content-available": 1
}}

I tried adding "Remote notifications" and "Background processing" as app capabilities in all variations (only "Remote notifications"/"Background processing", without any of those capabilities, enabling both) without any change. I set the delegate for the UNUserNotificationCenter but again without success. I set the headers accordingly:

curl -v \
 -H 'apns-priority: 4' \
 -H 'apns-topic: xx.xxxxx.xxxx' \
 -H 'apns-push-type: alert' \
 -H 'Content-Type: application/json; charset=utf-8' \
 -d '{"aps": {"badge": 10,"alert": "test", "content-available":1}}' \
 --http2 \
 --cert pushcert.pem \
 https://api.sandbox.push.apple.com/3/device/1234567890

From the docs it states that this method is called even when the app is in background:

Use this method to process incoming remote notifications for your app. Unlike the application:didReceiveRemoteNotification: method, which is called only when your app is running in the foreground, the system calls this method when your app is running in the foreground or background.

What am I missing here for iOS 13?

Bronchi answered 23/1, 2020 at 10:57 Comment(11)
Check if you are using this method: developer.apple.com/documentation/uikit/uiapplicationdelegate/…Pomegranate
Please see above: Everything is working fine (push is received, badge is updated) but under iOS 13.3 the method application(_:didReceiveRemoteNotification:fetchCompletionHandler:) is not called when the app is in the background.Bronchi
application(_:didReceiveRemoteNotification:fetchCompletionHandler:) will be called when you tap on the notification bannerPomegranate
Yes, this is correct. My question is: Why is it not called when the app is in background. From my understanding, the docs say so.Bronchi
Have you tried by implementing application(_:didReceiveRemoteNotification:withCompletionHandler:) method?Solvent
@HardikS: yes. But beside beeing deprecated, the method is not called on an iOS device running 13.3 when in background.Bronchi
when your application is in background and notification will come, at that time I think there is no one method called. You must tap on the notification to called this method.Solvent
@Bronchi Have you found solution for this? I am also facing the same :(Refugia
Yes, you have to implement Apple's preferred way which is a notification extension. After that, you have to adapt the payload to include "mutable-content". Very easy. It would have saved me hours of testing and researching if it was mentioned correctly in the documentation.Bronchi
this is how I receive the notificationpayload while in the background: https://mcmap.net/q/130514/-didreceiveremotenotification-not-working-in-the-backgroundAeromancy
@Bronchi can you explain what is Apple's preferred way? I am stuck on same problem that you faced.Soledadsolely
L
14

Have you set

"content-available": 1

in you backend APS payload?

Also need to make sure you have enabled background mode in your iOS App's info.plist file

<key>UIBackgroundModes</key>
<array>
    <string>processing</string>
    <string>remote-notification</string>
</array>
Larios answered 5/2, 2020 at 5:39 Comment(4)
@matt the post was accidentally submitted before I type in all the answers. Now it is completed with app side change.Larios
Yes, I did. You can see from my CURL request in my initial post. I also added "content-available": 1 and tried all background modes (enabled, disabled, variations) but still application(_:didReceiveRemoteNotification:fetchCompletionHandler:) is not getting called. Only if the app is active.Bronchi
<string>processing</string> => this is importnant lineMelliemelliferous
@ZhangZhan: Did you ever solve this problem? I am running into the same problem where silent/background notifications are not received even when content-available key is set correctly. On iOS 12, everything works properly but on iOS13, only the audible/visual notifications are working. I am aware that apple requires a new header type to be included in notification but that is already handled (we are using SNS). Interestingly, this isn't working in the simulator with the apns file with latest XCode version.Tavy
B
4

I spend a support ticket to get an answer to this problem.

It turns out that the documentation is not 100% "valid" for iOS 13 on this topic. The device decides whether to wake up or not. Although the documentation states a little different.

Apple's preferred way of implementation as a notification extension. After that, you have to adapt the payload to include "mutable-content".

I asked the support afterward if I should file a radar and they replied with "yes".

Bronchi answered 22/3, 2020 at 14:2 Comment(2)
if you provide a notification extension, is the app woken up?Speak
The extension of course runs as a separate process with a separate bundle identifier. So if you're trying to update some data in the app then you need to use some app group data sharing approach.Lessor
G
4

This delegate method : -

-(void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void(^)())completionHandler{

is getting invoked when we click on notification for iOS 13 and app is in background.

Gregarious answered 30/3, 2020 at 9:17 Comment(1)
Yes, but I was looking for a way to get the payload without clicking the notification.Bronchi
B
1

Need activated "BackGround fetch" go to File Project - signIn capabilities - BackgroundModes - and check Background fetch and Remote notifications as well

remember in didFinishAlunchWithOptions

    Messaging.messaging().delegate = self
    UNUserNotificationCenter.current().delegate = self

with that I have a response in method - didReceive response - in background but only if tap the notification.

I found this info... but i dont know if is real

"If this is a new system behavior from iOS, then it's unlikely that FCM will be able to provide a workaround. A few things to note:

Apple's docs mention that silent notifications will be throttled by the system to conserve power. In the past, we've seen that testing silent notifications with your device plugged in to a power source will reduce throttling. In older iOS versions, Apple's docs have mentioned that force-quitting an application will prevent wakeups from silent notifications entirely. I'm not sure if this is still the case."

I Still looking around for the notifications in background for update badge icon in the app, at the momen I receive the notification.

Binkley answered 24/9, 2020 at 2:42 Comment(0)
S
0

Implement didRegisterForRemoteNotificationsWithDeviceToken and also didFailToRegisterForRemoteNotificationsWithError in your app's delegate, to check if the device makes a good connection with Apple's APN server. If this is not the case, restart the device and/or try to make a connection via another Wi-Fi network and restart the app.

Shapely answered 20/3, 2020 at 17:41 Comment(0)
O
0

I had the same problem. Read this :https://medium.com/fenrir-inc/handling-ios-push-notifications-the-not-so-apparent-side-420891ddf10b

I use Iphone XS Software Version 13.7

1. Before iOS 10, using the UIApplication method: registerUserNotificationSettings(:) iOS 10 onwards, using the UserNotifications framework methods: requestAuthorization(options:completionHandler:) setNotificationCategories(:)

if (UIDevice.CurrentDevice.CheckSystemVersion(10, 0))
        {
            UNUserNotificationCenter.Current.RequestAuthorization(UNAuthorizationOptions.Alert | UNAuthorizationOptions.Badge | UNAuthorizationOptions.Sound,
                                                                    (granted, error) => InvokeOnMainThread(UIApplication.SharedApplication.RegisterForRemoteNotifications));
        }
        else if (UIDevice.CurrentDevice.CheckSystemVersion(8, 0))
        {
            var pushSettings = UIUserNotificationSettings.GetSettingsForTypes(
                    UIUserNotificationType.Alert | UIUserNotificationType.Badge | UIUserNotificationType.Sound,
                    new NSSet());

            UIApplication.SharedApplication.RegisterUserNotificationSettings(pushSettings);
            UIApplication.SharedApplication.RegisterForRemoteNotifications();
        }
        else
        {
            UIRemoteNotificationType notificationTypes = UIRemoteNotificationType.Alert | UIRemoteNotificationType.Badge | UIRemoteNotificationType.Sound;
            UIApplication.SharedApplication.RegisterForRemoteNotificationTypes(notificationTypes);
        }

“Push Notifications”, "Remote notifications" and Background Modes capability is required (info.plist and Entitlements.plist)

3.

Implement DidReceiveRemoteNotification method and RegisteredForRemoteNotifications

After this change the DidReceiveRemoteNotification method was called.

Try this to silent Push Notification:

{
    "aps" : {
        "alert" : "",
        "content-available" : 1
    },
}

Try this to Normal Push Notification:

{
    "aps" : {
        "alert" : "Alert!!!"
    },
}
Ong answered 3/12, 2020 at 12:30 Comment(0)
S
0

I was facing the same problem too.

But it works after I reboot my device.

I'm using iOS 15.2

Subcontract answered 25/2, 2022 at 1:34 Comment(0)
D
0

I've ran into this problem as well with iOS 15. I've noticed that didReceiveRemoteNotification does not get called in the background if I build the target for Debug, but it does get called just fine if it's built for Release.

Druid answered 14/3, 2022 at 1:5 Comment(1)
This may just have been coincidence. I am seeing it work/not work on and off. Sometimes rebooting the device appears to fix it, sometimes not. I get periods of it working for a day or so, then it quits working. Terribly frustrating.Druid
S
-1

You need to implement a Notification Content Extension

As i was using OneSignal and it's setup code, this worked fine for me https://documentation.onesignal.com/docs/ios-sdk-setup

not sure if the OneSignal bits make a difference, but appending them anyway

import UserNotifications
import OneSignal

class NotificationService: UNNotificationServiceExtension {
    
    var contentHandler: ((UNNotificationContent) -> Void)?
    var receivedRequest: UNNotificationRequest!
    var bestAttemptContent: UNMutableNotificationContent?
    
    override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
        self.receivedRequest = request;
        self.contentHandler = contentHandler
        bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
        
        if let bestAttemptContent = bestAttemptContent {
            OneSignal.didReceiveNotificationExtensionRequest(self.receivedRequest, with: self.bestAttemptContent)
            contentHandler(bestAttemptContent)
        }
    }
    
    override func serviceExtensionTimeWillExpire() {
        // Called just before the extension will be terminated by the system.
        // Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used.
        if let contentHandler = contentHandler, let bestAttemptContent =  bestAttemptContent {
            OneSignal.serviceExtensionTimeWillExpireRequest(self.receivedRequest, with: self.bestAttemptContent)
            contentHandler(bestAttemptContent)
        }
    }
    
}

ObjC

#import <OneSignal/OneSignal.h>

#import "NotificationService.h"

@interface NotificationService ()

@property (nonatomic, strong) void (^contentHandler)(UNNotificationContent *contentToDeliver);
@property (nonatomic, strong) UNNotificationRequest *receivedRequest;
@property (nonatomic, strong) UNMutableNotificationContent *bestAttemptContent;

@end

@implementation NotificationService

- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler {
    self.receivedRequest = request;
    self.contentHandler = contentHandler;
    self.bestAttemptContent = [request.content mutableCopy];
    
    //If your SDK version is < 3.5.0 uncomment and use this code:
    /*
    [OneSignal didReceiveNotificationExtensionRequest:self.receivedRequest
                       withMutableNotificationContent:self.bestAttemptContent];
    self.contentHandler(self.bestAttemptContent);
    */
    
    /* DEBUGGING: Uncomment the 2 lines below and comment out the one above to ensure this extension is excuting
                  Note, this extension only runs when mutable-content is set
                  Setting an attachment or action buttons automatically adds this */
    // NSLog(@"Running NotificationServiceExtension");
    // self.bestAttemptContent.body = [@"[Modified] " stringByAppendingString:self.bestAttemptContent.body];
    
    // Uncomment this line to set the default log level of NSE to VERBOSE so we get all logs from NSE logic
    //[OneSignal setLogLevel:ONE_S_LL_VERBOSE visualLevel:ONE_S_LL_NONE];
    [OneSignal didReceiveNotificationExtensionRequest:self.receivedRequest
                       withMutableNotificationContent:self.bestAttemptContent
                                   withContentHandler:self.contentHandler];
}

- (void)serviceExtensionTimeWillExpire {
    // Called just before the extension will be terminated by the system.
    // Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used.
    
    [OneSignal serviceExtensionTimeWillExpireRequest:self.receivedRequest withMutableNotificationContent:self.bestAttemptContent];
    
    self.contentHandler(self.bestAttemptContent);
}

@end
Speak answered 30/4, 2020 at 10:4 Comment(3)
This question is for Objective-c in ios. Your answer seems to be for Kotlin in Android.Guesthouse
No, its Swift for iOS obviouslySpeak
Sorry, just realised that.Guesthouse

© 2022 - 2024 — McMap. All rights reserved.