UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

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)
    }
}

2      

TAKE YOUR SKILLS TO THE NEXT LEVEL If you like Hacking with Swift, you'll love Hacking with Swift+ – it's my premium service where you can learn advanced Swift and SwiftUI, functional programming, algorithms, and more. Plus it comes with stacks of benefits, including monthly live streams, downloadable projects, a 20% discount on all books, and free gifts!

Find out more

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.