Last Updated: September 09, 2019
·
12.41K
· ktusznio

Handling UILocalNotification When App Is Active

In My Minutes I use UILocalNotifications to let users know when they hit their goal for a task. iOS displays these notifications nicely when My Minutes is running in the background or when the phone is locked.

But what about when the app is active?

It's certainly an edge-case since I don't expect the user keep the app active when they are running the timer for a task, but it still occurs and I want the user to get that same heads-up if they hit a goal with the app open.

Here's how to implement an alert view and a notification sound to mimic the behavior of a UILocalNotification being received.

Responding to UILocalNotification

First I added UIAlertView and SystemSoundID properties to the app delegate.

@interface AppDelegate : UIResponder <UIApplicationDelegate> {
    SystemSoundID _notificationSound;
}

@property (strong, nonatomic) UIAlertView *notificationAlert;

The notification alert is synthesized in the implementation:

@synthesize notificationAlert = _notificationAlert;

Whether the app is active or in the background, application:didReceiveLocalNotification: still gets called in the app delegate so the code goes in there:

- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification {
    // Play a sound and show an alert only if the application is active, to avoid doubly notifiying the user.
    if ([application applicationState] == UIApplicationStateActive) {
        // Initialize the alert view.
        if (!_notificationAlert) {
            UIAlertView *alert = [[UIAlertView alloc] initWithTitle:nil
                                                            message:nil
                                                           delegate:nil
                                                  cancelButtonTitle:@"Ok"
                                                  otherButtonTitles:nil];
            [self setNotificationAlert:alert];
        }

        // Load the notification sound.
        if (!_notificationSound) {
            NSString *soundPath = [[NSBundle mainBundle] pathForResource:@"Notification"
                                                                  ofType:@"wav"];
            NSURL *soundURL = [NSURL fileURLWithPath:soundPath];
            AudioServicesCreateSystemSoundID((__bridge CFURLRef)soundURL, &_notificationSound);
        }

        // Set the title of the alert with the notification's body.
        [_notificationAlert setTitle:[notification alertBody]];

        // Play the sound and show the alert.
        AudioServicesPlaySystemSound(_notificationSound);
        [alert show];
    }
}

I check whether the app is active. Otherwise the user gets doubly notified of the same event, since they're already receiving the UILocalNotification when the app is in the background.

The alert and sound are lazy-loaded and kept around so they aren't initialized every time. They could be initialized when the app starts but I chose not to do that to minimize startup time.

In order to use AudioServicesCreateSystemSoundID you need to add AudioToolbox.framework to your project.

The text from the notification is used in the alert. That's done outside initialization since it's dynamic.

For the actual wav file, I found a collection of sounds that are similar to standard iOS system sounds here.

Lastly, the notification sound is disposed of in applicationWillTerminate:

- (void)applicationWillTerminate:(UIApplication *)application {
    // Dispose of the notification sound.
    if (_notificationSound) {
        AudioServicesDisposeSystemSoundID(_notificationSound);
    }
}

1 Response
Add your response

two 'my' in link, fyi

over 1 year ago ·