モチベーション
Apollo iOSのリリースページを見ていたら、preview-defer.1
というものがあった。
実験的に @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はある。
クエリするデータの中で取得に時間がかかるものは後回しにしてそれ以外を早く受け取りたい時がある。そういう時に今までだとクエリの分割やプリフェッチで対応していたが、リクエスト数が増えたり、リクエストが増えるということは個々でレスポンスをハンドリングしないといけなくなる等の課題があった。
解決方法として、 @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。
その他メモ
- 元々Apollo iOSの実装で
PossiblyDeferred
というものがあって名前ややこしいなと思ったけどリネームされるようだ。- https://github.com/apollographql/apollo-ios/issues/3144
PossiblyDeferred
はDataLoaderというクラスでキャッシュの読み取り時の遅延の表現として使われている。まとめてキャッシュ読み取りしているんだけど何故そうなっているのかまでは追えていない...
- クライアント側で解決しないので、サーバー側も実装する必要がある。サーバー側の実装は
Transfer-Encoding: chunked
とContent-Type: multipart/mixed
を組み合わせて通信するのが一般的のようだ。Transfer-Encoding: chunked
: 分割して送るパートContent-Type: multipart/mixed
: 異なる種類のデータを送るパート