Cody Husek
Cody Husek

Reputation: 73

Swift 3 Beacon Monitoring using CLLocationManager

Here is a shortened version of what I am trying to do with beacon region monitoring using CLLocationManager.

I need to be informed of entry/exit events of multiple (less than 20) regions.

I get consistent location entry events even in the background. I am not getting exit events in the foreground while in the Client View, or in the background.

At one point I did successfully get exit events in the background, but I was not obtaining a variable number of beacon regions to listen to.

Are there any overall convention/logic flaws in this code?

//------------------
/* Client View Controller - Main Profile View */
class ClientVC: UIViewController, ClientVC_Preferences_Protocol, OpenPreferencesProtocol, ClientVCProtocol {

	/* Reference to AppDelegate file where location manager resides */
	let appDelegate = UIApplication.shared.delegate as! AppDelegate
	override func viewDidLoad(){

		// .. Obtain some beacon info - not shown

		for beacon in beacons {
	        /* Create a region for each beacon and start monitoring */
	        var region = CLBeaconRegion(proximityUUID: UUID(uuidString:beacon.UUID)!, identifier: beacon.Identifier)
	        region.notifyEntryStateOnDisplay = true
	        region.notifyOnExit = true
	        region.notifyOnEntry = true
	        self.appDelegate.locationManager.startMonitoring(for: region)
	    }
	}

	/* Protocol function to alert client when exit event occured */
	func alertClient(businessName:String) {
        
        let notification = UILocalNotification()
        notification.alertBody = "Did you leave " + businessName + "?"
        UIApplication.shared.presentLocalNotificationNow(notification)
        
        let alertController = UIAlertController(title: businessName, message: "Did you leave?", preferredStyle: .alert)
        
        let okAction = UIAlertAction(title: "Yes, I Left!", style: UIAlertActionStyle.default) {
            UIAlertAction in
        	// .. Do some firebase work - not shown
        }

        let cancelAction = UIAlertAction(title: "No, I'm Here!", style: UIAlertActionStyle.cancel) {
            UIAlertAction in
            // .. Do some firebase work - not shown
        }
        
        alertController.addAction(okAction)
        alertController.addAction(cancelAction)
        
        self.present(alertController, animated:true, completion:nil)
    }
}

//------------------
/* AppDelegate */

/* Protocol connected to Client View Controller */
protocol ClientVCProtocol{
    func alertClient(businessName:String) /* Displays alert to client when exiting the region */
    func reloadTableView() /* Refreshes table view */
}

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, CLLocationManagerDelegate {

	/* Delegate for ClientVC Protocol - used to call methods */
	var ClientVCDelegate:ClientVCProtocol?

	/* Instantiate location manager */
	var locationManager = CLLocationManager()

	/* Triggered by entering a region */
	func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) {
        print("Enter event " + region.identifier)
        // .. Do some firebase work - not shown
	}

	/* Triggered by exiting a region */
	func locationManager(_ manager: CLLocationManager, didExitRegion region: CLRegion) {

		/* Break set on this line to ensure whether or not this function is called */
		print("Exit Attempt") 

		/* Gets business name associated with region and calls protocol function to ClientVC */
		if let beaconRegion = region as? CLBeaconRegion {
            self.getBusinessName(region: region){
                (result: String) in
                print("Exit event " + region.identifier)
                self.ClientVCDelegate?.alertClient(businessName:result)
            }
        }
	}

	/* Runs when application finishes launching - configures location manager */
	func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {        
        self.fbRef = Database.database().reference()
        UIApplication.shared.registerUserNotificationSettings(
            UIUserNotificationSettings(types: .alert, categories: nil))
        
        /* Not sure how relevent all of these calls are */
        locationManager.delegate = self
        locationManager.desiredAccuracy = kCLLocationAccuracyBest
        locationManager.distanceFilter = 2000
        if #available(iOS 9.0, *) {
            locationManager.allowsBackgroundLocationUpdates = true
        }
        locationManager.startMonitoringSignificantLocationChanges()
        locationManager.pausesLocationUpdatesAutomatically = true
        locationManager.requestAlwaysAuthorization()
      
        return true
    }
}

Upvotes: 1

Views: 727

Answers (1)

davidgyoung
davidgyoung

Reputation: 64941

One problem is that you only set up the regions to monitor in the ViewController, this should really be done in the AppDelegate if you want background entry/exit events to fire.

Understand that CoreLocation keeps track of the regions you are monitoring and will auto-launch your app into the background if the region state changes. It will only fire callbacks, however, if you set up the CLLocationManager, regions, and set the delegate in the AppDelegate's didFinishLaunching method.

Upvotes: 1

Related Questions