7

I'm working on an app in which there are 66 annotations. These annotations are centers of regions and whenever user enters a region, a notification appears, but that works only for first 20 of them because there's a limited number on monitoring regoins. My problem is that I don't know how to monitor more than 20 regions. Could anyone help?

andre
  • 773
  • 1
  • 6
  • 16

3 Answers3

5

There is no way to monitor more than 20 regions using Apples API.

You have to update the actively monitored regions to the nearest 20 regions.

Whenever you enter/leave a region:

  • Check entered location
  • Stop monitoring all regions
  • Start monitoring the nearest 19 regions (distance to entered location) plus the entered one.

If the result is not satisfying, you might also want to monitor significant location changes to have the chance to update the monitored regions every ~500 meters while not draining too much battery.

shallowThought
  • 19,212
  • 9
  • 65
  • 112
  • how can I define nearest regions and start monitoring just them – andre Jul 06 '17 at 11:50
  • 1
    You have the location (coordinate) of the entered region. Also your regions to monitor have a coordinate. Sort them by enteredRegionsCoordinate.distanceTo(currentyChecktRegionsCoordinate). If this does not help, post a new question with your actual issue, what you have tried and what the actuall issue is. – shallowThought Jul 06 '17 at 11:53
  • @andre, if this helped you, you should mark it as your answer. – Daniel Jul 29 '17 at 17:17
5

set currentLocation from your didUpdateLocations

var currentLocation : CLLocation?{
    didSet{
        evaluateClosestRegions()
    }
}

var allRegions : [CLRegion] = [] // Fill all your regions

Now calculate and find the closest regions to your current location and only track those.

func evaluateClosestRegions() {

    var allDistance : [Double] = []

    //Calulate distance of each region's center to currentLocation
    for region in allRegions{
        let circularRegion = region as! CLCircularRegion
        let distance = currentLocation!.distance(from: CLLocation(latitude: circularRegion.center.latitude, longitude: circularRegion.center.longitude))
        allDistance.append(distance)
    }
    // a Array of Tuples
    let distanceOfEachRegionToCurrentLocation = zip(allRegions, allDistance)

    //sort and get 20 closest
    let twentyNearbyRegions = distanceOfEachRegionToCurrentLocation
        .sorted{ tuple1, tuple2 in return tuple1.1 < tuple2.1 }
        .prefix(20)

    // Remove all regions you were tracking before
    for region in locationManager.monitoredRegions{
        locationManager.stopMonitoring(for: region)
    }

    twentyNearbyRegions.forEach{
        locationManager.startMonitoring(for: $0.0)
    }

}

To avoid having the didSet called too many times, I suggest you set the distanceFilter appropriately (not too big so you would catch the region's callbacks too late and not too small so that you won't have redundant code running). Or as this answer suggests, just use startMonitoringSignificantLocationChanges to update your currentLocation

mfaani
  • 33,269
  • 19
  • 164
  • 293
  • where do I put this: var currentLocation : CLLocation?{ didSet{ evaluateClosestRegions() } } – andre Jul 30 '17 at 16:36
  • @andre you should make a property on the class which adopts the `cllocationmanagerdelegate` protocol. Basically the same class you are getting `didUpdateLocation` callback. – mfaani Jul 30 '17 at 16:55
  • i did that but function evaluateClosestRegions is never being called – andre Jul 30 '17 at 17:35
  • @andre are you setting `currentLocation` inside your `didUpdatelocations`? Inside it do something like `guard let location = locations.last else { return } currentLocation = location`. But to be honest I think you first need to do some more basic stuff. Your questions are too basic. Try to to play around with coreLocation a bit more, watch some Youtube videos – mfaani Jul 30 '17 at 17:40
  • I watched some basics of corelocation and now it works. thank you very much – andre Jul 30 '17 at 22:41
  • `Could not cast value of type 'CLBeaconRegion' (0x1b75039c8) to 'CLCircularRegion' (0x1b7503978).` – Jack Mar 22 '18 at 12:51
0

Short and clean solution:

private var locations = [CLLocation]()

private var currentLocation: CLLocation? {
    didSet {
        evaluateClosestRegions()
    }
}

private func distance(from location: CLLocation) -> Double {
    return currentLocation.distance(from: location))
}

private func evaluateClosestRegions() {
    locationManager.monitoredRegions.forEach {
        locationManager.stopMonitoring(for: $0)
    }

    locations.sort {
        distance(from: $0) < distance(from: $1)
    }

    locations.prefix(20).forEach {
        ...
    }
}
Alexander
  • 21
  • 2