長いあいだ、Swift でのデータ操作が煩雑な Cloud Firestore を使う気になれなかったのだが、いつのまにか Swift Concurrency に対応していた。async / await が使えると話が変わってくる。格段に使いやすくなっていたので、お試しで簡単なアプリを作ってみることにした。
しかし、Firestore へデータを書き込もうとしたところ例外が発生してしまった。
Terminating app due to uncaught exception 'FIRInvalidArgumentException', reason: 'Invalid collection reference. Collection references must have an odd number of segments, but version/1/z5NXjW6CTFdHjlNjsrWQ4XBxcvg2/items has 4'
「無効なコレクション参照です。コレクション参照には奇数のセグメントが必要です。」と言われても、どういうことかわからない…… なぜ偶数なら大丈夫なのか?
まとめ
先に答えを書いておく。
- Firestore のパス設計は
collection/document/collection/document
としなければいけない - よってコレクションのリファレンスは、奇数のセグメントでなければいけない
- 例:
/version/1/users
など
- 例:
- よってドキュメントのリファレンスは、偶数のセグメントでなければいけない
- 例:
/version/1
など
- 例:
- それ以外の場合、例外
FIRInvalidArgumentException
が発生する
経緯
Firestore にデータを保存するため、以下のようなコードを書いた。
let documentRef = firestore.collection("version/1/\(userId)/items").document(item.id) try await documentRef.setData( [ "title": item.title, "create_at": item.createdAt, "update_at": item.updatedAt, ] )
このエラーに関する情報は日本語では「【Swift】FireStoreの機能を使用すると、"Invalid document reference.."のエラー」でしか取り上げられていなかったが、回答としては db.collection("User").document(userDataClass.userID).getDocument()
を db.collection("User").getDocument()
に変更したら動くようになったという曖昧な理由であった。
理屈がわからなかったため調べたところ、Firestoreのドキュメントのパス設計は collection/document/collection/document
という交互のパターンにしなければいけない。よって、ドキュメントのリファレンスは必ず偶数のセグメント、コレクションのリファレンスは必ず奇数のセグメントを持つことになるため、
version/1/\(userId)/items/\(itemId)
は、collection/d/d/collection/d
になっていしまうため例外が発生するversion/1/users/\(userId)/items/\(itemId)
は、collection/d/collection/d/collection/d
なので問題なく書き込みが成功する
まとめに書いたように Firestore にはパス設計が存在するため注意すること。