iOS 7上的区域监视故障 – 同时显示多个通知

我一直在iOS上开发区域监控大约2个月。 最近我们发现一个小故障,一个半径范围内(大约4.7KM或4700米)的所有地区同时触发。 该设备的当前位置甚至没有接近任何地区。 我不知道是什么引发了这个事件。 我已经通过StackOverFlow,苹果开发者论坛等进行了search,我还没有发现任何类似的问题,我所面临的。

在我正在开发的应用程序中,我们正在监控市内的8个区域(吉隆坡)。 有一次,我的同事发现有4个地区的通知同时在他的电话上触发。 下面是显示他的位置,所有监控区域,触发4区域通知的潜在半径的地图。

iOS区域监控

  • 绿色标记是设备接收通知时的位置。
  • 蓝圈是设备的潜在半径(大约4700米),覆盖了向设备发送通知的4个区域。
  • 红圈是每个区域的半径。
  • 地图上还有另外两个地区永远不会发送通知(从不遮盖蓝色圆圈)

触发通知的屏幕截图

iOS区域监视故障

这是我的位置pipe理器代码

CLLocationManager *locationManager = [LocationTracker sharedLocationManager]; locationManager.delegate = self; locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation; locationManager.distanceFilter = kCLDistanceFilterNone; 

