酢ろぐ!

カレーが嫌いなスマートフォンアプリプログラマのブログ。

Cloud Firestore の Collection references は偶数個のセグメントだと例外が発生する

長いあいだ、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 にはパス設計が存在するため注意すること。