应用终止时的位置访问,没有互联网 iOS



我正在开发一个iOS应用程序,我的问题如下:

  1. 是否需要在应用程序运行时每次访问用户位置?
  2. 位置不依赖于互联网吗?
  3. 苹果会批准这种应用吗?

关于问题1。

我正在使用这些行来获取用户位置

locationManager.allowsBackgroundLocationUpdates = true
locationManager.startUpdatingLocation()

借助这些,当应用程序处于前台或后台时,我能够获得位置。

当应用程序终止时

locationManager.startMonitoringSignificantLocationChanges()

仅当用户穿越 500 米时,它才有效

然后应用程序变为活动状态并更新位置

2.这些取决于互联网,而我希望每次互联网是否存在时接收位置。

3.也请让我知道这些类型的应用程序将得到苹果的批准与否。

请帮助/建议。

提前谢谢。

你的答案在这里:

问题 1:是否需要在应用程序运行时每次访问用户位置?

答:不,如果您正在使用应用程序并且应用程序处于运行模式。您正在继续更新位置,然后您应该始终检查位置可访问性和活动,如UBER应用程序。

问题2:位置不依赖于互联网吗?

答:取决于应用的功能。您也可以通过互联网获取当前区域,也可以使用GPS获取当前区域。

问题3:苹果会批准这种应用吗?

答:是的,如果您遵循适当的指导方针,那么苹果肯定会接受它。但是,请确保在获得用户访问位置的权限时显示用户友好的消息。

注意:继续位置更新会消耗更多电量,因此会迅速减少电池电量。

"只有当用户越过 500 米时,它才有效" 为此,您可以设置位置更新的准确性,例如...

locationManager.desiredAccuracy = kCLLocationAccuracyBest

参考:获取用户当前位置/坐标

https://developer.apple.com/library/content/documentation/Performance/Conceptual/EnergyGuide-iOS/LocationBestPractices.html

  1. 是的,你可以编写应用,使其可以接收后台位置修复,如你自己的代码所示。但是,您需要向用户请求后台位置权限,出于隐私和电池原因,许多用户通常不愿意这样做。

  2. CLLocationManager是否使用互联网(甚至只是Wifi接入点检测)与GPS非常依赖于设备。 有关详细信息,请参阅此 SO 答案。

  3. App Store 中有许多应用程序通过 CLLocationManager 执行后台位置修复。 我经常对有多少应用程序要求始终位置权限感到震惊。

是的,我们可以在大于或小于 500m 的情况下做到这一点 我已经测试过了它,首先我在谷歌上搜索了很多,在某个地方我发现它正在工作,但我不相信它,因为根据 Apple 安全指南,这似乎是不可能的,所以我在我的代码中应用它并对其进行测试。结果很棒。当应用程序甚至被用户强制退出时,它会将所有位置数据保存在本地plist中,因此您可以通过该plist查看它。我不记得我在哪里看到过那个链接,但我分享了我的一堆直接代码,可以帮助你理解它。

最有趣的部分是,当应用程序处于终止状态时,我能够调用服务器,这真的很棒。 当位置更改时,位置管理器似乎会唤醒应用程序几毫秒。

位置管理器.h

#import <Foundation/Foundation.h>
#import <CoreLocation/CoreLocation.h>
#define IS_OS_8_OR_LATER ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0)
@interface LocationManager : NSObject
@property (nonatomic) CLLocationManager * anotherLocationManager;
@property (nonatomic) CLLocationCoordinate2D myLastLocation;
@property (nonatomic) CLLocationAccuracy myLastLocationAccuracy;
@property (nonatomic) CLLocationCoordinate2D myLocation;
@property (nonatomic) CLLocationAccuracy myLocationAccuracy;
@property (nonatomic) NSMutableDictionary *myLocationDictInPlist;
@property (nonatomic) NSMutableArray *myLocationArrayInPlist;
@property (nonatomic) BOOL afterResume;
+ (id)sharedManager;
- (void)startMonitoringLocation;
- (void)restartMonitoringLocation;
- (void)addResumeLocationToPList;
- (void)addLocationToPList:(BOOL)fromResume;
- (void)addApplicationStatusToPList:(NSString*)applicationStatus;
@end

位置管理器.m

#import "LocationManager.h"
#import <UIKit/UIKit.h>
#import "UpdateUserLocation.h"
#import "Constants.h"
@interface LocationManager () <CLLocationManagerDelegate>
@property (nonatomic , strong) UpdateUserLocation *updateUserLocation;
@end

@implementation LocationManager
//Class method to make sure the share model is synch across the app
+ (id)sharedManager {
static id sharedMyModel = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedMyModel = [[self alloc] init];
});
return sharedMyModel;
}

