Apollo iOSの @defer ディレクティブの実装を見る

モチベーション

Apollo iOSのリリースページを見ていたら、preview-defer.1 というものがあった。

github.com

実験的に @defer ディレクティブの実装が行われている。v1.9.2時点のロードマップにも書いてある。

https://github.com/apollographql/apollo-ios/blob/1.9.2/ROADMAP.md#defer-support

今回はそのディレクティブについて調べたりコードを読んだのでメモを書く。

@defer ディレクティブ

即座に返す必要がないということを表現するディレクティブ。 2024/03/10時点ではまだGraphQL specificationに追加されていない概念で、RFCはある。

github.com

クエリするデータの中で取得に時間がかかるものは後回しにしてそれ以外を早く受け取りたい時がある。そういう時に今までだとクエリの分割やプリフェッチで対応していたが、リクエスト数が増えたり、リクエストが増えるということは個々でレスポンスをハンドリングしないといけなくなる等の課題があった。

解決方法として、 @defer ディレクティブが考えられた。@defer が付与されたもの以外は最初のレスポンスで返され、付与されたものが後続のレスポンスで返ってくる。

Swiftにおけるコード生成後の @defer の表現

Property Wrapperとして表現されている。@defer を付与したフラグメントがコード生成をされるとそのProperty Wrapperが付与される。

https://github.com/apollographql/apollo-ios/blob/preview-defer.1/Sources/ApolloAPI/Deferred.swift

  • wrappedValue: 値を受け取っていれば値を返し、そうでなければnilを返している。
  • projectedValue: .pending, .notExecuted, .fullFilled(Fragment) をcaseに持つEnumを返している。

基本的にwrappedValueを使うだけで事足りそうで、.notExecuted の時にハンドリングしたい時だけprojectedValue使えば良さそう。表示するデータが部分的に取得失敗して、その時用に表示の出し分けをする、ということが考えられそう。コメント内容的に実装時に分かりたいエラーな気もするので違うかも。

ちなみに、Apollo Kotlinでは既に実装されており、nullableで表現されている。Apollo iOSでも案としてあったが、表現が複雑になるため採用されなかった模様*1

https://www.apollographql.com/docs/kotlin/fetching/defer/#:~:text=In%20the%20generated%20code%20for%20this%20query%2C%20the%20onUser%20field%20for%20the%20fragment%20will%20be%20nullable.

その他メモ

  • 元々Apollo iOSの実装で PossiblyDeferred というものがあって名前ややこしいなと思ったけどリネームされるようだ。
  • クライアント側で解決しないので、サーバー側も実装する必要がある。サーバー側の実装は Transfer-Encoding: chunkedContent-Type: multipart/mixed を組み合わせて通信するのが一般的のようだ。
    • Transfer-Encoding: chunked: 分割して送るパート
    • Content-Type: multipart/mixed: 異なる種類のデータを送るパート

参考