35K Views
April 16, 22
スライド概要
JAWS-UG 浜松 AWS 勉強会 2021#11 2021/11/26
Qiita や Zenn でいろいろ書いてます。 https://qiita.com/hmatsu47 https://zenn.dev/hmatsu47 MySQL 8.0 の薄い本 : https://github.com/hmatsu47/mysql80_no_usui_hon Aurora MySQL v1 → v3 移行計画 : https://zenn.dev/hmatsu47/books/aurora-mysql3-plan-book https://speakerdeck.com/hmatsu47
Amplify Flutter を使おうとしたけれど 微妙な結果に終わった話 JAWS-UG 浜松 AWS 勉強会 2021#11 2021/11/26 まつひさ(hmatsu47)
自己紹介 松久裕保(@hmatsu47) https://qiita.com/hmatsu47 名古屋で Web インフラのお守り係をしています (ほかに書くことがなくなったので省略) 2
おことわり ● ここに書いた内容は私が実際に試した時点の情報です ○ 2021/05 〜 2021/10 頃の話です ○ その後 Amplify CLI のバージョンアップが入っているので、すで に解消されている問題があるかもしれません ○ 実際、最初に書いた「iOS で DataStore のレコード削除をすると 落ちる」問題はその後解消されていました 3
Flutter を試そうと思ったきっかけ ● 以前 Qiita でバズったこのサイト ○ https://korette.jp/ 4
Flutter を試そうと思ったきっかけ ● 以前 Qiita でバズったこのサイト ○ https://korette.jp/ ○ サポーターズの一員として大量にクイズ投稿 5
Flutter を試そうと思ったきっかけ ● 以前 Qiita でバズったこのサイト ○ https://korette.jp/ ○ サポーターズの一員として大量にクイズ投稿 ○ その後、コロナ禍で観光地の状況が一変 ○ コロナが落ち着いた隙をみながら問題メンテナンスの旅へ 6
Flutter を試そうと思ったきっかけ ● 以前 Qiita でバズったこのサイト ○ https://korette.jp/ ○ サポーターズの一員として大量にクイズ投稿 ○ その後、コロナ禍で観光地の状況が一変 ○ コロナが落ち着いた隙をみながら問題メンテナンスの旅へ ○ 旅のお供として、情報収集・整理のためのアプリが欲しい ○ 作ることにした 7
余談ですが ● ご本人は起業されて・・・ ○ https://ambirise.jp/ 8
Amplify Flutter を試そうと思った動機 ● 普通に考えたら Firebase ○ Flutter も Google 謹製 9
Amplify Flutter を試そうと思った動機 ● 普通に考えたら Firebase ○ Flutter も Google 謹製 ○ ありがちで面白くない ○ どうせなら普段使っている AWS で 10
Amplify Flutter を試してみた ● まずはチュートリアルから(英語版) ○ https://docs.amplify.aws/start/getting-started/installation/q/integration/flutter/ ○ Android で DataStore を使った DynamoDB 同期が成功 ○ iOS ではなぜかレコード削除で落ちる 11
Amplify Flutter を試してみた ● まずはチュートリアルから(英語版) ○ https://docs.amplify.aws/start/getting-started/installation/q/integration/flutter/ ○ Android で DataStore を使った DynamoDB 同期が成功 ○ iOS ではなぜかレコード削除で落ちる ○ しばらく放置 12
その後 ● AWS へのデータ同期は一旦置いて、 ○ アプリ本体の開発に着手 ○ https://github.com/hmatsu47/maptool ○ ローカルへのデータ保存に SQLite(sqflite)を使用 13
その後 ● AWS へのデータ同期は一旦置いて、 ○ アプリ本体の開発に着手 ○ https://github.com/hmatsu47/maptool ○ ローカルへのデータ保存に SQLite(sqflite)を使用 ○ ある程度骨格ができたところで、まずは DataStore を導入 ○ 問題発覚 14
自動生成されたデータモデルがエラーに ● 原因は Null Safety ○ Flutter 2.0 で導入されたもの ■ この時点ではエラーにはならず ○ Flutter 2.2 以降で非 Null Safety なデータモデルはエラーに ■ 仕方なく温かみのある手作業で修正 15
自動生成されたデータモデルがエラーに ● 原因は Null Safety ○ Flutter 2.0 で導入されたもの ■ この時点ではエラーにはならず ○ Flutter 2.2 以降で非 Null Safety なデータモデルはエラーに ■ 仕方なく温かみのある手作業で修正 ○ ほどなくして Amplify CLI のバージョンアップ通知が ■ バージョンアップでエラーは解消(ワーニング多くて気持ち悪いけど) 16
自動生成されたデータモデルがエラーに ● 原因は Null Safety ○ Flutter 2.0 で導入されたもの ■ この時点ではエラーにはならず ○ Flutter 2.2 以降で非 Null Safety なデータモデルはエラーに ■ 仕方なく温かみのある手作業で修正 ○ ほどなくして Amplify CLI のバージョンアップ通知が ■ バージョンアップでエラーは解消(ワーニング多くて気持ち悪いけど) ○ そして事件が発生 17
SQLite のテーブルが消失😱 ● DataStore のローカルデータ保存 : SQLite を使用 ○ アプリで実装している DB は DataStore のローカルストアが 初期化される都度、巻き添えをくらって削除される 18
SQLite のテーブルが消失😱 ● DataStore のローカルデータ保存 : SQLite を使用 ○ アプリで実装している DB は DataStore のローカルストアが 初期化される都度、巻き添えをくらって削除される ○ たまたまコピーしてあった SQLite のファイルからローカル DB を復活させた ○ DataStore 使うなら別のローカル DB を使うこともないか・・・ 19
SQLite のテーブルが消失😱 ● DataStore のローカルデータ保存 : SQLite を使用 ○ アプリで実装している DB は DataStore のローカルストアが 初期化される都度、巻き添えをくらって削除される ○ たまたまコピーしてあった SQLite のファイルからローカル DB を復活させた ○ DataStore 使うなら別のローカル DB を使うこともないか・・・ ○ しかし 20
DynamoDB に同期されないテーブルがある ● 3 つあるテーブルのうち最も単純なものを除いて NG ○ 試しにテーブルを 1 つにしても NG 21
DynamoDB に同期されないテーブルがある ● 3 つあるテーブルのうち最も単純なものを除いて NG ○ 試しにテーブルを 1 つにしても NG ○ ローカルの SQLite には正しく保存される ○ AppSync で AWS に送っている形跡はある(ログより) ○ でもテーブルには同期されない ■ 「失敗した」ログは出てくれない 22
DynamoDB に同期されないテーブルがある ● 3 つあるテーブルのうち最も単純なものを除いて NG ○ 試しにテーブルを 1 つにしても NG ○ ローカルの SQLite には正しく保存される ○ AppSync で AWS に送っている形跡はある(ログより) ○ でもテーブルには同期されない ■ 「失敗した」ログは出てくれない 23
DynamoDB に同期されないテーブルがある ● 3 つあるテーブルのうち最も単純なものを除いて NG ○ 試しにテーブルを 1 つにしても NG ○ ローカルの SQLite には正しく保存される ○ AppSync で AWS に送っている形跡はある(ログより) ○ でもテーブルには同期されない ■ 「失敗した」ログは出てくれない 24
DynamoDB に同期されないテーブルがある ● 3 つあるテーブルのうち最も単純なものを除いて NG ○ 試しにテーブルを 1 つにしても NG ○ ローカルの SQLite には正しく保存される ○ AppSync で AWS に送っている形跡はある(ログより) ○ でもテーブルには同期されない ■ 「失敗した」ログは出てくれない 25
DynamoDB に同期されないテーブルがある ● 3 つあるテーブルのうち最も単純なものを除いて NG ○ 試しにテーブルを 1 つにしても NG ○ ローカルの SQLite には正しく保存される ○ AppSync で AWS に送っている形跡はある(ログより) ○ でもテーブルには同期されない ■ 「失敗した」ログは出てくれない ○ 諦めた 26
REST API に変えてみた ● CLI で amplify add api して REST を選んで・・・ ○ API Gateway 経由で Node.js の DynamoDB 保存用 Lambda を 使ってチャレンジ→失敗 ■ JavaScript 版の Amplify ではうまく行くのに・・・ 27
REST API に変えてみた ● CLI で amplify add api して REST を選んで・・・ ○ API Gateway 経由で Node.js の DynamoDB 保存用 Lambda を 使ってチャレンジ→失敗 ■ JavaScript 版の Amplify ではうまく行くのに・・・ ○ Lambda の環境変数名と Node.js に書かれた環境変数を参照して いる部分の記述がなぜか違う ■ REGION と TABLE_REGION(なお、直しても動かず) 28
REST API に変えてみた ● CLI で amplify add api して REST を選んで・・・ ○ API Gateway 経由で Node.js の DynamoDB 保存用 Lambda を 使ってチャレンジ→失敗 ■ JavaScript 版の Amplify ではうまく行くのに・・・ ○ Lambda の環境変数名と Node.js に書かれた環境変数を参照して いる部分の記述がなぜか違う ■ REGION と TABLE_REGION(なお、直しても動かず) ○ 諦めた 29
結局、 ● API Gateway & Lambda Python で回避 ○ これ↓を参考に https://business.ntt-east.co.jp/content/cloudsolution/column-try-20.html ○ コード的には RESTful ではなくなったけどヨシ!! ■ パスパラメータなし ■ 全部 POST ○ クライアント側だけ Amplify Flutter のライブラリを使用 30
結局、
// バックアップ情報を登録
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;
}
}
● API Gateway & Lambda Python で回避
○ これ↓を参考に
https://business.ntt-east.co.jp/content/cloudsolution/column-try-20.html
○ コード的には RESTful ではなくなったけどヨシ!!
■
パスパラメータなし
■
全部 POST
○ クライアント側だけ Amplify Flutter のライブラリを使用
31
結局、
// バックアップ情報リストを 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;
}
● API Gateway & Lambda Python で回避
○ これ↓を参考に
https://business.ntt-east.co.jp/content/cloudsolution/column-try-20.html
○ コード的には RESTful ではなくなったけどヨシ!!
■
パスパラメータなし
■
全部 POST
○ クライアント側だけ Amplify Flutter のライブラリを使用
32
S3 も・・・ ● 個人で使うアプリにわざわざ Cognito を入れたくない ○ Amplify Flutter では Storage(S3)だけ API KEY 認証がない ■ 面倒臭い ○ MinIO の非公式クライアント(minio)で回避 ■ Amplify はどこへ・・・? 33
S3 も・・・
// 画像ファイルを 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;
}
}
● 個人で使うアプリにわざわざ Cognito を入れたくない
○ Amplify Flutter では Storage(S3)だけ API KEY 認証がない
■
面倒臭い
○ MinIO の非公式クライアント(minio)で回避
■
Amplify はどこへ・・・?
34
トドメ ● 距離計測と絡めてデータを取得したい ○ DynamoDB ではできない ○ DocumentDB は最近できるようになったようだけど高い ○ PostGIS 使いたい 35
トドメ ● 距離計測と絡めてデータを取得したい ○ DynamoDB ではできない ○ DocumentDB は最近できるようになったようだけど高い ○ PostGIS 使いたい ○ Supabase 使えばいいじゃん、PostgREST 使えるし https://supabase.com/ ■ Amplify はどこへ・・・?(2 回目) 36
トドメ
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;
}
● 距離計測と絡めてデータを取得したい
○ DynamoDB ではできない
○ DocumentDB は最近できるようになったようだけど高い
○ PostGIS 使いたい
○ Supabase 使えばいいじゃん、PostgREST 使えるし
https://supabase.com/
■
Amplify はどこへ・・・?(2 回目)
37
まとめ ● Amplify : 頻出構成を最速で作るためのもの ○ 自分専用の個人開発アプリとはユースケースが合わなくて当然 ○ ユースケースが合わないものに対して無理に使うと、かえって 開発がつらくなる ○ Flutter 以外の環境と比較するとまだ開発途上→うまく使えなく ても仕方がない 38
おまけ ● 作っているアプリ(maptool) Google Maps Platform ではなく Mapbox の地図を使用 ←訪問(予定)先にピンを立てて、ピンに関連づけて写真撮影 39
おまけ ● 作っているアプリ(maptool) ←地図スタイル切り替え可 40
おまけ ● 作っているアプリ(maptool) ←地図スタイル切り替え可 41
おまけ ● 作っているアプリ(maptool) 42
おまけ ● 作っているアプリ(maptool) ←10km 以内にあるスポットを表示 (ほかにもいくつか機能あり) 43
おまけ ● 関連ブログ記事 ○ 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 44