How to show beacon notification only once a day in Swift?

Viewed 563

My beacon notification fires at any time when the beacon signal is detected. How can I limit this to receive notification only once a day?

I implemented a date check, but this didn't help. The if/else check works fine, but beacon notifications are fired disregarding this check

Any suggestion for a workaround is highly appreciated!

Here my code snippet:

class ViewController: UIViewController, CLLocationManagerDelegate {

    let locationManager = CLLocationManager()

    let date = Date()
    let calendar = Calendar.current

    override func viewDidLoad() {
        super.viewDidLoad()

        locationManager.delegate = self
        locationManager.requestAlwaysAuthorization()
        locationManager.desiredAccuracy = 10
        locationManager.distanceFilter = 100
        locationManager.startUpdatingLocation()


        let year = calendar.component(.year, from: date)
        let month = calendar.component(.month, from: date)
        let day = calendar.component(.day, from: date)
        let currentDate = "\(year)\(month)\(day)"
        var savedDate = UserDefaults.standard.string(forKey: "savedDate") ?? "12345"

        print(currentDate)
        print(savedDate)

        if (savedDate == currentDate){
            print("same date - no action")
        } else {
            savedDate = currentDate
            UserDefaults.standard.set(savedDate, forKey: "savedDate")
            print(savedDate + " execute program")

            let beaconRegion = CLBeaconRegion(proximityUUID: UUID(uuidString: "13D9F4C7-A68D-46F4-8D35-4BA7F64BC417")!, identifier: "estimote")
            beaconRegion.notifyOnEntry = true
            beaconRegion.notifyOnExit = false

            let content = UNMutableNotificationContent()
            content.title = " Daily beacon check! "
            content.subtitle = "Receive a new info every day!"
            content.body = "ONLY ONCE A DAY WE SUPPLY INFO!"
            content.sound = .default
            content.badge = 1

            let trigger = UNLocationNotificationTrigger(region: beaconRegion, repeats: true)
            let identifier = "estimote"
            let request = UNNotificationRequest.init(identifier: identifier, content: content, trigger: trigger)
            UNUserNotificationCenter.current().removeAllPendingNotificationRequests()

            UNUserNotificationCenter.current().delegate = self as? UNUserNotificationCenterDelegate
            UNUserNotificationCenter.current().add(request, withCompletionHandler: { (error) in
            })

            self.locationManager.startRangingBeacons(in: beaconRegion)

        }
    }
1 Answers

By setting

let trigger = UNLocationNotificationTrigger(region: beaconRegion, repeats: true)
let request = UNNotificationRequest.init(identifier: identifier, content: content, trigger: trigger)

You are basically telling the program to go off and automatically deliver a notification when the beaconRegion condition is met. That is comfortable in case you always want the notification and let the system handle it for you. But you have no possibility to intervene at that point as it runs automatically.

What you can do is handle the notifications manually and check if you should send the notification each time the beacon is detected. Use the locationManager(didRangeBeacons beacons:in region:) delegate function for that:

let locationManager = CLLocationManager()
override func viewDidLoad() {
    super.viewDidLoad()

    // Setup beacon tracking once
    locationManager.delegate = self
    locationManager.requestAlwaysAuthorization()
    locationManager.desiredAccuracy = 10
    locationManager.distanceFilter = 100
    locationManager.startUpdatingLocation()
    let beaconRegion = CLBeaconRegion(proximityUUID: UUID(uuidString: "13D9F4C7-A68D-46F4-8D35-4BA7F64BC417")!, identifier: "estimote")
    beaconRegion.notifyOnEntry = true
    beaconRegion.notifyOnExit = false

    // Start looking for beacons
    self.locationManager.startRangingBeacons(in: beaconRegion)
}

// Handle beacons in range
func locationManager(_ manager: CLLocationManager, didRangeBeacons beacons: [CLBeacon], in region: CLBeaconRegion) {
    let now = Date()
    let formatter = DateFormatter()
    formatter.dateFormat = "yyyyMMdd"
    let nowString = formatter.string(from: now)

    // Check if notified ever (lastTime != nil) and if notified today
    if let lastTime = UserDefaults.standard.string(forKey: "savedDate"), lastTime == nowString {
        // Already notified today, skip
        print("same date - no action")
        return
    }

    // Your notification code
    let content = UNMutableNotificationContent()
    content.title = " Daily beacon check! "
    content.subtitle = "Receive a new info every day!"
    content.body = "ONLY ONCE A DAY WE SUPPLY INFO!"
    content.sound = UNNotificationSound.default()
    content.badge = 1

    let identifier = "estimote"
    let request = UNNotificationRequest.init(identifier: identifier, content: content, trigger: nil)

    UNUserNotificationCenter.current().add(request, withCompletionHandler: { (error) in
        if error != nil {
          print("Error showing notification: \(error!.localizedDescription)")
        } else {
          print("Notification shown")
        }
    })

    UserDefaults.standard.set(nowString, forKey: "savedDate")
}
Related