I want to check every time the user passes a store from an array of stores, I have more than 20 stores so I wrote a function that finds the 20 closest stores to the user’s location and monitors them. The list being updated on locationManager: didUpdateLocations
, I also replace the old 20 monitored regions with the new 20 closest stores location.
The problem is that locationManager: didEnterRegion
isn’t being called regularly when the user enters a region.
I’ve also noticed that when I check the locationManager.monitoredRegions
NSSet
the regions there are wrong for some reason (I checked it with if
sentence so maybe they are correct and just shorter?).
If someone could check my code and maybe notice something I done wrong, It’ll really help me!
My code:
monitorLocationViewController.m(scroll to see the full code):
-(void)getStoresArrays:(NSNotification*)notification
{
//Fetching "allStoresArray"(NSArray of all the stores sent from another class using NSNotificationCenter) and adding it to "allStores"(NSMutableArray)
NSDictionary *storesCategoriesArrays=[notification userInfo];
self.allStores=[storesCategoriesArrays objectForKey:@"allStoresArray"];
//Calling "locationChangeHandler" for monitoring
[self locationChangeHandler];
}
#pragma mark - CLLocationDelegate methods
//Being called when user's location updated
-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations
{
//If "allStores"(NSMutableArray) isn't nil - calling "locationChangeHandler" to update monitoring
if (self.allStores!=nil) {
[self locationChangeHandler];
}
}
//Being called when user enters a monitored region
-(void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region
{
NSLog(@"Entered");
}
#pragma mark - Closest stores sorting methods
//Sorting closest stores to the user's location and adding the 20 closest store to "twentyClosestStores"(NSMutableArray)
-(void)sortClosestStores
{
//Sorting "allStores"(NSMutableArray) from the closest "Store" to the furthest.
[self.allStores sortUsingComparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2) {
//Creating "location1"(CLLocation) and "location2"(CLLocation) and initializing each with "obj1"(id) and "obj2"(id) coordinates
CLLocation *location1=[[CLLocation alloc] initWithLatitude:((Store*)obj1).geoPoint.latitude longitude:((Store*)obj1).geoPoint.longitude];
CLLocation *location2=[[CLLocation alloc] initWithLatitude:((Store*)obj2).geoPoint.latitude longitude:((Store*)obj2).geoPoint.longitude];
//Creating "dist1"(float) and setting its value to the distance between "location1"(CLLocation) and the user's location
float dist1 =[location1 distanceFromLocation:self.locationManager.location];
//Creating "dist2"(float) and setting its value to the distance between "location2"(CLLocation) and the user's location
float dist2 = [location2 distanceFromLocation:self.locationManager.location];
//If the distances are equal - the order will stay the same
if (dist1 == dist2) {
return NSOrderedSame;
}
else if (dist1 < dist2) { //If "dist1"(float) is smaller than "dist2"(float) - "dist2"(float) will be before "dist1" in the array
return NSOrderedAscending;
}
else { //else - "dist2"(float) will be before "dist1" in the array
return NSOrderedDescending;
}
}];
//If "twentyClosestStores"(NSMutableArray) is nil
if (self.twentyClosestStores==nil) {
//Initializing "twentyClosestStores"(NSMutableArray)
self.twentyClosestStores=[NSMutableArray array];
}
//If "previousTwentyStores"(NSMutableArray) is nil
if (self.previousTwentyStores==nil) {
//Initializing "previousTwentyStores"(NSMutableArray)
self.previousTwentyStores=[NSMutableArray array];
}
//Setting "previousTwentyStores"(NSMutableArray) to "twentyClosestStores"(NSMutableArray)
self.previousTwentyStores=self.twentyClosestStores;
//Cleaning (reInitializing) "twentyClosestStores"(NSMutableArray)
self.twentyClosestStores=[NSMutableArray array];
//Adding indexes 0-19 of "allStores"(NSMutableArray) (20 closest stores to the user's current location) to "twentyClosestStores"(NSMutableArray)
for (int i = 0; i < 20; i++) {
[self.twentyClosestStores addObject:[self.allStores objectAtIndex:i]];
}
}
#pragma mark - Start/stop monitoring methods
//For updating monitoring
-(void)locationChangeHandler
{
//If "allStores"(NSMutableArray) isn't nil
if (self.allStores!=nil) {
//Finding the 20 closest stores to he user's location and adding it to "twentyClosestStores"(NSMutableArray)
[self sortClosestStores];
//Stop monitoring "previousTwentyStores"(NSMutableArray) (20 closest stores before user's location updated)
[self stopMonitoringStores];
//Start monitoring "twentyClosestStores"(NSMutableArray)
[self startMonitoringClosestStores];
}
}
//Start monitoring "twentyClosestStores"(NSMutableArray)
-(void)startMonitoringClosestStores
{
//If monitoring isn't availible for "CLCircularRegion"
if (![CLLocationManager isMonitoringAvailableForClass:[CLCircularRegion class]]) {
NSLog(@"Monitoring is not available for CLCircularRegion class");
}
//Run on all "twentyClosestStores"(NSMutableArray)'s objects
for (Store *currentStore in self.twentyClosestStores) {
//Creating "region"(CLCircularRegion) and setting it to "currentStore"(Store)'s circular region
CLCircularRegion *region=[currentStore createCircularRegion];
//Start monitoring "region"(CLCircularRegion)
[self.locationManager startMonitoringForRegion:region];
}
}
//Stop monitoring "previousTwentyStores"(NSMutableArray) (20 closest stores before user's location updated)
-(void)stopMonitoringStores
{
//Run on all "previousTwentyStores"(NSMutableArray)'s objects
for (Store *currentStore in self.previousTwentyStores) {
//Creating "region"(CLCircularRegion) and setting it to "currentStore"(Store)'s circular region
CLCircularRegion *region=[currentStore createCircularRegion];
//Stop monitoring "region"(CLCircularRegion)
[self.locationManager stopMonitoringForRegion:region];
}
}
//Finding a store for region
-(Store*)storeForRegion:(CLCircularRegion*)region
{
//Creating "latitude"(CGFloat) and "longtitude"(CGFloat) and setting it to "region"(CLCircularRegion)'s center.latitude/longtitude
CGFloat latitude=region.center.latitude;
CGFloat longtitude=region.center.longitude;
//Run on all "allStores"(NSMutableArray)'s objects
for (Store *currentStore in self.allStores) {
//If "currentStore"(Store)'s latitude and longtitude is equal to "latitude"(CGFloat) and longtitude(CGFloat)
if (currentStore.geoPoint.latitude==latitude&¤tStore.geoPoint.longitude==longtitude) {
//Returning "currentStore"(Store)
return currentStore;
}
}
//Store not found - returning nil
NSLog(@"No store found for this region: %@",[region description]);
return nil;
}
Store.m:
//Creating and returning a "CLCircularRegion" object of the store
-(CLCircularRegion*)createCircularRegion
{
//Creating "region"(CLCircularRegion) and initializing it with current store information (self) and radios of 200m
CLCircularRegion *region=[[CLCircularRegion alloc] initWithCenter:self.geoPoint radius:200 identifier:self.identifier];
//Setting "region"(CLCircularRegion)'s notifyOnEntry to YES
region.notifyOnEntry=YES;
//Returning "region"(CLCircularRegion)
return region;
}
Note: The delegate methods are being called, even didEnterRegion:
but not always for some reason.
Solved the problem:
(I've decided not to use region monitoring and to do it myself)
-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations
{
[self checkIfNearStore]; //Not being called in background
}
-(void)checkIfNearStore
{
for (Store *currentStore in self.allStores) {
if ([currentStore.circularRegion containsCoordinate:self.locationManager.location.coordinate]&¤tStore.alreadySendNotification==NO) {
NSLog(@"Entered: %@",[[self storeForRegion:currentStore.circularRegion] address]);
currentStore.alreadySendNotification=YES;
[self.storesAlreadySentNotifications addObject:currentStore];
}
}
for (Store *currentStore in self.storesAlreadySentNotifications) {
if (![currentStore.circularRegion containsCoordinate:self.locationManager.location.coordinate]) {
currentStore.alreadySendNotification=NO;
}
}
}
Thank you very much!