Displaying a stock iOS notification banner when your app is open and in the foreground?

104,355

Solution 1

iOS 10 adds the UNUserNotificationCenterDelegate protocol for handling notifications while your app is in the foreground.

The UNUserNotificationCenterDelegate protocol defines methods for receiving notifications and for handling actions. When your app is in the foreground, arriving notifications are delivered to your delegate object instead of displayed automatically using the system interfaces.

Swift:

optional func userNotificationCenter(_ center: UNUserNotificationCenter, 
                     willPresent notification: UNNotification, 
      withCompletionHandler completionHandler: (UNNotificationPresentationOptions) -> Void)

Objective-C:

- (void)userNotificationCenter:(UNUserNotificationCenter *)center 
       willPresentNotification:(UNNotification *)notification 
         withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler;

The UNNotificationPresentationOptions flags allow you to specify UNNotificationPresentationOptionAlert to display an alert using the text provided by the notification.

This is key as it allows you to display the alert while your app is open and in the foreground, which is new for iOS 10.


Sample code:

class AppDelegate: UIResponder, UIApplicationDelegate {
    
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        
        // Set UNUserNotificationCenterDelegate
        UNUserNotificationCenter.current().delegate = self
        
        return true
    }
    
}

// Conform to UNUserNotificationCenterDelegate
extension AppDelegate: UNUserNotificationCenterDelegate {
    
    func userNotificationCenter(_ center: UNUserNotificationCenter,
           willPresent notification: UNNotification,
           withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void)
    {
        completionHandler(.alert)
    }
    
}

Solution 2

For displaying banner message while app is in foreground, use the following method.

iOS 10+, Swift 3+:

func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
    completionHandler([.alert, .badge, .sound])
}

Solution 3

EDIT:

Foreground alerts are now possible in iOS 10! Please see this answer.

For iOS 9 and below:

It does not seem to be possible to show the stock iOS notification alert when your app is open and in the foreground. Messages.app must be using a private API.

The system does not display any alerts, badge the app’s icon, or play any sounds when the app is already frontmost. - UILocalNotification docs

The UIApplicationDelegate methods will still be called, allowing your app to respond to the local or remote notification:

application:didReceiveLocalNotification:
application:didReceiveRemoteNotification:

However, the stock native iOS notification alert banner UI will not be shown as it is in Apple's Messages.app, which must be using a Private API.

The best you can do is is roll your own alert banner or use an existing framework:

-(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo 
{    
    // Use a 3rd party toast alert framework to display a banner 
    [self toastAlertFromGithub]
}

I have opened a radar for this behavior here: rdar://22313177

Solution 4

To show notifications while the App is open, we need to handle it manually. So what I am doing below is to handle the notification once received.

Add all below in AppDelegate.m

  1. Handle call for notify
  2. Create a view, add AppIcon, Notification message and show it as an animation
  3. Add Touch recogniser to remove if touched or remove in 5 seconds with animation.

Let me know if this is an ok solution. Worked well for me but am unsure if this is the right way.

- (void)application:(UIApplication *)applicationdidReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {


NSString *notifMessage = [[userInfo objectForKey:@"aps"] objectForKey:@"alert"];

//Define notifView as UIView in the header file
[_notifView removeFromSuperview]; //If already existing

_notifView = [[UIView alloc] initWithFrame:CGRectMake(0, -70, self.window.frame.size.width, 80)];
[_notifView setBackgroundColor:[UIColor blackColor]];

UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(10,15,30,30)];
imageView.image = [UIImage imageNamed:@"AppLogo.png"];

UILabel *myLabel = [[UILabel alloc] initWithFrame:CGRectMake(60, 15, self.window.frame.size.width - 100 , 30)];
myLabel.font = [UIFont fontWithName:@"Helvetica" size:10.0];
myLabel.text = notifMessage;

[myLabel setTextColor:[UIColor whiteColor]];
[myLabel setNumberOfLines:0];

[_notifView setAlpha:0.95];

//The Icon
[_notifView addSubview:imageView];

//The Text
[_notifView addSubview:myLabel];

//The View
[self.window addSubview:_notifView];

UITapGestureRecognizer *tapToDismissNotif = [[UITapGestureRecognizer alloc] initWithTarget:self
                                                                                    action:@selector(dismissNotifFromScreen)];
tapToDismissNotif.numberOfTapsRequired = 1;
tapToDismissNotif.numberOfTouchesRequired = 1;

[_notifView addGestureRecognizer:tapToDismissNotif];


[UIView animateWithDuration:1.0 delay:.1 usingSpringWithDamping:0.5 initialSpringVelocity:0.1 options:UIViewAnimationOptionCurveEaseIn animations:^{

    [_notifView setFrame:CGRectMake(0, 0, self.window.frame.size.width, 60)];

} completion:^(BOOL finished) {


}];


//Remove from top view after 5 seconds
[self performSelector:@selector(dismissNotifFromScreen) withObject:nil afterDelay:5.0];


return;


}