#pragma mark - CLLocationManager
- (void)startMonitoringLocation {
if (_anotherLocationManager)
[_anotherLocationManager stopMonitoringSignificantLocationChanges];
self.anotherLocationManager = [[CLLocationManager alloc]init];
_anotherLocationManager.delegate = self;
_anotherLocationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation;
_anotherLocationManager.activityType = CLActivityTypeOtherNavigation;
if(IS_OS_8_OR_LATER) {
[_anotherLocationManager requestAlwaysAuthorization];
}
[_anotherLocationManager startMonitoringSignificantLocationChanges];
}
- (void)restartMonitoringLocation {
[_anotherLocationManager stopMonitoringSignificantLocationChanges];
if (IS_OS_8_OR_LATER) {
[_anotherLocationManager requestAlwaysAuthorization];
}
[_anotherLocationManager startMonitoringSignificantLocationChanges];
}

#pragma mark - CLLocationManager Delegate
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations{
NSLog(@"locationManager didUpdateLocations: %@",locations);
for (int i = 0; i < locations.count; i++) {
CLLocation * newLocation = [locations objectAtIndex:i];
CLLocationCoordinate2D theLocation = newLocation.coordinate;
CLLocationAccuracy theAccuracy = newLocation.horizontalAccuracy;
self.myLocation = theLocation;
self.myLocationAccuracy = theAccuracy;
}
[self addLocationToPList:_afterResume];
}

#pragma mark - Plist helper methods
// Below are 3 functions that add location and Application status to PList
// The purpose is to collect location information locally
- (NSString *)appState {
UIApplication* application = [UIApplication sharedApplication];
NSString * appState;
if([application applicationState]==UIApplicationStateActive)
appState = @"UIApplicationStateActive";
if([application applicationState]==UIApplicationStateBackground)
appState = @"UIApplicationStateBackground";
if([application applicationState]==UIApplicationStateInactive)
appState = @"UIApplicationStateInactive";
return appState;
}
- (void)addResumeLocationToPList {
NSLog(@"addResumeLocationToPList");
NSString * appState = [self appState];
self.myLocationDictInPlist = [[NSMutableDictionary alloc]init];
[_myLocationDictInPlist setObject:@"UIApplicationLaunchOptionsLocationKey" forKey:@"Resume"];
[_myLocationDictInPlist setObject:appState forKey:@"AppState"];
[_myLocationDictInPlist setObject:[NSDate date] forKey:@"Time"];
[self saveLocationsToPlist];
}

- (void)addLocationToPList:(BOOL)fromResume {
NSLog(@"addLocationToPList");
NSString * appState = [self appState];
self.myLocationDictInPlist = [[NSMutableDictionary alloc]init];
[_myLocationDictInPlist setObject:[NSNumber numberWithDouble:self.myLocation.latitude]  forKey:@"Latitude"];
[_myLocationDictInPlist setObject:[NSNumber numberWithDouble:self.myLocation.longitude] forKey:@"Longitude"];
[_myLocationDictInPlist setObject:[NSNumber numberWithDouble:self.myLocationAccuracy] forKey:@"Accuracy"];
[_myLocationDictInPlist setObject:appState forKey:@"AppState"];
if (fromResume) {
[_myLocationDictInPlist setObject:@"YES" forKey:@"AddFromResume"];
[self updatePreferredUserLocation:[NSString stringWithFormat:@"%f",self.myLocation.latitude] withLongitude:[NSString stringWithFormat:@"%f",self.myLocation.longitude]];
} else {
[_myLocationDictInPlist setObject:@"NO" forKey:@"AddFromResume"];
}
[_myLocationDictInPlist setObject:[NSDate date] forKey:@"Time"];
[self saveLocationsToPlist];
}
- (void)addApplicationStatusToPList:(NSString*)applicationStatus {
NSLog(@"addApplicationStatusToPList");
NSString * appState = [self appState];
self.myLocationDictInPlist = [[NSMutableDictionary alloc]init];
[_myLocationDictInPlist setObject:applicationStatus forKey:@"applicationStatus"];
[_myLocationDictInPlist setObject:appState forKey:@"AppState"];
[_myLocationDictInPlist setObject:[NSDate date] forKey:@"Time"];
[self saveLocationsToPlist];
}
- (void)saveLocationsToPlist {
NSString *plistName = [NSString stringWithFormat:@"LocationArray.plist"];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docDir = [paths objectAtIndex:0];
NSString *fullPath = [NSString stringWithFormat:@"%@/%@", docDir, plistName];
NSMutableDictionary *savedProfile = [[NSMutableDictionary alloc] initWithContentsOfFile:fullPath];
if (!savedProfile) {
savedProfile = [[NSMutableDictionary alloc] init];
self.myLocationArrayInPlist = [[NSMutableArray alloc]init];
} else {
self.myLocationArrayInPlist = [savedProfile objectForKey:@"LocationArray"];
}
if(_myLocationDictInPlist) {
[_myLocationArrayInPlist addObject:_myLocationDictInPlist];
[savedProfile setObject:_myLocationArrayInPlist forKey:@"LocationArray"];
}
if (![savedProfile writeToFile:fullPath atomically:FALSE]) {
NSLog(@"Couldn't save LocationArray.plist" );
}
}

