Ошибка мониторинга региона на iOS 7 - несколько уведомлений одновременно
Я занимаюсь мониторингом региона на iOS около 2 месяцев. Недавно мы обнаружили сбой, при котором все области в пределах определенного радиуса (около 4,7 км или 4700 метров) сработали одновременно. Текущее местоположение устройства даже не близко ни к одному из регионов. Я не уверен, что могло вызвать это событие. Я искал в StackOverFlow, на форумах Apple Developer и т. Д., Я не нашел подобной проблемы с тем, с чем сталкиваюсь.
В приложении, которое я разрабатываю, мы отслеживаем 8 регионов в городе (Куала-Лумпур). В одном случае мой коллега узнал, что на его телефоне одновременно сработало уведомление 4 регионов. Ниже карта, показывающая его местоположение, все контролируемые регионы, потенциальный радиус, который вызвал 4 уведомления региона.
- Зеленый маркер - это местоположение устройства при получении уведомления.
- Синий круг - это потенциальный радиус устройства (около 4700 метров), который охватывает 4 области, которые отправляют уведомление на устройство.
- Красный круг - это радиус для каждого региона.
- На карте есть еще 2 региона, которые никогда не отправляют уведомления (никогда не закрываются синим кружком)
Снимок экрана с запущенными уведомлениями:
Вот мой код для менеджера местоположения:-
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];
}
}
Примечание. Эта проблема возникает не каждый раз, а только время от времени. 4700 метров - это радиус, с которым я вышел после анализа местоположения 4 сработавших регионов. Я не уверен, что это сбой в моем коде, на iOS или есть проблема с местной телефонной компанией в моей стране. В последней версии приложения я настраиваю distanceFiter на 10, и мы тестируем его прямо сейчас, чтобы посмотреть, решит ли это проблему.
//locationManager.distanceFilter = kCLDistanceFilterNone;
locationManager.distanceFilter = 10;
Метод didEnterRegion никогда не возвращает местоположение пользователя, я не могу отфильтровать потенциально плохое местоположение с большим радиусом, как в примере, который я показываю выше. Что я могу сделать, чтобы решить этот глюк?
Любой разработчик, который сталкивается с подобной проблемой, пожалуйста, поделитесь своим опытом и решением этой проблемы (если есть). Благодарю.
3 ответа
Я нашел исправление для этой странной ошибки. Мы тестировали более 1 недели и до сих пор не увидели ту же ошибку снова. Вот решение:-
-(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:. Если он внутри, то сработает только локальное уведомление.
Подробное описание этой ошибки и способы ее устранения вы можете найти в: http://mobileoop.com/ios-region-monitoring-and-location-manager
ЗДЕСЬ - это полностью проверенное рабочее решение / исправление ошибки с гео-фехтованием на iOS!
Это и решит проблему получения неправильных нескольких событий на коммутаторе сети, таких как Wi-Fi к мобильным данным, и наоборот.. также это даст вам точные результаты / уведомления с этой логикой / исправлением
В принципе, LocationManager
должно быть взято как singleton, а distanceFilter должен быть kCLDistanceFilterNone
и желаемая точность должна быть 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;
}
}
Это старый вопрос, но позвольте мне поделиться своим опытом с Region Monitoring в отношении неточных вызовов didExit- didEnterRegion:
Стоит отметить, что если на устройстве отключен Wi-Fi, надежность геозоны на устройствах iOS сильно ухудшается.
В случае спрашивающего, вы не должны просто проверить .coordinate
последнего места для отладки, но также .horizontalAccuracy
СЛОЖЕНИЯ. Это может показать очень низкую точность (большая возможная область), если Wi-Fi и GPS отключены. В случае, если вы также используете отслеживание местоположения GPS, принятый ответ на самом деле является довольно хорошим ответом. Если нет, то.horticalAcraracy может быть очень большим числом. Мониторинг региона (по моему опыту), похоже, не слушает обновления GPS, поэтому вы не можете повысить его надежность таким образом. Но с включенным WiFi можно. В городской местности это.
Без Wi-Fi и GPS-трюка, как в случае принятого ответа, все устройство имеет местоположение сотовой вышки. Я обнаружил, что didExitRegion и didEnterRegion могут быть ненадежными, если все устройство должно получить свое местоположение, являются вышками сотовой связи. Это потому, что точность определения местоположения сотовой вышки, конечно, намного ниже, чем Wi-Fi. В этом случае низкая точность (большая возможная область) может означать, что она находится в любой из четырех областей спрашивающего. Он может даже вызываться несколько раз для одного региона, потому что он может внезапно подумать (на основе вышек сотовой связи), что он находится на расстоянии одного километра, а затем понять, что это не так, что может привести к тому, что didEnterRegion будет вызываться несколько раз.
То же самое касается didExitRegion. Если точность действительно низкая, система не может быть уверена, что вы покинули регион на расстоянии нескольких километров (что является одной из проблем комментаторов)
Поэтому, если вы не уверены, что ваши пользователи используют ваше приложение с постоянно включенным Wi-Fi (что вы не можете проверить или убедиться в 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 всегда включен, сделайте!! Это делает его намного надежнее.