//If the user touches the view or to remove from view after 5 seconds
- (void)dismissNotifFromScreen{

[UIView animateWithDuration:1.0 delay:.1 usingSpringWithDamping:0.5 initialSpringVelocity:0.1 options:UIViewAnimationOptionCurveEaseIn animations:^{

    [_notifView setFrame:CGRectMake(0, -70, self.window.frame.size.width, 60)];

} completion:^(BOOL finished) {


}];


}

Solution 5

UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init];
content.body = body;
content.userInfo = userInfo;
content.sound = [UNNotificationSound defaultSound];
[content setValue:@(YES) forKeyPath:@"shouldAlwaysAlertWhileAppIsForeground"];

UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:@"Notif" content:content trigger:nil];
[[UNUserNotificationCenter currentNotificationCenter] addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) {
    DLog(@"Error:%@", error);
}];

I can show push notification when app is active for iOS 10.

  1. The push notification from server should be silent.

  2. When receive remote notification from server, you send a local notification and set the value for keyPath: shouldAlwaysAlertWhileAppIsForeground = True

Share:
104,355
pkamb
Author by

pkamb

Mac / iOS engineer

Updated on July 18, 2022

Comments

  • pkamb
    pkamb almost 2 years

    When Apple's official iOS Messages app is open and in the foreground, new messages from other contacts trigger a stock, native iOS notification alert banner. See image below.

    Is this possible in 3rd party apps on the App Store? Local and/or Push Notifications for your app while your app is open and in the foreground?

    When testing my app, notifications are received but no iOS alert UI is shown.

    But this behavior is seen in Apple's official Messages app:

    Messages is open and in the foreground. Still shows a notification alert.

    The Local and Remote Notification Programming Guide says:

    When the operating system delivers a local notification or remote notification and the target app is not running in the foreground, it can present the notification to the user through an alert, icon badge number, or sound.

    If the app is running in the foreground when the notification is delivered, the app delegate receives a local or remote notification.

    So yes, we can receive the notification data while in the foreground. But I see no way to present the native iOS notification alert UI.

    -(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo 
    {
        // I know we still receive the notification `userInfo` payload in the foreground.
        // This question is about displaying the stock iOS notification alert UI.
    
        // Yes, one *could* use a 3rd party toast alert framework. 
        [self use3rdPartyToastAlertFrameworkFromGithub]
    }
    

    Is Messages then using a private API to display the alert while in the foreground?

    For the purpose of this question, please do not suggest any 3rd party "toast" pop-up alerts on github or etc. I'm only interested if this can be done using the stock, native iOS Local or Push Notification alerts UI while your application is open and in the foreground.

  • pkamb
    pkamb about 9 years
    Thanks, but just looking for answers using the stock iOS notification alert.
  • Mo Farhand
    Mo Farhand about 9 years
    ok but i think you can't do it , also look at this Link : stackoverflow.com/questions/14872088/…
  • pkamb
    pkamb about 9 years
    @matt I am attempting to keep this question on topic: showing the stock iOS alert in the foreground. Plenty of other "best toast alert framework" questions. An answer of "No, you cannot show the iOS alert in the foreground" would be perfectly acceptable, and I will accept that answer until this is possible in a future version of iOS.
  • Machado
    Machado about 8 years
    How does whatsapp and other famous apps do that, then?
  • pkamb
    pkamb about 8 years
    @tardoandre have a screenshot? I assume it's by mimicking the stock iOS alert in their own views.
  • pkamb
    pkamb about 8 years
    This may be a good answer for creating your own alert, but it isn't showing the Stock iOS Alert as shown in the screenshot.
  • Hafeez
    Hafeez about 8 years
    Hi,Thank you for your kind words. Yes, it won't look like that (if you are talking about the appearance) as the one shown above in the image is iOS notification and the one via code is custom. We can maybe replicate the looks in the view. I think WhatsApp has a neat looking one with background blur etc.
  • Hafeez
    Hafeez about 8 years
    Someone down voted :(. Would be nice to hear why!?. Thanks.
  • pkamb
    pkamb about 8 years
    I did, as the code in this answer (nice though it may be) doesn't really answer the question asked: showing a stock iOS toast banner inside the app. There are other questions on SO where this answer would be great.
  • Hafeez
    Hafeez about 8 years
    Thanks pkamb for clarifying, makes sense. I missed the "stock" word I guess.
  • pkamb
    pkamb over 7 years
    This BRYXBanner framework does not seem to use the stock iOS notification banner.
  • azmeuk
    azmeuk over 7 years
    Could you provide a working code sample where an incoming foreground notification is displayed by the system as if the application where background?
  • chengsam
    chengsam over 7 years
    @azmeuk Check my answer...
  • Torre Lasley
    Torre Lasley over 7 years
    Wow, everywhere I read people said this wasn't possible. I'm so glad I found this answer, it works exactly as I expected! When the app is running, push notifications now show exactly like they would if the app were in the background. Thank you!
  • Ketan Parmar
    Ketan Parmar over 7 years
    You should include link for toastAlertFromGithub if you are suggesting this third party library to use!
  • Yoav R.
    Yoav R. over 7 years
    Where this snippet goes? In the appDelegate, or somewhere else?
  • Yoav R.
    Yoav R. over 7 years
    in swift 2 - it is content.setValue("YES", forKeyPath: "shouldAlwaysAlertWhileAppIsForeground") , Right? (with "Yes" and not true)
  • chengsam
    chengsam over 7 years
    @YoavR. AppDelegate
  • Erika Electra
    Erika Electra over 7 years
    simply calling completionHandler(UNNotificationPresentationOptionAlert) doesn't show anything for me.. Is there something else I need to call, or pass in? I'm running on watchOS 3.1 using the same delegate method. Thanks!
  • zeus
    zeus about 7 years
    me too, it's doesn't show anything for me :( i m ios 10 under ipad pro
  • chengsam
    chengsam about 7 years
    @Cindeselia Can you receive notification data in the foreground?
  • chengsam
    chengsam about 7 years
    @loki Can you receive notification data in the foreground?
  • mfaani
    mfaani about 7 years
    just to be sure...do you mean since iOS 10 you can now also show alerts/banners/sounds in foreground just like you do in background?
  • Boominadha Prakash M
    Boominadha Prakash M almost 7 years
    I have used the same code. But I don't know why the notification is not displaying when the app was in foreground.
  • chengsam
    chengsam almost 7 years
    @BoominadhaPrakashM There are many reasons for this, e.g. Did you set up push notification? Can you receive the notification? Is your app development or release? Give more details so I can help you.
  • Boominadha Prakash M
    Boominadha Prakash M almost 7 years
    @chengsam Thanks for your reply. Now I am getting the notification Alert. Actually I am using firebase could messaging using firebase js functions. I haven't added body parameter in payload. I have added and now and I am receiving. Thank you :)
  • chengsam
    chengsam almost 7 years
    @BoominadhaPrakashM Good to hear that!
  • Mamta
    Mamta almost 7 years
    @chengsam please give the equivalent code for objective c (I'm using xcode 8 and iOS10)
  • user1960169
    user1960169 almost 7 years
    Where I can put the above coding part? in did receiveRemoteNotification?
  • Dilip Tiwari
    Dilip Tiwari almost 7 years
    @Leslie Godwin will it work for ios 8.0 for showing notifications in Foreground
  • Lunarchaos42
    Lunarchaos42 over 6 years
    Fix for iPhone X put this in the animation block: if (@available(iOS 11.0, *)) { if (UIApplication.sharedApplication.keyWindow.safeAreaInsets.to‌​p > 0.0) { [_notifView setFrame:CGRectMake(0, 30, self.window.frame.size.width, 60)]; } else { [_notifView setFrame:CGRectMake(0, 0, self.window.frame.size.width, 60)]; } } else { [_notifView setFrame:CGRectMake(0, 0, self.window.frame.size.width, 60)]; }
  • Piyush Sanepara
    Piyush Sanepara over 6 years
    @pkamb What is the best solution for ios 9
  • Deniss Fedotovs
    Deniss Fedotovs over 6 years
    Don't forget to add UNUserNotificationCenter.current().delegate = self in the application(_:willFinishLaunchingWithOptions:) or application(_:didFinishLaunchingWithOptions:) method
  • DawnSong
    DawnSong over 6 years
    userNotificationCenter(_:willPresent:withCompletionHandler:) will be called when your app is running in the foreground. So it does NOT matter with "content-available", which is related to "background fetch".
  • Jadent
    Jadent about 6 years
    Also make sure that you do not have your device in Do Not Disturb mode otherwise the notifications will be visible in the Notifications Center, but would not be presented in the foreground over your application
  • mfaani
    mfaani about 6 years
    Chengsam you might want to correct this: It doesn't have to be put in the AppDelegate. It has to be put for whatever object that conforms to UNUserNotificationCenterDelegate. Having that said most developers make their AppDelegate conform to UNUserNotificationCenterDelegate. @YoavR.
  • Matt Bearson
    Matt Bearson almost 6 years
    Caution: This will crash your app under iOS 12. See discussion here: stackoverflow.com/questions/41373321/…
  • Hilaj
    Hilaj over 5 years
    Thank you @chengsam.. I did something wrong by seeing a tutorial. Now i get the correct solution. Anyway thanks..
  • ingconti
    ingconti about 5 years
    my 2 cents: don't miss: import UserNotifications. !
  • iOSer
    iOSer about 4 years
    Can this be conditional? I mean suppose I'm showing chat notifications. Can I not show the notifications if the chat screen is open but show it otherwise?
  • Chris Prince
    Chris Prince over 3 years
    I'm having a problem with this now-- with iOS 14.4 and a SwiftUI app. I am using an AppDelegate. Here is the code: github.com/SyncServerII/Neebla/blob/… and github.com/SyncServerII/Neebla/blob/… That is, I am not getting push notifications with the app in the foreground. No problem getting them with app in background.
  • bze12
    bze12 about 3 years
    .alert is deprecated as of ios 14. What should we replace it with?
  • Harshit Jain
    Harshit Jain almost 3 years
    Could you please explain how's this working? like when is this function being called?
  • Harshit Jain
    Harshit Jain almost 3 years
    Could anyone please explain how's this working? like when is this function being called?
  • Harshit Jain
    Harshit Jain almost 3 years
    what is optional func ? in above written 'optional func userNotificationCenter('
  • pkamb
    pkamb almost 3 years
    @HarshitJain you need to read about the Delegate Pattern: developer.apple.com/documentation/usernotifications/…
  • Ahmad Ismail
    Ahmad Ismail almost 3 years
    I also noticed that when you implement userNotificationCenter(_:willPresent:completionHandler:) method, the system does NOT call application(_:didReceiveRemoteNotification:fetchCompletionHa‌​ndler:) AppDelegate method
  • Kishan Bhatiya
    Kishan Bhatiya over 2 years
    @pkamb, are we able to handle click on local push notification (without any action identifier) when an app is in an in-active state (killed state)?
  • pkamb
    pkamb over 2 years
    @KishanBhatiya not sure; another Question asking that would be valuable on SO.