10

I've setup (default iOS8) location-based notifications for my app.

UILocalNotification *notification = [[UILocalNotification alloc] init];
notification.regionTriggersOnce = NO;
notification.userInfo = @{ @"notification_id" : @"someID" };
notification.region = region;
notification.alertBody = alertBody;
[[UIApplication sharedApplication] scheduleLocalNotification:notification];

When a user enters the specified region, the notification is shown in the NotificationCenter correctly.

However, I want to remove that notification message when the user exits that region, because it makes little sense for the user to go home and look at the notification center until they recieve a message which looks like so:

"You are at XXXXX!"

Has anyone tried something similar? The documentation is unclear on how this can be done.

Turnerj
  • 4,258
  • 5
  • 35
  • 52
Vrasidas
  • 2,085
  • 1
  • 17
  • 23
  • Please check this once http://stackoverflow.com/questions/6340664/delete-a-particular-local-notification – naresh Aug 04 '15 at 10:46

7 Answers7

3

CLRegion object has special properties for that: notifyOnEntry and notifyOnExit.
Everything that you need is update code in this way:

CLRegion *region = .... // Configure region here
region.notifyOnEntry = YES;
region.notifyOnExit = NO;  

UILocalNotification *notification = [[UILocalNotification alloc] init];
notification.regionTriggersOnce = NO;
notification.userInfo = @{ @"notification_id" : @"someID" };
notification.region = region;
notification.alertBody = alertBody;
[[UIApplication sharedApplication] scheduleLocalNotification:notification];

Here is Apple documentation that explains it:

@property(nonatomic, copy) CLRegion *region
Assigning a value to this property causes the local notification to be delivered when the user crosses the region’s boundary.
The region object itself defines whether the notification is triggered when the user enters or exits the region.

Vlad Papko
  • 13,184
  • 4
  • 41
  • 57
  • True but you don't know the state you are in when the notification is fired. You could save the first delivered notification and delete if when the second comes in, but this is tricky here. If you schedule your notification while you are inside nothing will happen at all and the first fired notification will be on exit. The better way to do so is to monitor regions but we only can register up to 20 of them per app. I described my suggestions in my answer below. – DevAndArtist Aug 05 '15 at 10:12
  • @DevAndArtist, you know state: it's entry in region state, it's configured in region. If you are inside the region when you schedule local notification it means that app is launched and you can show necessary dialog immediately. If you need show notification when user exits region, then you can additionally create region with properties: `notifyOnEntry = NO`, `notifyOnExit = YES` and schedule local notification for this region, and set `regionTriggersOnce = YES`. It will be fired once when user exits current region, for other cases notification will be fired when user enters the region. – Vlad Papko Aug 05 '15 at 15:14
  • True, this is another workaround but if both properties are set to `yes` there is no chance to know what state fired your notification without calling `requestStateForRegion`. Sure it is possible to build a mechanic to fire two different notifications then always cancel `onExit` notification and the delivered `onEntry` notification. I kinda missed that you set both properties with a different value. My bad. :D – DevAndArtist Aug 05 '15 at 15:20
  • Please tell me how to determine after opening a closed app that region based UILocalNotification is fired or not. – S.J Mar 02 '16 at 08:39
1

I was really tired yesterday and couldn't complete my answer in time.

