How to monitor more than 20 regions?
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
Need to get more than 20 notification for Region Monitoring
- Have an NSMutableArray with all the regions you want to monitor +20.
- Listen to significant location updates.
- When you get a location update, if the NSMutableArray of all your regions is more than 20 then stop monitoring all regions been monitored and calculate the 20 nearest regions using the harvesine formula:
Harvesine - Objective C
Harvensine - Swift
That will give you the distance between the two locations. After that you could compare that distance with the region radius to know if is inside the region.
Note: This distance will be in kilometers if your radius is on meters then just multiply the haversine method result with 1000 so that it's converted to meters.
- Start monitoring the result list of the 20 nearest regions.
This will allow you to always monitor the 20 nearest regions based on your location. Been able to monitor more than 20 since it will change the monitoring regions always to the 20 nearest regions.
Does the location manager's limit of 20 regions mean 20 total geofence AND beacon regions?
Yes, the 20 region limit is the maximum that CoreLocation lets you monitor for both CLBeaconRegion
s and CLCircularRegion
s (geofences) combined. When iOS 7 added beacon support, beacon regions inherited this same limitation for geofences because of the ways the APIs were defined. And as you suspected, the limit applies to any type of region you want to monitor. So you can monitor 10 CLBeaconRegion
s and 10 CLCircularRegion
s but no more than 20 combined of each type.
how to startMonitoringForRegion for more then 20 region
You just can't monitor more than 20 regions. Maybe you could stop monitoring some regions depending on user location and start some others (depending on your use case).
Discussion You must call this method once for each region you want to
monitor. If an existing region with the same identifier is already
being monitored by the application, the old region is replaced by the
new one. The regions you add using this method are shared by all
location manager objects in your application and stored in the
monitoredRegions property.Region events are delivered to the locationManager:didEnterRegion: and
locationManager:didExitRegion: methods of your delegate. If there is
an error, the location manager calls the
locationManager:monitoringDidFailForRegion:withError: method of your
delegate instead.An app can register up to 20 regions at a time. In order to report
region changes in a timely manner, the region monitoring service
requires network connectivity.In iOS 6, regions with a radius between 1 and 400 meters work better
on iPhone 4S or later devices. (In iOS 5, regions with a radius
between 1 and 150 meters work better on iPhone 4S and later devices.)
On these devices, an app can expect to receive the appropriate region
entered or region exited notification within 3 to 5 minutes on
average, if not sooner.
https://developer.apple.com/library/ios/documentation/CoreLocation/Reference/CLLocationManager_Class/CLLocationManager/CLLocationManager.html#//apple_ref/occ/instm/CLLocationManager/startMonitoringForRegion:
iOS region monitoring, to monitor more than 20 locations
This is actually Apple's suggestion Core Location Programming Guide:
To work around this limit, consider registering only those regions in
the user’s immediate vicinity. As the user’s location changes, you can
remove regions that are now farther way and add regions coming up on
the user’s path.
However, it's not clear how much time you get when didEnterRegion: is called in the background, so it's not clear if you have time to make a server call if running the background. The "significant-change location service" information says:
If you leave the significant-change location service running and your
iOS app is subsequently suspended or terminated, the service
automatically wakes up your app when new location data arrives. At
wake-up time, the app is put into the background and you are given a
small amount of time (around 10 seconds) to manually restart location
services and process the location data.... Because your app is in the
background, it must do minimal work and avoid any tasks (such as
querying the network) that might prevent it from returning before the
allocated time expires. If it does not, your app will be terminated.
If an iOS app needs more time to process the location data, it can
request more background execution time using the
beginBackgroundTaskWithName:expirationHandler:
method of the
UIApplication class.
You could try the combination of region monitoring, making a server call in didEnterEter region and then calling beginBackgroundTaskWithName:expirationHandler:
to make sure you have enough time. The combination of region monitoring + server calls + background processing is going to hit battery life, though.
EDIT: You could also create "mega regions" of a large area that contain many smaller regions. When the user enters those mega regions, set up and add all the smaller regions you are interested in, and when they exit, remove them.
Related Topics
Supporting a Nsmanagedobject Fetchrequest() Class Method in iOS 9 and 10
Show Bounding Box While Detecting Object Using Arkit 2
iOS Healthkit How to Save Heart Rate (Bpm) Values? Swift
Transit Mkdirectionsrequest Produces Null Error Error Domain=Mkerrordomain Code=5 "(Null)"
Set the Center of a Uibutton Programmatically - Swift
Resize Inputaccessoryview Dynamically in iOS 8
Xcode 12.5 Missing Entitlement Com.Apple.Developer.Associated-Appclip-App-Identifiers
Cannot Invoke Initializer for Type Unsafemutablepointer<Uint8>
How to Add .Plist File to All Targets in Xcode
How to Bridge Nsnumber to Float in JSON Parsing
(Swift Spritekit) Rotate Sprite in the Direction of Touch
Uiview Rounded Corner - Swift 2.0
Swiftui Tabbedview Only Shows First Tab's Content
Issue with Observing Wkwebview Url Changes via JavaScript Events