2
votes

With UIBackgroundRefreshStatusAvailable, my app receives silent push notifications as expected, that is both in background as well as in foreground.

The name of this feature -- Background App Refresh -- suggests to me that it does not affect Foreground app behaviour when it is disabled.

Unfortunately, my app does not receive silent push notifications while it is in the foreground, with background app refresh disabled, i.e. UIBackgroundRefreshStatusDenied.

I'm not using user-visible push notitfications, hence no involvement of UNUserNotificationCenter etc.

Is it possible to receive silent push-notifications with background app refresh disabled, i.e. UIBackgroundRefreshStatusDenied?

Objective-C, Xcode 11.3.1, deployment target iOS 10.3. iPhone 6 with iOS 12.4.5 installed.

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    [application registerForRemoteNotifications];
    return YES;
}

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
    // not called when app in foreground but bg app refresh turned off
}

Here is an example userInfo dict that didReceiveRemoteNotification received when bg app refresh was enabled:

{
aps =     {
    "content-available" = 1;
};
ck =     {
    ce = 2;
    cid = "iCloud.de.udo-thiel.DiskBench";
    ckuserid = "_56bd97c2eb1e52d09756163efaab6b02";
    nid = "e70e4a8d-d77b-4315-8b3e-d9de229cf083";
    qry =         {
        dbs = 2;
        fo = 2;
        rid = "Res-iPad 2-16";
        sid = "public-results2";
        zid = "_defaultZone";
        zoid = "_defaultOwner";
    };
};

}

1
Could you share the payload of apns request?Ahmet Aygun
@AhmetAYGÜN I have added an example userInfo, is that what you were asking for?Mojo66

1 Answers

0
votes

This is an interesting question as it basically addresses the issue of naming this quite complex functionality.

In short, I am afraid the answer is no. While I haven't had to deal with this myself I have witnessed many others in the same position as you.

To me it helps to think of the entire "silent remote notification feature" as having less to do with the app being in foreground or background, and more with "remote app input":

If it is activated, i.e. you have UIBackgroundRefreshStatusAvailable, your server can send it, silently, in this case, messages on which is reacts. Basically, the server gives input in a similar manner as a user taps (albeit via different called functions, obviously). It doesn't matter whether the app is in foreground or background, this input happens.

If the feature is inactive, i.e. you have UIBackgroundRefreshStatusDenied or UIBackgroundRefreshStatusRestricted, the entire feature is switched off. That means this way of receiving input doesn't work, application:didReceiveRemoteNotification:fetchCompletionHandler: isn't called at all. This method name reflects the issue better than the state enum cases do, admittedly.


Two workarounds:

  1. The most obvious one: register and deregister for remote notifications in applicationDidBecomeActive: and applicationWillResignActive:. Unfortunately this might turn ugly as it results in your server having constantly changing tokens for users, but if you want to avoid getting notifications in background at any cost, go this way.
  2. Register for notifications once and indeed let your logic handle notifications in the foreground and background as you (seem to) already do, but simply make it so it ignores notifications while it is in background (using UIApplication.shared.applicationState). Technically this "wastes" a bit of runtime as your app might get woken up and then not do anything meaningful, but I think that's not too bad.

I'd go with option 2 myself as I rarely see a case where it hurts to receive a silent notification in background.

Generally I would not do anything that relies on notifications being enabled (background or foreground). To put it differently: Yes, if my app is in the foreground and I require to react to something happening on my server, I'm afraid I need to "check" said server, i.e. pull from it in some form.

Or I would, depending on the scenario, inform the user that they should enable it as otherwise the app won't make much sense... Hm...


As a side note: Yup, the Apple SDK is kind of confusing with naming and explaining all the different background things and notifications. Even the app state itself (active, inactive, background, foreground, suspended, ...) is more complex than the names make it seem. I think the reason for that is historical in nature. Before we had background modes and notifications at all, people were just polling data to get something like "silent foreground notifications", basically the thing you want. So eventually they wanted to be able to also do that when the app was not in foreground, asking loudly for background execution. Apple didn't really want to grant that without limits, so slowly the notification concept evolved, but since it was kind of related, the term "background" sneaked in there (besides, we got background fetch as well...), even if it doesn't necessarily make sense. One could also argue that it's still kind of valid, because it is just more important for when the app is in background/suspended. The use case to "get a silent notification only while in foreground" can still be covered by simple polling (although I agree that this is ugly), and if you use push it doesn't hurt to also get those in background.