Based on some answers here I have only a clue what you have to do. I did not tried it myself (geofencing is a really a pain to test, I know that because I'm working on a geofencing project atm.), I also never had to delete a delivered notification from the Notification Center before.

I assume your application will not terminate due the whole process. So we won't use startMonitoringForRegion function here.

Answer written in Swift 2.0 (It's not that hard to translate it to ObjC).

func createAndRegisterSomeNotificationSomewhere() {

    let region = CLCircularRegion(center: someCoordinates, radius: someRadius, identifier: someIdentifier)

    region.notifyOnEntry = true
    region.notifyOnExit = true

    let locationNotification = UILocalNotification()
    locationNotification.alertBody = "someAlertBody"
    locationNotification.userInfo = ["notification_id" : "someID"]
    locationNotification.regionTriggersOnce = false
    locationNotification.region = region // remember 'presentLocalNotificationNow' will not work if this value is set

    UIApplication.sharedApplication().scheduleLocalNotification(locationNotification)
}

/* CLLocationManagerDelegate provides two function */

// func locationManager(manager: CLLocationManager, didEnterRegion region: CLRegion)
// func locationManager(manager: CLLocationManager, didExitRegion region: CLRegion)

/* If I'm not mistaken they are only called for monitored regions and not location based local notifications */
/* I mean you will have to use something like: self.locationManager.startMonitoringForRegion(someCircularRegion) */
/* Correct me if I'm wrong. So consider to rebuild the following logic to ease everything if you want to monitor regions. */

/* Now when you receive your location notification */
func application(application: UIApplication, didReceiveLocalNotification notification: UILocalNotification) {

    if let region = notification.region {

        self.locationManager.requestStateForRegion(region)

        /* based on other answers this will remove your noticaiton from NC and cancel from showing it anywhere */
        application.cancelLocalNotification(notification)
        /* but we need this notification still be scheduled because 'region.notifyOnExit = true' should fire it again later */
        application.scheduleLocalNotification(notification)
    }
}

func locationManager(manager: CLLocationManager, didDetermineState state: CLRegionState, forRegion region: CLRegion) {

    /* this is not the best solution, because it adds some latency to the dilivery code and CLRegionState can also be Unknown sometimes */
    /* I'd go with the two functions above if I only had up to 20 regions to monitor (max region limit per App, read CLLocationManager docs) */
    /* the mechanics would be more clear and save */

    switch state {

    case .Inside:

        /* create a new noticiation with the same cpecs as the cancled notification but this time withot the region */
        let sameNotificationAsAbove = UILocalNotification()
        /* if you really need to know your IDs inside userInfo so create some good mechanics to pass these before canceling */
        /* at least I would save the identifier of the region iside the noticiation */
        /* save the notification somewhere to delete it later from NC */
        self.someArrayToSaveDeliveredNotifications.append(sameNotificationAsAbove)

        /* fire the notification */
        UIApplication.sharedApplication().presentLocalNotificationNow(sameNotificationAsAbove)

    case default:

        /* if it is true that notication inside NC can be deleted just by calling 'cancelLocalNotification' function */
        /* so find your notification inside someArrayToSaveDeliveredNotifications bases on the region.identier which you saved inside userInfo */
        let notificationToCancel = self.getNotificationForIdentifier(region.identifier)

        UIApplication.sharedApplication().cancelLocalNotification(notificationToCancel)
        /* this should delete your notification from NC based on other answers */
    }
}

This is some kind of pseudo mechanics I would build if I had to, so if anything is wrong or not correct I would appreciate to hear your feedback. :)

DevAndArtist
  • 4,971
  • 1
  • 23
  • 48
0

This will clear all of the app's notifications from Notification Center.

[[UIApplication sharedApplication] setApplicationIconBadgeNumber: 0];
[[UIApplication sharedApplication] cancelAllLocalNotifications];
arturdev
  • 10,884
  • 2
  • 39
  • 67
  • I want to know if I can remove a specific notification from the NotificationCenter after it has been shown. Kind of like the Facebook Messenger app does, but with a Location-Based Notification instead. – Vrasidas Jul 29 '15 at 10:54
0

It appears that you can clear a specific notification if you hold onto the UILocalNotification object. Using your the notification object you created in your example above you can call

[[UIApplication sharedApplication] cancelLocalNotification:notification];

to clear the notification.

  • Can I store the UILocalNotification in NSUserDefaults? I get a "non-property list" error. I need to remove the notification from NotificationCenter in background mode when the user exits the region. From what I've seen searching (and from examples like Foursquare) this might not be possible at all. – Vrasidas Aug 01 '15 at 07:51
  • No, the only data types you can store in NSUserDefaults are NSDictionary, NSArray, NSData, NSNumber, and NSString. – elephant_ken Aug 03 '15 at 15:38
0

It looks like very simple solution. follow the below steps it will help you.

  • Make one background service which will work continue in the background to check your location.
  • When you are entering in some region fire local notification which is already done by you. good work.
  • But when you are leaving that region (check background service with enter location details like now location is matching with the location details when user entered in that region). fire the new local notification with empty data. and than it will clear the notification from the notification window also.
Code Hunterr
  • 175
  • 9
  • The behavior I've seen is that a UILocalNotification with empty data (empty alertBody) is ignored. And if alertBody is present, it gets added to the list of notifications. – Vrasidas Aug 03 '15 at 11:43
  • @vrasidas: just add a body add count 0 and description is empty string. and try. – Code Hunterr Aug 03 '15 at 11:44
  • Testing in applicationDidFinishLaunching: `UILocalNotification *localNotif = [[UILocalNotification alloc] init]; localNotif.fireDate = [[NSDate date] dateByAddingTimeInterval:10]; localNotif.alertBody = @"THIS IS A LOCAL NOTIFICATION"; [[UIApplication sharedApplication] scheduleLocalNotification:localNotif]; UILocalNotification *localNotif2 = [[UILocalNotification alloc] init]; localNotif2.fireDate = [[NSDate date] dateByAddingTimeInterval:15]; localNotif2.alertBody = @""; [[UIApplication sharedApplication] scheduleLocalNotification:localNotif2];` – Vrasidas Aug 03 '15 at 11:56
  • This shows the first notification after 10 seconds, but the second one is just ignored. I'm going on the assumption that UILocalNotifications behave similarly when triggered by a region. – Vrasidas Aug 03 '15 at 11:57
0

You should trigger a background method which should only be called when the device is going out of the location. This can be achieved by creating layer for region and trigger immediately when it is coming out of boundary.

In the method, you may either clear all the notification of the respective app by

[[UIApplication sharedApplication] cancelLocalNotification:notification];

or may clear only specific notification by calling

UIApplication* application = [UIApplication sharedApplication];
NSArray* scheduledNotifications = [NSArray arrayWithArray:application.scheduledLocalNotifications];
application.scheduledLocalNotifications = scheduledNotifications;

You will receive all the valid notification available for the particular app. Delete the specific notification for the specific region.

Mithun Ravindran
  • 2,292
  • 2
  • 14
  • 23
0

CLLocationManagerDelegate delegate has a set of methods that get triggered base on the device location. Such as;

-(void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region
-(void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region
-(void)locationManager:(CLLocationManager *)manager didRangeBeacons:(NSArray *)beacons inRegion:(CLBeaconRegion *)region

In your case what you have to do is when the following callback get triggered;

-(void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region

remove your notification from the NotificationCenter

Laky
  • 632
  • 3
  • 10