目的
- デバイス間でデータ同期したいときに、個人でやるにはバックエンド全部開発するのは辛いなぁというモチベーション
- 会社でfirestore使っているサービスをみて、自分もちょっと触れておきたくなった
やったこと
- データのread
- データのcreate
- データのリアルタイム更新
事前準備
コンソール側でルールを追記する。
開発用に認証無しでのアクセスを許可するのもあり。ただしリスクが有るのと後から更新し忘れると辛いので、先に設定するのを推奨。
※一応、未認証アクセスを許可した場合、一定期間後に自動的にアクセス不可に更新される仕組みはある。
先にAuthの実装はしておいたので、認証ありの場合のみ許可する設定にしておいた。追加で、、実装後使わないことを想定してある程度の期間後にアクセスできないようにしておいた。
データのReadとリアルタイム更新
samples
というcollectionを読み込むwidgetの例。StreamBuilderを使っているので、更新通知を受け取るとこれだけで中身が変わる。
@override Widget build(BuildContext context) { final samples = FirebaseFirestore.instance.collection('samples').snapshots(); return StreamBuilder<QuerySnapshot>( stream: samples, builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) { if (snapshot.hasError) { return Text("Something went wrong"); } if (snapshot.connectionState == ConnectionState.waiting) { return Text("Loading"); } final listTiles = snapshot.data!.docs.map((element) { Map<String, dynamic> data = element.data()! as Map<String, dynamic>; return ListTile( title: Text(data['title']), subtitle: Text(data['body']), ); }).toList(); return Column( children: listTiles, ); }, ); }
データのCreate
fabのタップで先程readしていたsamplesにデータを一つ追加してる。タップするとリアルタイム更新が動いていた。
final CollectionReference _samples = FirebaseFirestore.instance.collection('samples'); @override Widget build(BuildContext context) { return Scaffold( // 色々省略 floatingActionButton: FloatingActionButton( onPressed: () { _samples .add({ 'title': "added title ${Random().nextInt(10000)}", 'body': "added body ${Random().nextInt(10000)}", }) .then((value) => print("add data")) .catchError((error) => print("Failed to add user: $error")); }, tooltip: 'add sample data', child: const Icon(Icons.add), ), ); }
所感
- リアルタイム更新はちょっと感動した。要件として上がった場合に選択肢として上がりそう
- 一方で、RDBで無いためコレクション内のドキュメントが同じフィールドを持つことを保証していないのが厄介。意識して処理をまとめるようにしないと関数によって登録されるデータ構造が変わったり、後から仕様変更しようとしたときに負債になりそう
- モバイルとWebでバラバラの実装になっているとそのへんが辛そう。実際会社のプロダクトでもそういう不具合が発生している。
- Flutterのような、そもそもプラットフォームの違いはフレームワークで吸収してアクセスするロジックを高いレベルでまとめることができると、開発効率が一気に上がりそう
- サブコレクションの更新など、データのリレーションを意識したデータ同期は大変そう。そういうのもやろうとすると、CloudFunction経由でのアクセスも検討したほうが良いかもしれない