[iOS][macOS][PhotoKit] PhotoKit のセットアップ async/await 対応板

Swift

話題(?)の async/await を使うと、PhotoKit への requestAuthorization がどうなるかを確認してみました。

環境&対象

以下の環境で動作確認を行なっています。

  • macOS Monterery beta 5
  • Xcode 13 beta5
  • iOS 15

async/await 対応した PhotoKit

Swift5.5 になって、Concurrency がサポートされました。

言語仕様として実装されただけでなく、Apple から提供されている多くの フレームワーク で Concurrency サポートが追加されました。

この記事では HealthKit のセットアップと使い方を確認します。

大きな変更点: async/await 対応

async/await 対応したことで、delegate や completionHandler でのやりとりを await を使った呼び出しに変えることができます。

PhotoKit でもいくつか用意されている非同期の関数を呼び出すと 処理終了時に 設定した delegate や completionHandler が呼び出される仕組みでした。その部分を await を使った呼び出しに変えることができるようになります。

await を使った呼び出しに変更することで、機能的な相違はありません。
ですが、コード的には、delegate を使うと必要な処理を別の箇所に記述しなければなりませんでしたし、completionHandler を使うと コードが nest を繰り返し 処理の流れを追いづらくなってしまうという問題がありました。

# 以下の記事では completionHandler を Combine 経由で利用するケースを説明しています。当時は、async や await がまだありませんでした。
[Photos][Combine] requestAuthorization を Combine と組み合わせて使う

PhotoKit を使う準備

PhotoKit を使うためのプロジェクト設定

プロジェクト作成後以下の設定を行う必要があります。

  • Info.plist に "Privacy - Photo Library Additions Usage Description" を追加し、理由を記述する
    自分のアプリが PhotoKit のデータを保存するケースで必要となります
  • Info.plist に "Privacy - Photo Library Usage Description" を追加し、理由を記述する
    自分のアプリが PhotoKit のデータを編集するケースで必要となります

# Xcode13 以降では、Info.plist が見えなくなっていて直接編集できません。
[Xcode] Xcode13 で作成した新規プロジェクトに Info.plist がない件

MEMO
ここでは、特に async/await 対応に起因する変更はありません。

PhotoKit アクセス要求

PhotoKit を使う時には、以下の点に注意しないといけません。

  • アプリが HealthKit データにアクセスしてよいかの許諾をユーザーに求めることが必要
  • ユーザーに許諾をリクエストしないままデータを取得しようとすると、クラッシュする

基本的に 許諾を得ようとしないことはあり得ません。(許諾なしにアクセスすると アプリはクラッシュしますし、レビューでもリジェクトされるはずです)

ただし、ユーザーが許諾しないことは考えられますし、当初許諾しても 後から 設定アプリで 許諾を取り消すことも可能ですので、許諾されていないときにどうするかを考える必要はあります。

以下のコードは、PhotoKit 中に 指定した UIImage を保存する機能を wrap するクラスをイメージしています。保存するために、写真を追加することの許諾を init 内でリクエストしています。


//
//  PhotoKitDataStore.swift
//
//  Created by : Tomoaki Yagishita on 2021/09/21
//  © 2021  SmallDeskSoftware
//

import Foundation
import PhotoKit

class PHDataStore {
    // (1)
    init() {
        // (2)
        if PHPhotoLibrary.authorizationStatus(for: .addOnly) != .authorized {
            // (3)
            Task {
                // (4)
                let result = await PHPhotoLibrary.requestAuthorization(for: .addOnly)
                if result != .authorized {
                    // user did not give access rights....
                }
            }
        }
    }
}
コード解説
  1. init で、ユーザーに許諾をリクエストしています
  2. PHPhotoLibrary.authorizationStatus は、sync 関数なので、普通に呼び出して使用します
  3. PHPhotoLibrary.requestAuthorization の await 版を使うため、Task を使用しています
  4. requestAuthorization の結果がくるまで待って処理が進みます

PhotoKit の requestAuthorization

Apple のドキュメントみると、iOS15 以降向けに新しい API が用意されています。Apple のドキュメントは、こちら


class func requestAuthorization(for accessLevel: PHAccessLevel) async -> PHAuthorizationStatus

話題(?) の async が付与されている API です。

参考までに CompletionHandler 版の API は以下です。


class func requestAuthorization(for accessLevel: PHAccessLevel, 
                        handler: @escaping (PHAuthorizationStatus) -> Void)

相違点は、handler の代わりに、"async" が付与されている点と、"throws" も付与されている点です。

throws が追加されている理由

throws は、呼び出した処理内でエラーが発生した時に、エラーとして呼び出し元に戻るために追加されています。

CompletionHandler 版の API では、closure に 結果とエラー が渡されてきます。ですが、async 版では、エラーが渡されることはありません。そのエラーは、async 版では、例外として投げられてきます。

なお、この設計は、PhotoKit 特有の設計ではなく completionHandler, delegate 的な API に対応する 関数の async 版で一般的に採用されているルールです。

async

# 本題です。

async というキーワードも追加されています。

async は、その関数が処理中に suspend されるかもしれないことを意味します。

CompletionHandlerがその記述タイミングで実行されないかもしれないことと同様に、async が付与された関数をコールしても、直後に実行されないかもしれないという意味です。(関数内で suspend される可能性もあります)

async 版 requestAuthorization

async 版の requesetAuthorization は、throws と async の両方がついていますので、以下のような振る舞いをすることがわかります。

  • throws がついているので、エラーは 例外として投げられてくる
  • async がついているので、関数コール直後に実行されないかもしれない

以上で、PhotoKit のセットアップは終了です。

まとめ:PhotoKit のセットアップ async/await 対応板

PhotoKit のセットアップ async/await 対応板
  • PhotoKit のセットアップでは、async/await 対応で大きな変化はない
  • async/await 対応した requestAuthorization を呼ぶときは、Task を使う

説明は以上です。
不明な点やおかしな点ありましたら、こちらまで。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です