はじめに (対象読者・この記事でわかること)
このページは、iOS アプリ開発に携わる Swift エンジニア、特に「UIDocumentPicker」を利用しようとしてコンパイルエラー
Cannot initialize a parameter of type 'id<UIDocumentPickerDelegate> _Nullable' with an lvalue of type 'const Class' に直面した方を対象としています。
記事を読み進めると、エラーの原因が「デリゲートの型指定ミス」や「クラス定義の書き方」にあることが分かり、正しいデリゲート設定のコードと、Xcode でのビルド回避手順が身につきます。また、同様の型エラー全般に応用できる Swift の型システムの基礎知識も復習でき、今後の開発で同種の問題に遭遇した際のトラブルシューティングがスムーズになるでしょう。
前提知識
この記事を読み進める上で、以下の知識があるとスムーズです。
- Swift の基本的な文法とクラス/プロトコルの概念
- iOS アプリ開発における Xcode の基本操作
- UIDocumentPicker とそのデリゲートメソッドの概要
UIDocumentPicker とデリゲート設定の背景
UIDocumentPickerViewController は、ユーザーに外部ストレージや iCloud からファイルを選択させるための標準 UI コンポーネントです。利用するには、UIDocumentPickerDelegate プロトコルに準拠したオブジェクトをデリゲートとして設定する必要があります。多くのサンプルコードでは、self をそのまま渡す形が示されていますが、Swift と Objective‑C の相互運用が絡むと型推論が崩れ、上記のようなコンパイルエラーが発生します。
エラー文を分解すると次のようになります。
id<UIDocumentPickerDelegate> _Nullable… Objective‑C の「UIDocumentPickerDelegate プロトコルに準拠したオブジェクト」型const Class… Swift 側でClass(メタタイプ)として扱っているが、constが付与されているため「インスタンス」ではなく「型」そのものを渡そうとしている
要するに、デリゲートに インスタンス を渡すべきところを クラスオブジェクト(メタタイプ)を渡していることが原因です。これが起きる典型的なシーンは、デリゲートクラスを static let shared = MyPickerDelegate.self のように .self を付けてしまう、あるいは MyPickerDelegate を直接 UIDocumentPickerViewController(delegate:) に渡すケースです。
正しい実装手順とコード例
以下では、エラーが再現しやすい「間違った書き方」と、正しく動作する「修正版」のコードをステップごとに比較しながら解説します。
ステップ 1 – デリゲートプロトコルを準拠したクラスを作成
Swiftimport UIKit class DocumentPickerHandler: NSObject, UIDocumentPickerDelegate { // MARK: - UIDocumentPickerDelegate メソッド func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) { // ユーザーが選択したファイルの URL 配列を処理 for url in urls { print("Picked file: \(url.lastPathComponent)") } } func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController) { print("Document picker was cancelled") } }
ポイントは NSObject を継承し、UIDocumentPickerDelegate に準拠させていることです。NSObject 継承は Objective‑C のランタイムに必要なブリッジングを提供します。
ステップ 2 – ビューコントローラでインスタンス化してデリゲートに設定
Swiftclass ViewController: UIViewController { // デリゲートインスタンスをプロパティとして保持 private let pickerHandler = DocumentPickerHandler() @IBAction func openDocumentPicker(_ sender: UIButton) { // UIDocumentPickerViewController の初期化 let types: [UTType] = [UTType.content, UTType.item] // 任意のUTType let picker = UIDocumentPickerViewController(forOpeningContentTypes: types, asCopy: true) // 正しいデリゲート設定 picker.delegate = pickerHandler // ← インスタンスを渡す // 表示 present(picker, animated: true, completion: nil) } }
重要: picker.delegate = pickerHandler とし、インスタンス を渡す点です。.self を付けずにクラス名だけを渡すと先ほどのエラーになります。
ステップ 3 – 間違いやすいコード例とそのエラーメッセージ
Swift// NG パターン: クラスオブジェクトを直接渡す picker.delegate = DocumentPickerHandler.self // ここがエラー原因
このコードをビルドすると、Xcode のコンパイル画面に次のように表示されます。
Cannot initialize a parameter of type 'id<UIDocumentPickerDelegate> _Nullable' with an lvalue of type 'const DocumentPickerHandler.Type'
ハマった点やエラー解決
1. .self の付与忘れ
多くの開発者が DocumentPickerHandler.self と書きがちです。Swift では型そのものを指すメタタイプになるため、デリゲートとしては不適格です。
2. プロパティが weak に設定されている
UIDocumentPickerViewController の delegate は weak 参照です。デリゲートインスタンスがローカル変数だけで管理されると、スコープ外になった瞬間に解放されてしまい、実行時に delegate が nil になるケースがあります。上記例では pickerHandler をビューコントローラのプロパティとして保持しているため、安全です。
3. NSObject 継承の抜け漏れ
UIDocumentPickerDelegate は Objective‑C のプロトコルです。Swift だけで実装すると、ランタイムがメソッドのシグネチャを認識できず、未実装メソッド の警告が出ます。NSObject を継承すれば Objective‑C ランタイムと正しく橋渡しできます。
解決策まとめ
| 項目 | 誤り例 | 正しい実装 |
|---|---|---|
| デリゲート型 | picker.delegate = DocumentPickerHandler.self |
picker.delegate = pickerHandler (インスタンス) |
| 継承 | class DocumentPickerHandler: UIDocumentPickerDelegate {} |
class DocumentPickerHandler: NSObject, UIDocumentPickerDelegate {} |
| インスタンス保持 | ローカル変数で let handler = DocumentPickerHandler() |
ビューコントローラのプロパティ private let pickerHandler = DocumentPickerHandler() |
| メソッド実装 | 省略 | documentPicker(_:didPickDocumentsAt:) と documentPickerWasCancelled(_:) を必ず実装 |
上記表の通り、インスタンスを渡す、NSObject 継承、インスタンスを保持 の3点がエラー回避の鍵です。
まとめ
本記事では、Swift で UIDocumentPickerViewController のデリゲート設定時に発生するコンパイルエラー「Cannot initialize a parameter of type 'id
- 原因:デリゲートにクラスのメタタイプ(
.self)を渡していたこと - 解決策:
NSObject継承したデリゲートクラスのインスタンスを作成し、プロパティで保持した上でpicker.delegateに設定する - ベストプラクティス:デリゲートは
weakになるため、インスタンスが解放されないように参照を保持する
この記事を読むことで、同様の型エラーに遭遇した際にすぐに対処でき、iOS のファイルピッカー実装を安全かつスムーズに進められるようになります。次回は、UIDocumentPicker の新しい iOS 16 以降の API(UIDocumentPickerViewController(forOpeningContentTypes:asCopy:))と、マルチドキュメントサポートの実装例を取り上げる予定です。
参考資料
- Apple Developer Documentation – UIDocumentPickerViewController
- Swift Programming Language – Protocols
- Apple Sample Code – Document Picker Sample