这是我的代码didEnterRegion : –

 -(void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region{ NSString* message = [NSString stringWithFormat:@"Message"]; UIApplicationState state = [[UIApplication sharedApplication] applicationState]; if (state == UIApplicationStateBackground || state == UIApplicationStateInactive) { UILocalNotification *notification = [[UILocalNotification alloc] init]; notification.fireDate = [NSDate date]; NSTimeZone* timezone = [NSTimeZone defaultTimeZone]; notification.timeZone = timezone; notification.alertBody = message; notification.alertAction = @"Show"; notification.soundName = UILocalNotificationDefaultSoundName; [[UIApplication sharedApplication] scheduleLocalNotification:notification]; } } 

注意:这个问题每次都不会发生,只会偶尔发生一次。 我分析了4个触发区域的位置后,出来的半径是4700米。 我不确定这是否是我的代码上的一个小问题,在iOS上,或者我的国家的本地电话公司存在问题。 在最新版本的应用程序中,我正在将distanceFiter调整为10,现在我们正在testing它是否能解决问题。

 //locationManager.distanceFilter = kCLDistanceFilterNone; locationManager.distanceFilter = 10; 

方法didEnterRegion永远不会返回用户的位置,我无法像上面显示的示例那样筛选出大半径的潜在不良位置。 我能做些什么来解决这个问题?

任何面临类似问题的开发者,请出面分享您的经验和解决scheme来解决这个问题(如果有的话)。 谢谢。

我find了一个修复这个奇怪的错误。 我们已经testing了一个多星期,到目前为止我们还没有看到同样的错误。 这是解决办法:

 -(void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region{ NSLog(@"didEnterRegion"); CLLocation * lastLocation = [manager location]; BOOL doesItContainMyPoint; if(lastLocation==nil) doesItContainMyPoint = NO; else{ CLLocationCoordinate2D theLocationCoordinate = lastLocation.coordinate; CLCircularRegion * theRegion = (CLCircularRegion*)region; doesItContainMyPoint = [theRegion containsCoordinate:theLocationCoordinate]; } if(doesItContainMyPoint){ NSString* message = [NSString stringWithFormat:@"You are now in this region:%@",region.identifier]; UIApplicationState state = [[UIApplication sharedApplication] applicationState]; if (state == UIApplicationStateBackground || state == UIApplicationStateInactive) { UILocalNotification *notification = [[UILocalNotification alloc] init]; notification.fireDate = [NSDate date]; NSTimeZone* timezone = [NSTimeZone defaultTimeZone]; notification.timeZone = timezone; notification.alertBody = message; notification.alertAction = @"Show"; notification.soundName = UILocalNotificationDefaultSoundName; [[UIApplication sharedApplication] scheduleLocalNotification:notification]; } } } 

我使用CLLocation * lastLocation = [manager location]; 从设备获取最新的位置,并使用此坐标来查看它是否位于使用containsCoordinate:方法的触发区域内。 如果在里面,那么只有本地通知会触发。

有关此错误的详细说明及解决方法,请访问: iOS区域监视和位置pipe理器

这里是充分的certificate工作解决scheme/修复iOS地理围栏毛刺!

它将解决接收错误的多个事件的问题,如WiFi的networking交换机到移动数据,反之亦然,也给你准确的结果/通知与此逻辑/修复

基本上, LocationManager应该被视为单例,distanceFilter应该是kCLDistanceFilterNone而desiredAccuracy应该是kCLLocationAccuracyBestForNavigation (我们已经采取了这个尽可能准确,因为我们需要真实的地理位置监测非常准确的实时地理栅栏进入通知发送给用户)

 - (void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region { CLLocation * lastLocation = [manager location]; BOOL doesItContainMyPoint; if(lastLocation==nil) doesItContainMyPoint = NO; else{ CLLocationCoordinate2D theLocationCoordinate = lastLocation.coordinate; CLCircularRegion * theRegion = (CLCircularRegion*)region; //we need to take new instance of given region for which the iOS triggers entry event..Adding 50.0 meters is needed just to make sure that containsCoordinate method of CLCircularRegion works well.Because there is lag/difference measured in our real time field testing that brought us to this conclusion and it works like a charm.If we do not add this 50.0 meters in the fence for which we are getting this event then there is a chance that containsCoordinate might miss the recent point/coordinate to consider in given region... CLCircularRegion * theCircularRegion = [[CLCircularRegion alloc]initWithCenter:theRegion.center radius:theRegion.radius+50.0 identifier:theRegion.identifier]; doesItContainMyPoint = [theCircularRegion containsCoordinate:theLocationCoordinate]; } if(doesItContainMyPoint){ NSLog(@"ItContainMyPoint"); //trigger local notification... }else{ NSLog(@"ItDoesNotContainMyPoint"); //do not trigger local notification...because it is triggered due to switching network(wifi to mobile data and vice versa) Currently user is not at all in the region for which we are getting event of entry return; } } 

这是一个老问题,但让我分享我的经验与区域监测有关不准确的didExit- didEnterRegion调用:

需要注意的是,如果您在设备上closures了Wifi,则iOS设备上的地理围栏的可靠性会大大降低。

在提问者的情况下,你不应该只检查最后一个位置的.coordinate来debugging,而且还要.horizontalAccuracy CLLocation的.horizontalAccuracy 。 如果wifi和GPSclosures,这可能会显示精度非常低(很大的可能区域)。 如果您也运行GPS位置跟踪,则接受的答案实际上是一个很好的答案。 如果没有,水平精度可能是一个非常大的数字。 区域监听似乎(以我的经验)听GPS的更新,所以你不能以这种方式提高其可靠性。 但启用WiFi可以。 在一个城市地区。

没有Wifi和像接受的答案使用的GPS技巧,所有的设备都是手机塔的位置。 我发现didExitRegion和didEnterRegion可能是不可靠的,如果所有的设备必须得到它的位置,是手机塔。 这是因为手机塔位置的准确性当然远低于wifi。 在这种情况下,低精度(可能大的区域)可能意味着它在提问者的四个区域中的任何一个。 甚至可能被称为多次单个地区,因为它可能突然想到(基于手机塔),它是一公里,然后意识到它不是,这可能会导致didEnterRegion被称为多次。

didExitRegion也是如此。 如果准确度真的很低,系统就不能确定你离开了几公里之外的地区(这是评论者的问题之一)

所以,如果你不确定你的用户使用你的应用程序,一直打开wifi(你不能在iOS上检查或保证),确保你不要使区域太小,或使用接受的答案与GPS( [locationManager startUpdatingLocation] )来确保你真的在那个区域。 在这种情况下,还要检查位置是否准确和足够年轻。 就像是:

 CLLocation * lastLocation = [manager location]; NSTimeInterval locationAge = -[lastLocation.timestamp timeIntervalSinceNow]; if (lastLocation != nil && locationAge < MAX_AGE && self.currentLocation.horizontalAccuracy <= MAX_ACCURACY) { CLLocationCoordinate2D theLocationCoordinate = lastLocation.coordinate; CLCircularRegion * theRegion = (CLCircularRegion*)region; doesItContainMyPoint = [theRegion containsCoordinate:theLocationCoordinate]; } 

但是,如果有任何方法可以说服用户在使用应用程序时始终启用Wifi,那么请执行此操作! 这使得它更可靠。