ObjecTips

Swift & Objective-C で iOS とか macOS とか

iOS 11 での UIImagePickerController の変更点

iOS 10 までは UIImagePickerControllerUIImagePickerControllerSourceTypePhotoLibrary の組み合わせでフォトライブラリを表示する際にアクセス権限の確認を自動で行ってくれていた。
アクセス権限がなければ鍵マークが表示され一切の情報を取得する事ができない。ユーザの操作としては必ずキャンセルが行われる。*1

iOS 10

自動でアクセス許可のアラートが表示される
f:id:Koze:20170920044429p:plain

権限なしの状態ではキャンセルしか出来ない
f:id:Koze:20170920044433p:plain

権限があればフォトライブラリを表示、写真を選択可能
f:id:Koze:20170920044437p:plain

iOS 11 ではアクセス権限の確認が自動で行われない様になった。そして権限がなくともフォトライブラリの表示は行われ、写真の選択も出来てしまう。

iOS 11

権限の確認無しにいきなりフォトライブラリが表示
f:id:Koze:20170920044959p:plain

何が起こるか

写真の選択を行うと通常通り delegate メソッドが呼ばれる。

- (void)imagePickerController:(UIImagePickerController *)picker 
didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info;

この時 info には
UIImagePickerControllerMediaType
UIImagePickerControllerOriginalImage
UIImagePickerControllerReferenceURL
UIImagePickerControllerImageURL
といった情報が入っている。

UIImagePickerControllerReferenceURL には ALAsset のURLが入っている。
iOS 11 で deprecated になったがまだ使う事は出来る。

ex.)
assets-library://asset/asset.HEIC?id=97F34D03-1230-4F4E-9AA6-E7A7469158DE&ext=HEIC

フォトライブラリへのアクセスが許可されていれば以下の様にしてアセットへアクセスする事が出来る。

    NSURL *assetURL = info[UIImagePickerControllerReferenceURL];
    PHFetchResult<PHAsset *> *result = [PHAsset fetchAssetsWithALAssetURLs:@[assetURL] options:nil];
    PHAsset *asset = result.firstObject;
    NSLog(@"%@ %@", result, asset);

ただし、iOS 11 の場合でアクセス権限無しでフォトライブラリを表示選択してこのコードが実行された場合 PHFetchResultPHUnauthorizedFetchResult という Private クラスが返され、アセットの取得が出来ない。

アセットの取得には失敗するがアセットにアクセスしようとした事がトリガーとなってアクセス許可のアラートが表示される。

f:id:Koze:20170920052745p:plain

このタイミングのアラートでアクセス権限を得たとしても既にアセットのフェッチには失敗しているので、再度フォトライブラリを開くか assetURL を使って再度アセットをフェッチするなどしないとアセットは取得出来ない。

iOS 11 に対応する処理の流れとしては UIImagePickerController を表示する前かアセットをフェッチする前に PHPhotoLibrary

+ (void)requestAuthorization:(void(^)(PHAuthorizationStatus status))handler;

で権限を得ておく必要がある。

iOS 10 まではそもそもアクセスが許可されていないとフォトライブラリが表示されないので、こういった事は問題は起こり得ない。

アクセス権限無しでフォトライブラリを開ける意味

アクセス権限が無い状態でも UIImagePickerControllerOriginalImage で取得した UIImage は利用する事が出来る。
また UIImagePickerControllerImageURL を使って NSURL から UIImageNSData を作ったりファイルコピーなどする事も出来る。

UIImagePickerControllerImageURL は iOS 11 で登場したもので元画像のファイルURLが入っている。

ex.)
file:///private/var/mobile/Containers/Data/Application/1770CD90-C268-48BF-8A35-6210698472EF/tmp/EBD7BD90-2A50-4BEB-86D3-63ADB1E12CB0.jpeg


先の例だとアクセス権限が無い状態 = 未確認、つまり PHAuthorizationStatusNotDetermined でのケースだったが、アクセス許可のアラートでアクセスが拒否された場合、つまり PHAuthorizationStatusDenied の状態でも UIImagePickerController によってフォトライブラリを開く事が出来る。

画像に様に許可アラートや設定画面でアクセスが許可されていなくてもフォトライブラリの表示と利用が可能

f:id:Koze:20170920060545p:plain f:id:Koze:20170920060548p:plain

まとめ

  • iOS 11 では UIImagePickerController によるフォトライブラリの表示はユーザのアクセス権限が不要になった。
  • アクセス権限が明確に拒否 (Denied) されている場合でも UIImagePickerController 経由で得られる UIImage や画像の NSURL を使ってユーザが選択した画像データを取り扱う事が出来る。
  • フォトライブラリへのアクセスが発生する PHAsset の取得はアクセス権限が必要。

UIImagePickerController での写真の表示と選択はユーザ操作によるものなのでOK(許可されていると同義)、PHAsset の取得などアプリがコードでフォトライブラリへアクセスする場合はユーザには何をしているか見えないのでアクセス権限が必要という方針なんだろうと推察される。

※動作確認 iOS 11.0 Xcode 9

iOS 11 UIImagePicker test


追記

WWDC 2017 - Session 702 Privacy and Your Apps
https://developer.apple.com/videos/play/wwdc2017/702/

Image picker without prompting for access

12:25 あたりから上記について写真ライブラリの書き込みのみパーミッションが必要な様に変更されたと言及有り。

WWDC 2017 - Session 505 What's New in Photos APIs
https://developer.apple.com/videos/play/wwdc2017/505/

2:15 からもこの仕様変更について詳しく言及あり。

*1:Info.plist に NSPhotoLibraryUsageDescription は記載済みの前提