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