📷

[iOS]近年の写真/動画とプライバシー

2023/07/23に公開

iOS 13〜iOS 17までの、写真/動画とプライバシーについて時系列で書いておきます。

※この記事ではiOSかつ本体(App ClipsやWidgetではない)におけるプライバシーについての記事です。

〜iOS 13

写真/動画にアクセスする場合、次のようなコードを書いて、ステータスチェック、権限の要求をしていた。

if PHPhotoLibrary.authorizationStatus() == .notDetermined {
    PHPhotoLibrary.requestAuthorization { (status) in
        // hogehgoe...
    }
}

同時に、Info.plistにも許諾メッセージの設定をする必要がある。 NSPhotoLibraryAddUsageDescription はwrite-only、 NSPhotoLibraryUsageDescription はread or writeな権限のときに使う。

<key>NSPhotoLibraryAddUsageDescription</key>
<string>写真を保存するためにアクセス権が必要です。</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>写真を表示するためにアクセス権が必要です。</string>

iOS 14

※写真以外のことも含めた、WWDC20においてのプライバシーの変更点についてはかつて記事にしているので興味ある方はこちらもどうぞ。
WWDC2020での許諾周りのアップデートまとめ

PHPickerViewControllerの登場

PHPickerViewController が登場した。写真ピッカーを表示する時に直接Photos Libraryにアクセスを要求する必要がないので許諾ダイアログを出す必要がなく、この方法が推奨されている。

詳しくはかつて記事にしたこちらをどうぞ。
UIImagePickerControllerに代わるPHPickerViewControllerの紹介

※iOS 16でPHPickerFilterの種類が増え、スクリーンショットやスローモーション動画などをフィルタリングできるようになっている。複合フィルタも .all(of:) , .not(_:) が登場した。一部はiOS 16 SDKでコンパイルしているなら、iOS 15でも使用できる。

「写真を選択...」

PHAuthorizationStatus.limited が増えた。ユーザが選択した写真しかアプリからアクセスできない状態を作ることができる。

これとは別に、 PHAccessLevel というアクセスレベルを指定するenumが登場している。 .addOnly (書き込みのみ)を指定した場合は左、 .readWrite (読み書き)を指定した場合は右の許諾ダイアログになる。

関連して、 PHPhotoLibrary.authorizationStatus() (ステータス確認API)に更新が入り、アクセスレベルの指定が必要になっている。 .readWrite レベルにおけるステータス確認は以下の通り。

許諾リクエスト処理に関してもアクセスレベルの指定が必要になっている。

もしユーザが「写真を選択...」を選択した場合、何も対策をしないとアプリのライフサイクルごと、PhotoKitのコンテンツにアクセスする度に現在のアクセス権限を維持するか、変更するかを問うアラートが出てきてしまう。

許諾アラートを毎回表示させないためには、Info.plistの PHPhotoLibraryPreventAutomaticLimitedAccessAlert にYESを設定すれば良い。

iOS 16

PHPickerViewController はSwiftUIから直接使うことはできない問題があった。iOS 16から登場した PhotosPicker はSwiftUIから直接使うことができる。こちらも許諾ダイアログを出す必要がない。

使い方は簡単で、例えば複数の写真を選択するピッカーは以下のコードで実現できる。 selectedPhotos に選択した写真が入ってくる。

struct ContentView: View {
    @State private var selectedPhotos: [PhotosPickerItem] = []
    // 単一選択にしたい場合はこちらを使用する
    //@State private var selectedPhoto: PhotosPickerItem? = nil

    var body: some View {
        PhotosPicker(selection: $selectedPhotos,
                     matching: .images) {
            Text("show PhotosPicker") 
	}
    }
}

Discussion