Ryota
Ryota

Reputation: 11

swift, detect ibeacons on the background and send notifications when in range

Hello I'm new at IBeacons and beginner on swift and I'm trying to make a small app that detects Ibeacon on the background of the app and send a notification when the Ibeacon is in range I manage to do so but only when I walk while the app is open I could not make it work and search for Ibeacons on the background even though I gave the app access to take the location on the background by using

 if (CLLocationManager.authorizationStatus() != CLAuthorizationStatus.authorizedAlways) {
        locationManager.requestAlwaysAuthorization();
    }

this is my main problem, I have a side problem too that the app does not save the person name if the app is closed and opened again the app will forget the name. Here is my code I'd really appreciate your help so much also please if you have any references to learn more about IBeacons applications I'd appreciate it

   import UIKit
   import CoreLocation
   import UserNotifications

   class ViewController: UIViewController, CLLocationManagerDelegate {
   @IBOutlet weak var field: UITextField!
   @IBOutlet weak var textLbl : UILabel!
   var inRoom = false
   var name = ""
   var sendYet = false ;

func sendHiNoti()
{
    name = field.text! ;
    let content = UNMutableNotificationContent()
    content.title = "Heloo "+name
    content.subtitle = "Welcome to your Home"
    content.body = "this messaage to welcome you in Home"
    content.badge = 1
    content.sound = UNNotificationSound(named: UNNotificationSoundName(rawValue: "quiteimpressed.mp3"))
    let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 2, repeats: false)
    let request = UNNotificationRequest(identifier: "azanSoon", content: content, trigger: trigger)
    UNUserNotificationCenter.current().removeAllPendingNotificationRequests()
    UNUserNotificationCenter.current().add(request) {(error) in
        if let error = error {
            print("error: \(error)")
        }
    }
}

func sendByeNoti()
{
    name = field.text! ;
    let content = UNMutableNotificationContent()
    content.title = "OH"+name
    content.subtitle = "You are going out already ??"
    content.body = "Take care of your self"
    content.badge = 1
    content.sound = UNNotificationSound(named: UNNotificationSoundName(rawValue: "quiteimpressed.mp3"))
    let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 2, repeats: false)
    let request = UNNotificationRequest(identifier: "azanSoon", content: content, trigger: trigger)
    UNUserNotificationCenter.current().removeAllPendingNotificationRequests()
    UNUserNotificationCenter.current().add(request) {(error) in
        if let error = error {
            print("error: \(error)")
        }
    }
}

@IBAction func getText(){
    name = field.text!
    let alert = UIAlertController(title: "Your Name is", message: name, preferredStyle: UIAlertController.Style.alert)
    alert.addAction(UIAlertAction(title: "Ok", style: UIAlertAction.Style.default, handler: nil))
    self.present(alert, animated: true, completion: nil)

}


var locationManager = CLLocationManager()

override func viewDidLoad() {

    super.viewDidLoad()

    locationManager.delegate = self


    UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge], completionHandler: {didAllow, error in})

    let uuid = UUID(uuidString: "E2C56DB5-DFFB-48D2-B060-D0F5A71096E0")!
    let beaconRegion = CLBeaconRegion(proximityUUID: uuid, major: 444, minor: 333, identifier: "abcdefac005b")

    if (CLLocationManager.authorizationStatus() != CLAuthorizationStatus.authorizedAlways) {
        locationManager.requestAlwaysAuthorization();
    }
    locationManager.startRangingBeacons(in: beaconRegion)
}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()

}



func locationManager(_ manager: CLLocationManager, didRangeBeacons beacons: [CLBeacon], in region: CLBeaconRegion) {
    print(beacons)
    if(beacons.count > 0){
    if(!sendYet){
    if beacons[0].proximity.rawValue < 2 {
        textLbl.text = "In the room"
        sendHiNoti()
        sendYet = true ;
    }
    }
   else if beacons[0].proximity.rawValue >= 3 {
        textLbl.text = "Outside the room"
        sendByeNoti()
        sendYet = false ;
    }
}
}
}

Upvotes: 1

Views: 2668

Answers (1)

davidgyoung
davidgyoung

Reputation: 64941

The code shown uses beacon ranging locationManager.startRangingBeacons(in: beaconRegion) which is generally not supported in the background for more than 10 seconds after transition between foreground and background.

The locationManager.requestAlwaysAuthorization() will only unlock the ability to use beacon monitoring in the background. Beacon monitoring gives you a single call when beacons either first appear (didEnter(region: region)) or all disappear(didExit(region: region)). This is the only beacon API which works in the background under normal circumstances.

It is possible to do beacon ranging in the background for longer than 10 seconds using two techniques:

  1. You can get 180 seconds of background ranging after the app transitions to the background by starting a background task as described in my blog post here.

  2. You can also tell iOS that you are a location app to unlock unlimited background beacon ranging. You must first implement the solution in part 1. Then, in your Info.plist, declare:

    <key>UIBackgroundModes</key>
    <array>
        <string>location</string>
    </array>
    

    Finally, in your code run locationManager.startUpdatingLocation(). This will cause your app to receive regular updates of its latitude/longitude, but as a side effect allows you background task from step 1 to run forever, allowing ranging to continue forever in the background.

If you choose to use option 2, beware that it will be more difficult to get your app approved for sale in the AppStore. You must convince Apple reviewers that your app is a legitimate location app (like Waze or Apple Maps) and the user is aware that your app is always running in the background. If you do not convince them of this, they will reject your app.

Separately, it is simple to save off values to persistent storage so they are retained across app restarts. Just use NSUserDefaults like this:

 // save off name when user fills it in
 UserDefaults.standard.set(name, forKey: "name")

 // load back name on app restart
 name = UserDefaults.standard.string(forKey: "name") 

Upvotes: 2

Related Questions