TL;DR;
- HiveStoreを使う
- HiveStoreの初期化は非同期処理のためgraphqlのproviderがFutureとなってしまいめんどくさい
- 上記解消のために main.dart で 初期化を終わらせ ProviderScopeでoverrideする
環境
[✓] Flutter (Channel stable, 2.2.3, on macOS 11.3.1 20E241 darwin-x64, locale ja-JP) [✓] Xcode - develop for iOS and macOS [✓] Chrome - develop for the web [✓] Android Studio (version 2020.3) [✓] IntelliJ IDEA Ultimate Edition (version 2021.1.2) [✓] VS Code (version 1.59.1) hooks_riverpod: 0.14.0+4 graphql: 5.0.0
Cash Persistence
GraphqlClientはクライアント内でキャッシュを管理してくれる。(やりようによっては正規化までサポートしている、素晴らしい) デフォルトだとインメモリキャッシュだが、DBを使用した永続化も可能。 透過的なキャッシュを実装する上では理想的だと思う。
GraphqlClientでキャッシュを永続化するにはHiveStoreを使う。
GraphqlFlutterであれば初期化処理は initHiveForFlutter()
をmain.dartで呼べば済むが、Graphqlを使っている場合はもう少し手間がかかる。
一例だが下記のようになる。DB保存先のPathを取得し、HiveStoreをopenする。デフォルトでは 内部で保持しているBoxの名称がgraphqlClientStore
となるが、
openの引数にnameがあるため変更もできる。
GraphQLClientの生成時のcacheにGraphQLCache(store: hiveStore)を入れればOK。
final appDir = await getApplicationDocumentsDirectory(); final path = appDir.path; final store = await HiveStore.open(path: path); final client = GraphQLClient( link: authLink.concat(_baseLink), cache: GraphQLCache(store: store));
RiverpodでDIしてる時
RiverpodでDIしている時は、graphqlClientもprovider経由で取得したい。 ただ、hiveStoreの初期化が非同期なので素直にやるとFutureProviderとなってしまい、使用する側がとてもめんどくさくなってしまう。 SharedPreferencesを使用する場合と同様の問題が起きる。
そこで hiveStoreを渡すようのproviderを作成し、main.dartでhiveStoreの初期化を実施、provderをProvderScope内でoverrideすることで非同期処理を吸収してしまう。
final hiveStoreProvider = Provider<HiveStore>((ref) { throw Exception('Provider was not initialized'); }); Future<void> main() async { ~~~~ var appDir = await getApplicationDocumentsDirectory(); var path = appDir.path; final store = await HiveStore.open(path: path); runApp( ProviderScope( overrides: [ hiveStoreProvider.overrideWithValue(store), ], child: App(), ), ); ~~~~ }
上記の対応をすることで、graphqlClientのproviderは下記のように同期的に呼び出せるようになる。
final gqlClientProvider = Provider((ref) => AppGqlClient((ref.read)));
最後に
GraphqlClient便利。