NEW: Start my new Ultimate Portfolio App course with a free Hacking with Swift+ trial! >>

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 Fernando Olivares

SPONSORED Would you describe yourself as knowledgeable, but struggling when you have to come up with your own code? Fernando Olivares has a new book containing iOS rules you can immediately apply to your coding habits to see dramatic improvements, while also teaching applied programming fundamentals seen in refactored code from published apps.

Try the book!

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

Archived topic

This topic has been closed due to inactivity, so you can't reply. Please create a new topic if you need to.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.