NEW: Subscribe to Hacking with Swift+ and accelerate your learning! >>

iBeacon app - problem with notifications looping instead of showing just once

Forums > Swift

Hi guys,

I started making my first app, which basically is all about getting certain notifications when being close to a beacon. I wanted a notification to show when the beacon proximity was '.near'. And all what be fine, if not for the notification showing over and over again, even though I tried to regulate it with a boolean to show it just once. I know the code might be much, for which I apologize, but I digged through StackOverflow and haven't managed to find the solution, you guys are all I have now :D

Most of the code is taken from an iBeacon sample app. I believe it was written in Swift 3 or 4, as when I launched the project, Xcode suggested migrating the code to Swift 5, which I did. I also changed some deprecated commands to the valid ones.

Anyway, here's the code (please don't mind the table view etc., all I care about now is just the notification issue):

class ViewController: UITableViewController, CLLocationManagerDelegate, UNUserNotificationCenterDelegate {

    let locationManager = CLLocationManager()
    var foundBeacons = [CLBeacon]()

    var beaconRegion: CLBeaconRegion!
    var beaconRegionConstraints: CLBeaconIdentityConstraint!

    var isRanging = false

    var notificationIsBlocked = false
    var notificationTimer: Timer!

    override func viewDidLoad() {
        super.viewDidLoad()

        title = "iBeacons"

        UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound]) { granted, error in
            print("Permission granted? \(granted)")
        }
        UNUserNotificationCenter.current().delegate = self

        locationManager.delegate = self

        let uuid = UUID(uuidString: "0fdb9b40-cf82-4362-ba5d-246f094f5c2a")!
        beaconRegion = CLBeaconRegion(uuid: uuid, major: 101, minor: 2, identifier: uuid.uuidString)
        beaconRegionConstraints = CLBeaconIdentityConstraint(uuid: uuid, major: 101, minor: 2)

        if CLLocationManager.authorizationStatus() != .authorizedAlways {
            locationManager.requestAlwaysAuthorization()
        } else {
            locationManager.startMonitoring(for: beaconRegion)
        }
    }

    func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
        switch status {
        case .authorizedAlways:
            if !locationManager.monitoredRegions.contains(beaconRegion) {
                locationManager.startMonitoring(for: beaconRegion)
            }
        case .authorizedWhenInUse:
            if !locationManager.monitoredRegions.contains(beaconRegion) {
                locationManager.startMonitoring(for: beaconRegion)
            }
        default:
            print("authorisation not granted")
        }
    }

    func locationManager(_ manager: CLLocationManager, didDetermineState state: CLRegionState, for region: CLRegion) {
        print("Did determine state for region \(region)")
        if state == .inside {
            locationManager.startRangingBeacons(satisfying: beaconRegionConstraints)
            isRanging = true
            postNotification()
        } else {
            isRanging = false
        }

        tableView.reloadData()

    }

    func locationManager(_ manager: CLLocationManager, didStartMonitoringFor region: CLRegion) {
        print("Did start monitoring region: \(region)\n")
        tableView.reloadData()
    }

    func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) {
        locationManager.startRangingBeacons(satisfying: beaconRegionConstraints)
        print("didEnter")

        tableView.reloadData()
    }

    func locationManager(_ manager: CLLocationManager, didExitRegion region: CLRegion) {
        locationManager.stopRangingBeacons(satisfying: beaconRegionConstraints)
        print("didExit")
        foundBeacons = []
        tableView.reloadData()
    }

    func locationManager(_ manager: CLLocationManager, didRangeBeacons beacons: [CLBeacon], in region: CLBeaconRegion) {
        foundBeacons = beacons
        foundBeacons.sort { beacon1, beacon2 -> Bool in
            beacon1.rssi < beacon2.rssi
        }

        for each in foundBeacons {
            switch each.proximity {
            case .far:
                print("You are far")

            case .near:
                print("You are near")
                if notificationIsBlocked == false {
                    postNearbyNotification()
                    notificationIsBlocked = true
                    notificationTimer = Timer(timeInterval: 30, target: self, selector: #selector(unblockShowingNotification), userInfo: nil, repeats: false)
                    notificationTimer.fire()
                }

            case .immediate:
                print("You are right here")

            case .unknown:
                print("Position unknown")

            @unknown default:
                print("Position unknown. Check for updates")
            }
        }
        tableView.reloadData()
        print(notificationIsBlocked)
    }

    override func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return foundBeacons.count
    }

    override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
        return isRanging ? "Ranging Active" : "Ranging Inactive"
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "BeaconCell", for: indexPath) as! BeaconCell
        let beacon = foundBeacons[indexPath.row]
        cell.idLabel.text = beacon.uuid.uuidString
        cell.majorLabel.text = "Major: \(beacon.major)"
        cell.minorLabel.text = "Minor: \(beacon.minor)"
        cell.unknown.layer.cornerRadius = cell.unknown.bounds.width / 2.0
        cell.far.layer.cornerRadius = cell.far.bounds.width / 2.0
        cell.near.layer.cornerRadius = cell.near.bounds.width / 2.0
        cell.immediate.layer.cornerRadius = cell.immediate.bounds.width / 2.0
        switch beacon.proximity {
        case .far:
            cell.unknown.isHidden = false
            cell.far.isHidden = false
            cell.near.isHidden = true
            cell.immediate.isHidden = true
        case .near:
            cell.unknown.isHidden = false
            cell.far.isHidden = false
            cell.near.isHidden = false
            cell.immediate.isHidden = true
        case .immediate:
            cell.unknown.isHidden = false
            cell.far.isHidden = false
            cell.near.isHidden = false
            cell.immediate.isHidden = false
        case .unknown:
            cell.unknown.isHidden = false
            cell.far.isHidden = true
            cell.near.isHidden = true
            cell.immediate.isHidden = true
        @unknown default:
            print("Error - unknown proximity case.")
        }
        return cell
    }

    func postNotification() {
    let content = UNMutableNotificationContent()
        content.title = "Welcome to Rantmedia!"
        content.body = "Enjoy your stay!"
        content.sound = UNNotificationSound.default
        let request = UNNotificationRequest(identifier: "EntryNotification", content: content, trigger: nil)
        UNUserNotificationCenter.current().add(request) { error in
            if let error = error {
                print("Error: \(error.localizedDescription)")
            }
        }
    }

    func postNearbyNotification() {
        let content = UNMutableNotificationContent()
        content.title = "You are near!"
        content.body = "Give me a hug!"
        content.sound = UNNotificationSound.default
        let request = UNNotificationRequest(identifier: "NearbyNotification", content: content, trigger: nil)
        UNUserNotificationCenter.current().add(request) { error in
            if let error = error {
                print("Error: \(error.localizedDescription)")
            }
        }
    }

    @objc func unblockShowingNotification() {
        notificationIsBlocked = false
    }

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

   

Hacking with Swift is sponsored by Paw

SPONSORED Use Paw to build, test and describe web APIs. Paw has a lightning fast native macOS interface to compose requests, collaborate in real-time on API specs, and generate client code for your applications. You can import and export API definitions.

Discover Paw for Mac

Sponsor Hacking with Swift and reach the world's largest Swift community!

Reply to this topic…

You need to create an account or log in to reply.

All interactions here are governed by our code of conduct.

 
Unknown user

Not logged in

Log in
 

Link copied to your pasteboard.