iOS Tips #5 位置情報サービスのアクセス制限

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

iOSで位置情報サービスを利用するときのTipsです。普段iOS端末を利用されている方であれば一度は以下のようなダイアログを見たことがあるかと思います。

ios-corelocation_1

iOSではこの仕組みのおかげで、ユーザーが意図していないのに個人情報にアクセスしてしまうことを防いだり、悪意を持ったアプリから個人情報を守ってくれています。iOSで扱う個人情報は様々ですが、今回は位置情報サービスへのアクセス制限の実装方法を紹介したいと思います。

プロジェクトの作成

まずは、XcodeよりSingle View Applicationを選択し、以下の内容でプロジェクトを作成しましょう。

項目 設定値
Product Name LocationAccessSample
Organization Name 自分の名前(サンプルなのでテキトー)
Company Identifier 会社名(サンプルなのでテキトー)
Class Prefix なし
Devices iPhone
Use Storyboards チェックする(ストーリーボードを使用)
Use Automatic Reference Counting チェックする(ARC有効)
Include Unit Tests チェックしない(unit testのターゲットを含まない)

CoreLocation.frameworkの追加

位置情報サービスを利用するにはCoreLocation.frameworkを使用するのでLocationAccessSampleプロジェクトにCoreLocation.frameworkを追加しましょう。

ios-corelocation_2

ViewControllerの実装

今回は位置情報サービスを利用するロジックをViewControllerに実装します。まずは以下のように実装してください。

ViewController.m
#import "ViewController.h"
#import <CoreLocation/CoreLocation.h>

@interface ViewController () <CLLocationManagerDelegate>

@property (strong, nonatomic) CLLocationManager *locationManager;

@end

@implementation ViewController

#pragma mark - UIViewController lifecicle event methods

- (void)viewDidLoad
{
    [super viewDidLoad];
}

- (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];
    
    // 位置情報サービスへのアクセスを開始する
    [self startLocationService];
}

#pragma mark - Private methods

- (void)startLocationService
{
    // このアプリの位置情報サービスへの認証状態を取得する
    CLAuthorizationStatus status = [CLLocationManager authorizationStatus];
    
    switch (status) {
        case kCLAuthorizationStatusAuthorized: // 位置情報サービスへのアクセスが許可されている
        case kCLAuthorizationStatusNotDetermined: // 位置情報サービスへのアクセスを許可するか選択されていない
        {
            // 位置情報サービスへのアクセスを許可するか確認するダイアログを表示する
            self.locationManager = [[CLLocationManager alloc] init];
            self.locationManager.delegate = self;
            [self.locationManager startUpdatingLocation];
        }
            break;
        
        case kCLAuthorizationStatusRestricted: // 設定 > 一般 > 機能制限で利用が制限されている
        {
            UIAlertView *alertView = [[UIAlertView alloc]
                                      initWithTitle:@"エラー"
                                      message:@"設定 > 一般 > 機能制限で利用が制限されてるよ!"
                                      delegate:nil
                                      cancelButtonTitle:@"OK"
                                      otherButtonTitles:nil];
            [alertView show];
        }
            break;

        case kCLAuthorizationStatusDenied: // ユーザーがこのアプリでの位置情報サービスへのアクセスを許可していない
        {
            UIAlertView *alertView = [[UIAlertView alloc]
                                      initWithTitle:@"エラー"
                                      message:@"このアプリでの位置情報サービスへのアクセスを許可されていないよ!"
                                      delegate:nil
                                      cancelButtonTitle:@"OK"
                                      otherButtonTitles:nil];
            [alertView show];
        }
            break;

        default:
            break;
    }
}

- (void)stopLocationService
{
    // 位置情報サービスを停止する
    [self.locationManager stopUpdatingLocation];
    self.locationManager.delegate = nil;
    self.locationManager = nil;
}

#pragma mark - CLLocationManagerDelegate methods

// 位置情報サービスへのアクセスが許可されていればこのデリゲートメソッドが定期的に実行される
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
    NSLog(@"%@", locations);
}

// 位置情報サービスへのアクセスが失敗した場合にこのデリゲートメソッドが呼ばれる
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
{
    switch (error.code) {
        case kCLErrorDenied: // 確認ダイアログで許可しないを選択した
        {
            UIAlertView *alertView = [[UIAlertView alloc]
                                      initWithTitle:@"エラー"
                                      message:@"このアプリでの位置情報サービスへのアクセスを許可されなかったよ!"
                                      delegate:nil
                                      cancelButtonTitle:@"OK"
                                      otherButtonTitles:nil];
            [alertView show];
        }
            break;
            
        default:
        {
            UIAlertView *alertView = [[UIAlertView alloc]
                                      initWithTitle:@"エラー"
                                      message:@"位置情報の取得に失敗したよ!"
                                      delegate:nil
                                      cancelButtonTitle:@"OK"
                                      otherButtonTitles:nil];
            [alertView show];
        }
            break;
    }
}

