ioscllocationmanagercllocationgeofencingclregion

Location Manager didEnterRegion and didExitRegion methods are not called


I have been doing some iOS development for a couple of months and recently I am developing a bus app.

I am currently mimicking the bus' movements and set up multiple annotations on the bus stops. For test purposes, I have setup just one bus stop and am trying to monitor when the bus has entered this region and exited as well.

Strangely, my didStartMonitoringForRegion method is called perfectly but neither the didEnterRegion nor didExitRegion methods are called. Along with this, no errors are called whatsoever. Every time I run the program, the bus pretty much passes the stop without prompting me so.

Could someone explain to me why this is happening and how to resolve it?

//  ViewController.swift
//  Parta Service
//  Created by Emil Shirima on 8/24/15.
//  Copyright (c) 2015 Emil Shirima. All rights reserved.

import UIKit
import MapKit
import CoreLocation

class ViewController: UIViewController, MKMapViewDelegate,   CLLocationManagerDelegate {

@IBOutlet weak var mapView: MKMapView!

let locationManager = CLLocationManager()
var allBusAnnotations = [MKPointAnnotation]()
var summitEastBusStations = [CLLocationCoordinate2D]()
var busStopNames = ["Dix Stadium", "Risman Plaza", "Terrace Drive", "Terrace Drive 2","C-Midway","Theatre Dr.","East Main Street","South Lincoln"]
var radius = 250 as CLLocationDistance

// 0.02 is the best zoom in factor
var mapZoomInFactor : Double = 0.02

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.

    self.getBusStop()
    self.locationManager.delegate = self
    // gets the exact location of the user
    self.locationManager.desiredAccuracy = kCLLocationAccuracyBest
    // gets the user's location only when the app is in use and not background
    self.locationManager.requestWhenInUseAuthorization()
    self.locationManager.startUpdatingLocation()

    self.mapView.showsUserLocation = true
    self.setBusStopAnnotations(summitEastBusStations)

    self.canDeviceSupportAppBackgroundRefresh()

}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}

func locationManager(manager: CLLocationManager!, didUpdateLocations locations: [AnyObject]!) {

    // sends the latitude and longitude to the Apple Servers then returns the address
    CLGeocoder().reverseGeocodeLocation(manager.location, completionHandler: { (placeMarks: [AnyObject]!, error: NSError!) -> Void in

        if error != nil
        {
            println("Reverse Geocode Failed: " + error.localizedDescription)
            println("The real reason is " + error.localizedFailureReason!)
            return
        }

        if placeMarks.count > 0
        {
            // gets the most updated location
            let pm = placeMarks.last as! CLPlacemark

            let centre = CLLocationCoordinate2D(latitude: manager.location.coordinate.latitude, longitude: manager.location.coordinate.longitude)

            // draws a circle in which the map will zoom to
            let region = MKCoordinateRegion(center: centre, span: MKCoordinateSpan(latitudeDelta: self.mapZoomInFactor, longitudeDelta: self.mapZoomInFactor))

            self.mapView.setRegion(region, animated: true)

            self.displayLocationInfo(pm)

            self.geoFencing()
        }
    })
}

func displayLocationInfo(placemark: CLPlacemark)
{

    println("Current Location:")
    println(placemark.name)
}

func locationManager(manager: CLLocationManager!, didFailWithError error: NSError!) {
    println("Location Manager Failed: " + error.localizedDescription)
}

func locationManager(manager: CLLocationManager!, didStartMonitoringForRegion region: CLRegion!) {
    println("The monitored regions are: \(locationManager.monitoredRegions)")
}

func locationManager(manager: CLLocationManager!, monitoringDidFailForRegion region: CLRegion!, withError error: NSError!) {
    println("Failed to monitor the stated region")
}

func locationManager(manager: CLLocationManager!, didEnterRegion region: CLRegion!) {
    println("The bus has entered the region")

    createAlert("Region Entry", alertMessage: "You have entered the specified region", alertCancelTitle: "OK")
}

func locationManager(manager: CLLocationManager!, didExitRegion region: CLRegion!) {
    println("The bus has left the region")

    createAlert("Region Exit", alertMessage: "You have exited the specified region", alertCancelTitle: "OK")
}

//TODO:Implement GeoFencing in order to know when the bus is at a station or not.
func geoFencing()
{
    locationManager.requestAlwaysAuthorization()
    let rismanPlaza = CLLocationCoordinate2D(latitude: 41.1469492, longitude: -81.344068)

    var currentBusStop = CLLocation(latitude: rismanPlaza.latitude, longitude: rismanPlaza.longitude)
    addRadiusCircle(currentBusStop)

    let busStopRegion = CLCircularRegion(center: CLLocationCoordinate2D(latitude: rismanPlaza.latitude, longitude: rismanPlaza.longitude), radius: radius, identifier: busStopNames[1])

    if radius > self.locationManager.maximumRegionMonitoringDistance
    {
        radius = self.locationManager.maximumRegionMonitoringDistance
    }

    if CLLocationManager.isMonitoringAvailableForClass(CLCircularRegion)
    {
        locationManager.startMonitoringForRegion(busStopRegion)
    }
    else
    {
        //TODO: Create an alert telling the user that region tracking for this area is not available
        println("Monitoring is not available for this region")
    }

}

// creates the radius around the specified location
func addRadiusCircle(location: CLLocation)
{
    self.mapView.delegate = self
    var circle = MKCircle(centerCoordinate: location.coordinate, radius: radius)
    self.mapView.addOverlay(circle)
}

// performs the actual circle colouring
func mapView(mapView: MKMapView!, rendererForOverlay overlay: MKOverlay!) -> MKOverlayRenderer!
{
    if overlay is MKCircle
    {
        var circle = MKCircleRenderer(overlay: overlay)
        circle.strokeColor = UIColor.redColor()
        circle.fillColor = UIColor(red: 255, green: 0, blue: 0, alpha: 0.1)
        circle.lineWidth = 1
        return circle
    }
    else
    {
        return nil
    }
}

// removes the drawn circles from the map
func removeCircleLayers()
{
    var layers = mapView.overlays
    mapView.removeOverlays(layers)
}

func canDeviceSupportAppBackgroundRefresh()
{
    if UIApplication.sharedApplication().backgroundRefreshStatus == UIBackgroundRefreshStatus.Available
    {
        createAlert("Background Refresh Status", alertMessage: "Background Updates are enable for the App", alertCancelTitle: "OK")
    }
    else if UIApplication.sharedApplication().backgroundRefreshStatus == UIBackgroundRefreshStatus.Denied
    {
        createAlert("Background Refresh Status", alertMessage: "Background Updates are disabled by the user for the App", alertCancelTitle: "OK")
    }
    else if UIApplication.sharedApplication().backgroundRefreshStatus == UIBackgroundRefreshStatus.Restricted
    {
        createAlert("Background Refresh Status", alertMessage: "Background Updates are restricted and the user can't do anything App", alertCancelTitle: "OK")
    }

}

func createAlert(alertTitle: String, alertMessage: String, alertCancelTitle: String)
{
    let alert = UIAlertView(title: alertTitle, message: alertMessage, delegate: self, cancelButtonTitle: alertCancelTitle)
    alert.show()
}

}

Solution

  • I ended up using the CLRegion.containsCoordinate(location.coordinate) method instead. It works pretty much the same way.

    Once the object has entered my set region, it returns true and from here I can know when it's entered and exited the region.