Appdelegate.m

#import "LocationManager.h"
@property (strong,nonatomic) LocationManager * locationShareModel;
- (void)applicationDidEnterBackground:(UIApplication *)application
{
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
enable when use track location at force quit
[self.locationShareModel restartMonitoringLocation];
[self.locationShareModel addApplicationStatusToPList:@"applicationDidEnterBackground"];
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
enable when use track location at force quit
[self.locationShareModel addApplicationStatusToPList:@"applicationDidBecomeActive"];
//Remove the "afterResume" Flag after the app is active again.
self.locationShareModel.afterResume = NO;
[self.locationShareModel startMonitoringLocation];
}
- (void)applicationWillTerminate:(UIApplication *)application
{
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
enable when use track location at force quit
[self.locationShareModel addApplicationStatusToPList:@"applicationWillTerminate"];
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{
NSLog(@"AG_Didfinishlaunching");
//enable when use track location at force quit
[self trackLocation:launchOptions];
}
- (void)trackLocation:(NSDictionary *)launchOptions {
self.locationShareModel = [LocationManager sharedManager];
self.locationShareModel.afterResume = NO;
[self.locationShareModel addApplicationStatusToPList:@"didFinishLaunchingWithOptions"];
//UIAlertView * alert;
//We have to make sure that the Background App Refresh is enable for the Location updates to work in the background.
if ([[UIApplication sharedApplication] backgroundRefreshStatus] == UIBackgroundRefreshStatusDenied) {
///////// delete this alert view if no need
alert = [[UIAlertView alloc]initWithTitle:@""
message:@"The app doesn't work without the Background App Refresh enabled. To turn it on, go to Settings > General > Background App Refresh"
delegate:nil
cancelButtonTitle:@"Ok"
otherButtonTitles:nil, nil];
[alert show];
UIAlertController *showMsgAlertController = [UIAlertController alertControllerWithTitle: @"Eventseeker" message: @"To use all location servies. Please turn on Background App Refresh, go to Settings > General > Background App Refresh" preferredStyle: UIAlertControllerStyleAlert];
UIAlertAction *showMsgAlertControllerOkAction = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault
         handler:nil];
[showMsgAlertController addAction:showMsgAlertControllerOkAction];
dispatch_async(dispatch_get_main_queue(), ^{
[self presentViewController:showMsgAlertController animated:YES completion:nil];
});
/////////
} else if ([[UIApplication sharedApplication] backgroundRefreshStatus] == UIBackgroundRefreshStatusRestricted) {
///////// delete this alert view if no need
alert = [[UIAlertView alloc]initWithTitle:@""
message:@"The functions of this app are limited because the Background App Refresh is disable."
delegate:nil
cancelButtonTitle:@"Ok"
otherButtonTitles:nil, nil];
[alert show];
/////////////////
} else {
// When there is a significant changes of the location,
// The key UIApplicationLaunchOptionsLocationKey will be returned from didFinishLaunchingWithOptions
// When the app is receiving the key, it must reinitiate the locationManager and get
// the latest location updates
// This UIApplicationLaunchOptionsLocationKey key enables the location update even when
// the app has been killed/terminated (Not in th background) by iOS or the user.
NSLog(@"UIApplicationLaunchOptionsLocationKey : %@" , [launchOptions objectForKey:UIApplicationLaunchOptionsLocationKey]);
if ([launchOptions objectForKey:UIApplicationLaunchOptionsLocationKey]) {
// This "afterResume" flag is just to show that he receiving location updates
// are actually from the key "UIApplicationLaunchOptionsLocationKey"
self.locationShareModel.afterResume = YES;
[self.locationShareModel startMonitoringLocation];
[self.locationShareModel addResumeLocationToPList];
}
}
}

最后但并非最不重要的一点是,不要忘记在plist中添加始终访问位置的权限。 这是在应用程序强制退出时访问它非常重要的步骤。用户必须选择始终使用位置访问,并且必须关闭节电模式,因为它会禁用后台刷新模式。 请记住,"位置始终"访问,此应用的后台刷新模式必须为"开"才能访问处于终止状态的位置。

对于离线模式,我没有测试它。但我认为它有效。

最新更新