// 位置情報サービスの設定が変更された場合にこのデリゲートメソッドが呼ばれる
- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status
{
    switch (status) {
        case kCLAuthorizationStatusRestricted: // 設定 > 一般 > 機能制限で利用が制限されている
        {
            // 位置情報サービスを停止する
            [self stopLocationService];

            UIAlertView *alertView = [[UIAlertView alloc]
                                      initWithTitle:@"エラー"
                                      message:@"設定 > 一般 > 機能制限で利用が制限されてるよ!"
                                      delegate:nil
                                      cancelButtonTitle:@"OK"
                                      otherButtonTitles:nil];
            [alertView show];
        }
            break;
            
        case kCLAuthorizationStatusDenied: // ユーザーがこのアプリでの位置情報サービスへのアクセスを許可していない
        {
            // 位置情報サービスを停止する
            [self stopLocationService];
            
            UIAlertView *alertView = [[UIAlertView alloc]
                                      initWithTitle:@"エラー"
                                      message:@"このアプリでの位置情報サービスへのアクセスを許可されていないよ!"
                                      delegate:nil
                                      cancelButtonTitle:@"OK"
                                      otherButtonTitles:nil];
            [alertView show];
        }
            break;
            
        default:
            break;
    }
}

@end

位置情報サービスへの認証状態を取得する

位置情報サービスの利用を開始するまえに、アプリが位置情報サービスへのアクセスが許可されているかを調べます。これにはCLLocationManagerクラスのクラスメソッド「+ (CLAuthorizationStatus)authorizationStatus(ViewController.m:32行目)」を使用します。このメソッドを実行するとCLAuthorizationStatusで定義される以下のステータスが返されます。

設定値 説明
kCLAuthorizationStatusNotDetermined アプリ起動後、位置情報サービスへのアクセスを許可するかまだ選択されていない状態
kCLAuthorizationStatusRestricted 設定 > 一般 > 機能制限により位置情報サービスの利用が制限されている状態
kCLAuthorizationStatusDenied ユーザーがこのアプリでの位置情報サービスへのアクセスを許可していない状態
kCLAuthorizationStatusAuthorized ユーザーがこのアプリでの位置情報サービスへのアクセスを許可している状態

kCLAuthorizationStatusRestricted

ユーザーが機能制限(ペアレントコントロール)により意図的に位置情報サービスの利用を制限している場合にこのステータスが返されます。この場合、ユーザーによって機能制限を解除してもらわないと位置情報サービスを利用することができません。機能制限の設定は[設定 > 一般 > 機能制限]で行います。

ios-corelocation_3

kCLAuthorizationStatusDenied

ユーザーが意図的にこのアプリでの位置情報サービスへのアクセスを許可していない場合はこのステータスが返されます(ダイアログで「許可しない」を選択した場合など)。この設定は[設定 > プライバシー > 位置情報サービス]から行います。

ios-corelocation_4

位置情報サービスの利用を開始する

+ (CLAuthorizationStatus)authorizationStatusでkCLAuthorizationStatusNotDeterminedkCLAuthorizationStatusAuthorizedが返された場合は位置情報サービスの利用開始を試みます(ViewController.m:39〜41行目)。ここが重要なのですが、CLLocationManagerクラスで定義されるメソッド「- (void)startUpdatingLocation」です。最初に説明したダイアログですが、実はこの「- (void)startUpdatingLocation」が初めて実行されるタイミングで表示されます。

位置情報サービスへのアクセスが許可された場合

正常に位置情報サービスの利用が開始できればこのアプリから位置情報サービスにアクセスすることができます(ViewController.m:85〜88行目)。

位置情報サービスへのアクセスが拒否された場合

ダイアログで「許可しない」が選択されると、デリゲートメソッド「- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error」が実行され、errorインスタンスのプロパティ変数codeにkCLErrorDenied」がセットされます。

iOS6以降:確認ダイアログに独自のメッセージを追加する

iOS6からはXxx-Info.plistにNSLocationUsageDescriptionを追加することで、確認ダイアログに独自のメッセージを追加することができます。

ios-corelocation_6

ios-corelocation_5

まとめ

iOSではこのようにプライバシーに関する機能が定義されています。次回は写真か連絡先について書きたいと思います。