【问题标题】:Location access while app is terminated, without internet iOS应用程序终止时的位置访问,没有互联网 iOS
【发布时间】:2018-08-28 20:16:49
【问题描述】:

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

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

对于问题 1。

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

locationManager.allowsBackgroundLocationUpdates = true
locationManager.startUpdatingLocation()

借助这些,我可以在应用程序处于前台或后台时获取位置。

当应用程序终止时

locationManager.startMonitoringSignificantLocationChanges()

只有在超过 500 米时才有效

然后应用程序激活并更新位置

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

3.也请告诉我这类应用是否会被苹果批准。

请帮助/建议。

提前致谢。

【问题讨论】:

    标签: ios swift location applet core-location


    【解决方案1】:

    你的答案在这里:

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

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

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

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

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

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

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

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

    locationManager.desiredAccuracy = kCLLocationAccuracyBest
    

    参考: Get User's Current Location / Coordinates

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

    【讨论】:

    • 谢谢,但这不是解决方案。如果您再次阅读我的问题,您会看到我在应用程序终止时获取位置,但苹果有限制注意应用程序可以在设备从之前的通知移动 500 米或更远时立即收到通知。它不应期望通知的频率超过每五分钟一次。如果设备能够从网络检索数据,则位置管理器更有可能及时传递通知。所以我想知道另一种方式,所以如何每次都获取位置
    • 我还是没有得到你。简单地告诉我这个问题,不要造成更多混乱。
    • 我可以在应用程序未运行时获取位置,但它会在 500 米行驶后更新。我想每次都像应用程序在前台一样获取位置。当我处于飞行模式时,无论应用程序处于活动状态还是非活动状态,我都没有收到位置
    【解决方案2】:
    1. 是的,您可以编写应用程序,使其能够接收后台位置修复,如您自己的代码所示。但是,您需要向用户询问后台位置权限,出于隐私和电池原因,许多用户通常不愿意这样做。

    2. CLLocationManager 是否使用互联网(甚至只是 Wifi 接入点检测)与 GPS 是非常依赖于设备的。详情请见this SO answer

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

    【讨论】:

    • 澄清一下,当用户终止您的应用时,您无法获得高度准确的位置; iOS 假定他们不希望应用程序运行,可能是出于隐私原因,可能是出于电池寿命原因或其他一些原因。
    • 感谢@Paulw11 每次应用程序终止时我们都可以获取位置吗?当我在飞行模式时,位置管理器无法返回位置
    • 您可以在飞行模式下获取基于 GPS 的位置。但是,您不会获得重要的位置更新,因为它取决于蜂窝塔三角测量。当用户终止应用时,您无法获取基于 gps 的位置更新。
    【解决方案3】:

    是的,我们可以在 500m 以上或以下的情况下进行测试似乎不可能,所以我将它应用到我的代码中并进行测试。结果很棒。当应用程序被用户强制退出时,它会将所有位置数据保存在本地 plist 中,因此您可以通过该 plist 看到它。我不记得我在哪里看到了那个链接,但我分享了我的一堆直接代码,可以帮助你理解它。

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

    LocationManager.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
    

    LocationManager.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 中添加始终访问位置的权限。当应用程序被强制退出时,这是访问它的非常重要的一步。用户必须选择始终使用位置访问,并且必须关闭省电模式,因为它会禁用 后台刷新模式。 请记住,此应用的位置始终访问和后台刷新模式必须打开才能访问处于终止状态的位置。

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

    【讨论】:

    • 我们怎样才能将范围缩小到 500 到 10 米
    • @ios swift:使用 kCLLocationAccuracyNearestTenMeters 作为所需的精度。在这段代码中,我使用的是 kCLLocationAccuracyBestForNavigation,它最适合导航,也可能对您的情况有所帮助。
    • 它只在移动设备 500 米距离上给出事件。当应用被终止或杀死时,是否有可能在 50 到 100 米内获得更新?
    • 你确定吗? @Dhaval Bhimani,因为在我的情况下,我检查了 plist 并计算了它在 50 到 100 米之间的距离。当我行驶到 6 公里的距离时,我的 plist 中有 186 个条目。因此,如果根据您的观察,它仅在 500 米距离后显示,那么 plist 中肯定只有 12 个条目,但它是 186 个。请您确认一下。
    • 您是否在应用处于终止状态时进行了测试?当应用程序处于前台时,它工作正常。但是,只有在 500 米后应用处于终止状态时才会更新。
    猜你喜欢
    • 2016-06-03
    • 1970-01-01
    • 2017-03-30
    • 2014-10-18
    • 2017-07-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多