Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Amplify Flutter を使おうとしたけど微妙な結果に終わった話

hmatsu47
November 26, 2021

Amplify Flutter を使おうとしたけど微妙な結果に終わった話

JAWS-UG 浜松 AWS 勉強会 2021#11 2021/11/26

hmatsu47

November 26, 2021
Tweet

More Decks by hmatsu47

Other Decks in Technology

Transcript

  1. おことわり • ここに書いた内容は私が実際に試した時点の情報です ◦ 2021/05 〜 2021/10 頃の話です ◦ その後

    Amplify CLI のバージョンアップが入っているので、すで に解消されている問題があるかもしれません ◦ 実際、最初に書いた「iOS で DataStore のレコード削除をすると 落ちる」問題はその後解消されていました 3
  2. Flutter を試そうと思ったきっかけ • 以前 Qiita でバズったこのサイト ◦ https://korette.jp/ ◦ サポーターズの一員として大量にクイズ投稿

    ◦ その後、コロナ禍で観光地の状況が一変 ◦ コロナが落ち着いた隙をみながら問題メンテナンスの旅へ 6
  3. Flutter を試そうと思ったきっかけ • 以前 Qiita でバズったこのサイト ◦ https://korette.jp/ ◦ サポーターズの一員として大量にクイズ投稿

    ◦ その後、コロナ禍で観光地の状況が一変 ◦ コロナが落ち着いた隙をみながら問題メンテナンスの旅へ ◦ 旅のお供として、情報収集・整理のためのアプリが欲しい ◦ 作ることにした 7
  4. Amplify Flutter を試そうと思った動機 • 普通に考えたら Firebase ◦ Flutter も Google

    謹製 ◦ ありがちで面白くない ◦ どうせなら普段使っている AWS で 10
  5. Amplify Flutter を試してみた • まずはチュートリアルから(英語版) ◦ https://docs.amplify.aws/start/getting-started/installation/q/integration/flutter/ ◦ Android で

    DataStore を使った DynamoDB 同期が成功 ◦ iOS ではなぜかレコード削除で落ちる ◦ しばらく放置 12
  6. その後 • AWS へのデータ同期は一旦置いて、 ◦ アプリ本体の開発に着手 ◦ https://github.com/hmatsu47/maptool ◦ ローカルへのデータ保存に

    SQLite(sqflite)を使用 ◦ ある程度骨格ができたところで、まずは DataStore を導入 ◦ 問題発覚 14
  7. 自動生成されたデータモデルがエラーに • 原因は Null Safety ◦ Flutter 2.0 で導入されたもの ▪

    この時点ではエラーにはならず ◦ Flutter 2.2 以降で非 Null Safety なデータモデルはエラーに ▪ 仕方なく温かみのある手作業で修正 15
  8. 自動生成されたデータモデルがエラーに • 原因は Null Safety ◦ Flutter 2.0 で導入されたもの ▪

    この時点ではエラーにはならず ◦ Flutter 2.2 以降で非 Null Safety なデータモデルはエラーに ▪ 仕方なく温かみのある手作業で修正 ◦ ほどなくして Amplify CLI のバージョンアップ通知が ▪ バージョンアップでエラーは解消(ワーニング多くて気持ち悪いけど) 16
  9. 自動生成されたデータモデルがエラーに • 原因は Null Safety ◦ Flutter 2.0 で導入されたもの ▪

    この時点ではエラーにはならず ◦ Flutter 2.2 以降で非 Null Safety なデータモデルはエラーに ▪ 仕方なく温かみのある手作業で修正 ◦ ほどなくして Amplify CLI のバージョンアップ通知が ▪ バージョンアップでエラーは解消(ワーニング多くて気持ち悪いけど) ◦ そして事件が発生 17
  10. SQLite のテーブルが消失😱 • DataStore のローカルデータ保存 : SQLite を使用 ◦ アプリで実装している

    DB は DataStore のローカルストアが 初期化される都度、巻き添えをくらって削除される 18
  11. SQLite のテーブルが消失😱 • DataStore のローカルデータ保存 : SQLite を使用 ◦ アプリで実装している

    DB は DataStore のローカルストアが 初期化される都度、巻き添えをくらって削除される ◦ たまたまコピーしてあった SQLite のファイルからローカル DB を復活させた ◦ DataStore 使うなら別のローカル DB を使うこともないか・・・ 19
  12. SQLite のテーブルが消失😱 • DataStore のローカルデータ保存 : SQLite を使用 ◦ アプリで実装している

    DB は DataStore のローカルストアが 初期化される都度、巻き添えをくらって削除される ◦ たまたまコピーしてあった SQLite のファイルからローカル DB を復活させた ◦ DataStore 使うなら別のローカル DB を使うこともないか・・・ ◦ しかし 20
  13. DynamoDB に同期されないテーブルがある • 3 つあるテーブルのうち最も単純なものを除いて NG ◦ 試しにテーブルを 1 つにしても

    NG ◦ ローカルの SQLite には正しく保存される ◦ AppSync で AWS に送っている形跡はある(ログより) ◦ でもテーブルには同期されない ▪ 「失敗した」ログは出てくれない 22
  14. DynamoDB に同期されないテーブルがある • 3 つあるテーブルのうち最も単純なものを除いて NG ◦ 試しにテーブルを 1 つにしても

    NG ◦ ローカルの SQLite には正しく保存される ◦ AppSync で AWS に送っている形跡はある(ログより) ◦ でもテーブルには同期されない ▪ 「失敗した」ログは出てくれない 23
  15. DynamoDB に同期されないテーブルがある • 3 つあるテーブルのうち最も単純なものを除いて NG ◦ 試しにテーブルを 1 つにしても

    NG ◦ ローカルの SQLite には正しく保存される ◦ AppSync で AWS に送っている形跡はある(ログより) ◦ でもテーブルには同期されない ▪ 「失敗した」ログは出てくれない 24
  16. DynamoDB に同期されないテーブルがある • 3 つあるテーブルのうち最も単純なものを除いて NG ◦ 試しにテーブルを 1 つにしても

    NG ◦ ローカルの SQLite には正しく保存される ◦ AppSync で AWS に送っている形跡はある(ログより) ◦ でもテーブルには同期されない ▪ 「失敗した」ログは出てくれない 25
  17. DynamoDB に同期されないテーブルがある • 3 つあるテーブルのうち最も単純なものを除いて NG ◦ 試しにテーブルを 1 つにしても

    NG ◦ ローカルの SQLite には正しく保存される ◦ AppSync で AWS に送っている形跡はある(ログより) ◦ でもテーブルには同期されない ▪ 「失敗した」ログは出てくれない ◦ 諦めた 26
  18. REST API に変えてみた • CLI で amplify add api して

    REST を選んで・・・ ◦ API Gateway 経由で Node.js の DynamoDB 保存用 Lambda を 使ってチャレンジ→失敗 ▪ JavaScript 版の Amplify ではうまく行くのに・・・ 27
  19. REST API に変えてみた • CLI で amplify add api して

    REST を選んで・・・ ◦ API Gateway 経由で Node.js の DynamoDB 保存用 Lambda を 使ってチャレンジ→失敗 ▪ JavaScript 版の Amplify ではうまく行くのに・・・ ◦ Lambda の環境変数名と Node.js に書かれた環境変数を参照して いる部分の記述がなぜか違う ▪ REGION と TABLE_REGION(なお、直しても動かず) 28
  20. REST API に変えてみた • CLI で amplify add api して

    REST を選んで・・・ ◦ API Gateway 経由で Node.js の DynamoDB 保存用 Lambda を 使ってチャレンジ→失敗 ▪ JavaScript 版の Amplify ではうまく行くのに・・・ ◦ Lambda の環境変数名と Node.js に書かれた環境変数を参照して いる部分の記述がなぜか違う ▪ REGION と TABLE_REGION(なお、直しても動かず) ◦ 諦めた 29
  21. 結局、 • API Gateway & Lambda Python で回避 ◦ これ↓を参考に

    https://business.ntt-east.co.jp/content/cloudsolution/column-try-20.html ◦ コード的には RESTful ではなくなったけどヨシ!! ▪ パスパラメータなし ▪ 全部 POST ◦ クライアント側だけ Amplify Flutter のライブラリを使用 30
  22. 結局、 • API Gateway & Lambda Python で回避 ◦ これ↓を参考に

    https://business.ntt-east.co.jp/content/cloudsolution/column-try-20.html ◦ コード的には RESTful ではなくなったけどヨシ!! ▪ パスパラメータなし ▪ 全部 POST ◦ クライアント側だけ Amplify Flutter のライブラリを使用 31 // バックアップ情報を登録 Future<bool> backupSet( AmplifyClass amplify, String backupTitle, String describe) async { try { final RestOptions options = RestOptions( path: '/backupsets', body: const Utf8Encoder() .convert(('{"OperationType": "PUT", "Keys": {"items": [' ' {"title": ${jsonEncode(backupTitle)}' ', "describe": ${jsonEncode(describe)}}' ']}}'))); final RestOperation restOperation = amplify.API.post(restOptions: options); await restOperation.response; // ignore: avoid_print print('POST call (/backupsets) succeeded'); return true; } on ApiException catch (e) { // ignore: avoid_print print('POST call (/backupsets) failed: $e'); return false; } }
  23. 結局、 • API Gateway & Lambda Python で回避 ◦ これ↓を参考に

    https://business.ntt-east.co.jp/content/cloudsolution/column-try-20.html ◦ コード的には RESTful ではなくなったけどヨシ!! ▪ パスパラメータなし ▪ 全部 POST ◦ クライアント側だけ Amplify Flutter のライブラリを使用 32 // バックアップ情報リストを AWS から取得 Future<List<BackupSet>> fetchBackupSets(AmplifyClass amplify) async { final List<BackupSet> resultList = []; try { final RestOptions options = RestOptions( path: '/backupsets', body: const Utf8Encoder().convert(('{"OperationType": "SCAN"}'))); final RestOperation restOperation = amplify.API.post(restOptions: options); final RestResponse response = await restOperation.response; final Map<String, dynamic> body = json.decode(response.body); final List<dynamic> items = body['Items']; for (dynamic item in items) { resultList.add(BackupSet(item['title'] as String, item['describe'])); } resultList.sort((a, b) => b.title.compareTo(a.title)); // ignore: avoid_print print('POST call (/backupsets) succeeded'); } catch (e) { // ignore: avoid_print print('POST call (/backupsets) failed: $e'); } return resultList; }
  24. S3 も・・・ • 個人で使うアプリにわざわざ Cognito を入れたくない ◦ Amplify Flutter では

    Storage(S3)だけ API KEY 認証がない ▪ 面倒臭い ◦ MinIO の非公式クライアント(minio)で回避 ▪ Amplify はどこへ・・・? 33
  25. S3 も・・・ • 個人で使うアプリにわざわざ Cognito を入れたくない ◦ Amplify Flutter では

    Storage(S3)だけ API KEY 認証がない ▪ 面倒臭い ◦ MinIO の非公式クライアント(minio)で回避 ▪ Amplify はどこへ・・・? 34 // 画像ファイルを S3 アップロード _uploadS3( Minio minio, Picture picture, String imagePath, String s3Bucket) async { final int pathIndexOf = picture.filePath.lastIndexOf('/'); final String fileName = (pathIndexOf == -1 ? picture.filePath : picture.filePath.substring(pathIndexOf + 1)); final String filePath = '$imagePath/$fileName'; try { await minio.fPutObject(s3Bucket, fileName, filePath); // ignore: avoid_print print('S3 upload $fileName succeeded'); return fileName; } catch (e) { // ignore: avoid_print print('S3 upload $fileName failed: $e'); return false; } }
  26. トドメ • 距離計測と絡めてデータを取得したい ◦ DynamoDB ではできない ◦ DocumentDB は最近できるようになったようだけど高い ◦

    PostGIS 使いたい ◦ Supabase 使えばいいじゃん、PostgREST 使えるし https://supabase.com/ ▪ Amplify はどこへ・・・?(2 回目) 36
  27. トドメ • 距離計測と絡めてデータを取得したい ◦ DynamoDB ではできない ◦ DocumentDB は最近できるようになったようだけど高い ◦

    PostGIS 使いたい ◦ Supabase 使えばいいじゃん、PostgREST 使えるし https://supabase.com/ ▪ Amplify はどこへ・・・?(2 回目) 37 Future<List<SpotData>> searchNearSpot( SupabaseClient client, LatLng latLng, int distLimit) async { final PostgrestResponse selectResponse = await client.rpc('get_spots', params: { 'point_latitude': latLng.latitude, 'point_longitude': latLng.longitude, 'dist_limit': distLimit }).execute(); final List<dynamic> items = selectResponse.data; final List<SpotData> resultList = []; for (dynamic item in items) { final SpotData spotData = SpotData( item['distance'] as num, item['category_name'] as String, item['title'] as String, item['describe'] as String, LatLng((item['latitude'] as num).toDouble(), (item['longitude'] as num).toDouble()), PrefMuni(item['prefecture'] as String, item['municipality'] as String)); resultList.add(spotData); } return resultList; }
  28. おまけ • 作っているアプリ(maptool) 39  Google Maps Platform ではなく Mapbox の地図を使用

    ←訪問(予定)先にピンを立てて、ピンに関連づけて写真撮影
  29. おまけ 44 • 関連ブログ記事 ◦ https://qiita.com/hmatsu47/items/b98ef4c1a87cc0ec415d ◦ https://zenn.dev/hmatsu47/articles/846c3186f5b4fe ◦ https://zenn.dev/hmatsu47/articles/9102fb79a99a98

    ◦ https://zenn.dev/hmatsu47/articles/e81bf3c2bf00f8 ◦ https://qiita.com/hmatsu47/items/e4f7e310e88376d54009 ◦ https://qiita.com/hmatsu47/items/86a9c028bb5b3beeebdd ◦ https://qiita.com/hmatsu47/items/53ea68769c4fc2d76450