Help us understand the problem. What is going on with this article?

AWS AmplifyとIonicを組み合わせてモバイルアプリまで爆速で開発する

はじめまして。Advent Calendar初参加になります!AWS Amplifyは一年前くらいから触り始めてその便利さから愛用しているので、今回はその知見をアウトプットできたらと思います。

目黒にあるAWS Loft TokyoでAWS Amplifyに出会った

1年ほど前、東京都の目黒駅徒歩1分の場所に「AWS Loft Tokyo」なる施設がオープンしました。
スタートアップとデベロッパーのための「挑戦をカタチにする場所」というコンセプトです。

https://aws.amazon.com/jp/start-ups/loft/tokyo/

今回のAWS Amplify Advent Calendarを作ったのもスタートアップソリューションアーキテクトの塚田さん( @akitsukada )ですね。

Loftにはオープンから足を運んでおり、コワーキング利用終了後に毎週のように開催される技術系イベントに参加していました。
参加したイベントの一つが「AWS Amplify Blackbelt公開収録 & AppSync入門」です。

Ionic(アイオニック)とは

https://ionicframework.com/

Ionicとは、HTML / CSS / JSの技術でモバイルアプリを作るためのUIフレームワークと言ったところでしょうか。iOS / AndroidのUIに極限まで近づけたUIコンポーネントが提供されており、美しいUIのアプリを素早く構築することができます。

CordovaやCapacitorを使うことで、Ionicで作ったアプリをiOS / Android用アプリとしてビルドしてApp Store / Google Playに並べることができます。

僕は3年ほどスタートアップで一人エンジニアをやっていたので、iOS / Androidアプリを一気に構築できるIonicがとても気に入っていました。

そこにAWS Amplifyが加わることで、フロントエンドからバックエンドまで一人で作ることができるくらいスピードを上げることができます。

今回はそんなIonicで作ったアプリにAWS Amplifyを組み込んで使うための手順をまとめようと思います。

動作確認環境

  • macOS Catalina 10.15
  • VS Code
  • node.js v12.7.0
  • @angular/core 8.1.2
  • @ionic/angular 4.7.1
  • aws-amplify 1.2.4
  • aws-amplify-angular 4.7.1
  • @amplify/cli 3.17.0

Ionicプロジェクトを新しく作成する

まずはIonic CLIを利用してプロジェクトを生成します。
npx を使うことでIonic CLIをグローバルインストールしていなくてもコマンドを利用できます。

# CLIでプロジェクトを作成開始
$ npx ionic start

# プロジェクト名を指定
? Project name: AmplifyIonicChat

# JSフレームワークを選択:今回はAngularを利用
? Framework: Angular

# テンプレートを選択
? Starter template: blank

# ディレクトリに移動
$ cd AmplifyIonicChat

この時点でのディレクトリ構造は次のようになります。
基本的にはAngular CLIで生成されるディレクトリと同じです。

$ tree -I node_modules
.
├── angular.json
├── browserslist
├── e2e
│   ├── protractor.conf.js
│   ├── src
│   │   ├── app.e2e-spec.ts
│   │   └── app.po.ts
│   └── tsconfig.json
├── ionic.config.json
├── karma.conf.js
├── package-lock.json
├── package.json
├── src
│   ├── app
│   │   ├── app-routing.module.ts
│   │   ├── app.component.html
│   │   ├── app.component.scss
│   │   ├── app.component.spec.ts
│   │   ├── app.component.ts
│   │   ├── app.module.ts
│   │   └── home
│   │       ├── home.module.ts
│   │       ├── home.page.html
│   │       ├── home.page.scss
│   │       ├── home.page.spec.ts
│   │       └── home.page.ts
│   ├── assets
│   │   ├── icon
│   │   │   └── favicon.png
│   │   └── shapes.svg
│   ├── environments
│   │   ├── environment.prod.ts
│   │   └── environment.ts
│   ├── global.scss
│   ├── index.html
│   ├── main.ts
│   ├── polyfills.ts
│   ├── test.ts
│   ├── theme
│   │   └── variables.scss
│   └── zone-flags.ts
├── tsconfig.app.json
├── tsconfig.json
├── tsconfig.spec.json
└── tslint.json

これだけでもう動作確認ができるので、AWS Amplify JavaScript Frameworkを入れる前に一旦動かしてみます。

# 開発サーバーを起動
$ npx ionic serve

自動でデフォルトのブラウザが開き、アプリケーションが表示されます。
これだけでiOS端末ではiOSのスタイルが、Android端末ではマテリアルデザインのスタイルが適用されたアプリケーションの雛形が出来上がります。

amplify_01.png

AWS Amplify JavaScript Frameworkをインストール

Ionicアプリケーションの準備が整ったので、ここから本題のAWS Amplifyを導入します。
公式ドキュメントがAngular / Ionicでの導入方法を出してくれているので、それに従います。

https://aws-amplify.github.io/docs/js/angular

AWS Amplify関連パッケージのインストール

まずAWS Amplify関連のパッケージをインストールします。 aws-amplify-angular というパッケージがあるのでそれを使います。

$ npm install aws-amplify aws-amplify-angular 

Angular 6以上で必要となる設定を書く

window.globalwindow.process がセットされている必要があるとのことです。指示に従って以下の設定を src/polyfills.ts に追記します。

src/polyfills.tsに追加する内容
(window as any).global = window;
(window as any).process = {
  env: { DEBUG: undefined },
};

src/polyfills.ts は最終的には以下のようになりました。Ionic CLI(Angular CLI)が説明のためのコメントを大量に書いてくれていますが、見やすくするために全て削除しています。

編集後のsrc/polyfills.ts
import './zone-flags.ts';
import 'zone.js/dist/zone';

(window as any).global = window;
(window as any).process = {
  env: { DEBUG: undefined },
};

余談ですが、公式ドキュメントに、

Please also note that the AWS Amplify Angular components do not yet support Ivy.

と書いてありました。Ivyのサポートはまだ無いようですね...

TypeScriptのコンパイルオプションを調整する

src/tsconfig.app.json 内の compilerOptions セクションの typesnode を追加します。ちなみに、 src/tsconfig.json というファイルもあるので混同しないよう注意が必要です。

編集後のsrc/tsconfig.app.json
{
  "extends": "./tsconfig.json",
  "compilerOptions": {
    "outDir": "./out-tsc/app",
    "types": ["node"]
  },
  "include": [
    "src/**/*.ts"
  ],
  "exclude": [
    "src/test.ts",
    "src/**/*.spec.ts"
  ]
}

これでIonicアプリへのAWS Amplify JavaScript Frameworkのインストールは完了です。

AWS Amplifyの初期設定を行う

AWS Amplify CLIを使って初期設定を行います。初期設定はプロジェクトごとに行う必要がある操作になります。

# Amplify CLIの初期設定を開始
$ npx amplify init

# プロジェクト名を指定
? Enter a name for the project AmplifyIonicChat

# 環境名を指定
? Enter a name for the environment dev

# エディタを指定
? Choose your default editor: Visual Studio Code

# 開発に使う言語を指定
? Choose the type of app that you re building javascript

# 開発に使うフレームワークを指定
? What javascript framework are you using ionic

# ソースコードのディレクトリを指定
? Source Directory Path:  src

# ビルド後の成果物の格納先ディレクトリを指定
? Distribution Directory Path: www

# ビルドコマンドを指定
? Build Command:  npm run-script build

# 開発サーバー起動コマンドを指定
? Start Command: ionic serve

# Amplify CLIが使うAWSプロファイルを使う
? Do you want to use an AWS profile? Yes

# プロファイルを指定:今回は個人用のAWSに繋がるプロファイルとして用意してある「private」を指定
? Please choose the profile you want to use private

ここまで指定するとCloudFormationが起動して、バックエンドに必要なリソースを生成します。

ここでは以下のリソースが作成されます。「この時点で作るリソースあるの?」という感じですが、何か意図があるのでしょうか。。。

  • S3バケット
  • Cognitoフェデレーテッドアイデンティティでの認証済みクライアントが引き受けるロール(AuthRole)
  • Cognitoフェデレーテッドアイデンティティでの未認証クライアントが引き受けるロール(UnauthRole)

AWS AmplifyのAuthenticationカテゴリを使ってユーザー認証を実装する

ここからはAWS Amplify CLIを使ってAWSクラウド上にバックエンドを構築していきます。

Authenticationカテゴリの設定をAmplify CLIで構築する

まずはアプリケーションにサインアップ・サインイン機能を作るために、AWS AmplifyのAuthenticationカテゴリをセットアップします。

こちらは公式ドキュメントの以下の箇所に沿って進めます。

https://aws-amplify.github.io/docs/js/authentication#automated-setup

# Amplify CLIを使ってAuthenticationカテゴリを追加
$ npx amplify add auth

# 認証の設定をどのように行うか指定:今回はデフォルトの設定を利用
Do you want to use the default authentication and security configuration? Default configuration

# サインインに使える属性を指定
How do you want users to be able to sign in? Username

# 追加の設定をするかどうかを指定
Do you want to configure advanced settings? No, I am done.

これで設定は完了です。この時点ではまだバックエンドに設定は適用されていません。

設定をバックエンドに適用する

では、実際に設定をバックエンドに適用します。

# 変更をバックエンドに適用
$ npx amplify push

# 変更内容が出るので、確認して先へ進む
Current Environment: dev

| Category | Resource name            | Operation | Provider plugin   |
| -------- | ------------------------ | --------- | ----------------- |
| Auth     | amplifyionicchatd1aa1b6e | Create    | awscloudformation |

? Are you sure you want to continue? (Y/n)

マネジメントコンソールで確認すると、Amazon Congitoのユーザープールが作成されていることがわかります。

amplify_02.png

Ionicアプリにバックエンドへの接続情報を適用する

先ほどの $ npx amplify push コマンド実行のタイミングで、バックエンドの接続情報(CognitoのユーザープールIDやアイデンティティプールID等)が src/aws-export.js というファイルに出力されました。

aws-export.js は以下のようなシンプルなJSファイルです。

// WARNING: DO NOT EDIT. This file is automatically generated by AWS Amplify. It will be overwritten.

const awsmobile = {
    "aws_project_region": "ap-northeast-1",
    "aws_cognito_identity_pool_id": "<アイデンティティプールID>",
    "aws_cognito_region": "ap-northeast-1",
    "aws_user_pools_id": "<ユーザープールID>",
    "aws_user_pools_web_client_id": "<ユーザープールクライアントID>",
    "oauth": {}
};


export default awsmobile;

1行目にコメントで書いてある通り、このファイルは今後の $ npx amplify push コマンド実行時に上書きされるので、手作業で変更してはいけません。

Amplify Frameworkを初期化する

Amplify Frameworkに aws-exports.js に書かれた設定情報を読み込ませるため、 src/main.ts に以下のようにAmplifyの初期化処理を書きます。

src/main.ts
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';

import { AppModule } from './app/app.module';
import { environment } from './environments/environment';

// 以下3行を追加
import Amplify from 'aws-amplify';
import awsconfig from './aws-exports';
Amplify.configure(awsconfig);

if (environment.production) {
  enableProdMode();
}

platformBrowserDynamic().bootstrapModule(AppModule)
  .catch(err => console.log(err));

ちなみに、公式ドキュメントでは、 Amplify のインポート元は @aws-amplify/core となっていますが、そのまま書いてもうまくいかず以下のようなエラーが出てしまいます。

amplify_04.png

これを解消するために、 Amplifyaws-amplify から読み込むようにしています。

これでフロントエンドとバックエンドの接続が完了です。

Angularのモジュールの整理をしておく

ここで一旦今後のためにAngularプロジェクトとしての準備を行います。

  • 未ログイン状態のときにアクセスできるページ群を管理する PublicModule を作成
  • ログイン状態のときにアクセスできるページ群を管理する ClosedModule を作成
  • アプリケーション全体から利用するモジュールをまとめた SharedModule を作成

Angularの「モジュール」

Angularでは、アプリケーションを「モジュール」という単位に分割できます。ページコンポーネントを一つのモジュールとして作成しておくことで、そのページが必要となったタイミングでモジュールごと読み込む遅延読み込み(Lazy Loading)ができます。

モジュールの定義には @NgModule デコレータを使います。

あるモジュールAであるモジュールBを使いたい場合、モジュールBの定義箇所の @NgModule の宣言内の imports にモジュールAを指定します。

Angularには最上位のモジュールである AppModule が存在します。
そして、 $ ionic start コマンドでBlankテンプレートを選択して作成した雛形には、Homeページ用の HomeModule も生成されています。

未ログイン状態でアクセスできるページ群を管理するPublicModuleを作成する

Ionic CLIを使ってモジュールを作成します。

$ npx ionic g module pages/public

これで、 src/app/pages/public/public.module.ts が作成されます。中身は以下のようにしました。ルーティングは後ほど追加していくので、まだ何も作成していません。

src/app/pages/public/public.module.ts
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

// ここにルーティングの定義を書いていく
const routes: Routes = [];

@NgModule({
  imports: [RouterModule.forChild(routes)]
})
export class PublicModule { }

ログイン状態でアクセスできるページ群を管理するClosedModuleを作成する

PublicModule と同様にモジュールを作成します。

$ npx ionic g module pages/closed
src/app/pages/closed/closed.module.ts
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

// ここにルーティングの定義を書いていく
const routes: Routes = [];

@NgModule({
  imports: [RouterModule.forChild(routes)]
})
export class ClosedModule { }

AppRoutingModuleにPublicModuleとClosedModuleを追加する

最後に、アプリケーションのルートのルーティング定義である AppRoutingModulePublicModuleClosedModule を登録します。
これで PublicModuleClosedModule に書いたルーティングが有効になります。

src/app/app-routing.module.ts
import { NgModule } from '@angular/core';
import { PreloadAllModules, RouterModule, Routes } from '@angular/router';

const routes: Routes = [
  // 直下へのアクセスは /auth へリダイレクト
  { path: '', redirectTo: 'auth', pathMatch: 'full' },
  // 未ログイン状態でアクセスできる画面群のルーティング
  {
    path: '',
    loadChildren: () => import('./pages/public/public.module').then(m => m.PublicModule)
  },
  // ログイン状態でアクセスできる画面群のルーティング
  {
    path: '',
    loadChildren: () => import('./pages/closed/closed.module').then(m => m.ClosedModule)
  },
];

@NgModule({
  imports: [
    RouterModule.forRoot(routes, { preloadingStrategy: PreloadAllModules })
  ],
  exports: [RouterModule]
})
export class AppRoutingModule { }

Angularでは、URLでの画面遷移を許可するかどうかを制御する「ガード」という仕組みがあります。このガードを PublicModuleClosedModule にかけることで、ログイン状態のときはサインアップフォームへのアクセスはリダイレクト、未ログイン状態のときはサインアップフォームへリダイレクトといった制御をきれいに書くことができます。

アプリケーション全体で使うモジュールをまとめたSharedModuleを作る

前述の通り各ページはそれぞれ独立したモジュールになっているので、それらのモジュールで共通したい処理はまとめておくと楽です。
全ページで使う IonicModuleFormsModule 等は全て SharedModule に含めてしまい、各ページのモジュールからはこの SharedModule のみインポートするようにします。

$ npx ionic g module shared

これで、 src/app/shared/shared.module.ts が生成されました。

内容は以下のようにします。

src/app/shared/shared.module.ts
import { NgModule } from '@angular/core';
import { IonicModule } from '@ionic/angular';
import { FormsModule } from '@angular/forms';
import { CommonModule } from '@angular/common';
import { AmplifyIonicModule, AmplifyService } from 'aws-amplify-angular';

const modules = [
  FormsModule,
  IonicModule,
  CommonModule,
  AmplifyIonicModule,
];

@NgModule({
  imports: [...modules],
  exports: [...modules],
  providers: [AmplifyService],
})
export class SharedModule { }

importを絶対パスで書けるようにする

ソースコードをディレクトリで整理していくと、 import 文が以下のようになったりします。

import { MyService } from '../../../../../services/myservice/myservice.service';

これだと辛いので、プロジェクトのルートディレクトリから絶対パスで指定できるようにしておきます。

tsconfig.jsoncompilerOptionspaths を指定します。

tsconfig.json
{
  "compilerOptions": {
    (...略)
    "paths": {
      "@/*": ["src/*"]
    },
    (...略)
  }
}

この設定を書くことによって、先ほどの import 文は以下のように書くことができます。

import { MyService } from '@/services/myservice/myservice.service`;

雛形で生成されていたAppModuleを調整する

後ほど AppComponent でAWS Amplifyが提供する AmplifyService を使うので、AppModule でも SharedModule をインポートするようにしておきます。

同時に雛形で生成されていた不要なモジュールのインポートも削除します。

src/app/app.module.ts
import { NgModule } from '@angular/core';
import { RouteReuseStrategy } from '@angular/router';
import { BrowserModule } from '@angular/platform-browser';

import { IonicModule, IonicRouteStrategy } from '@ionic/angular';

import { AppComponent } from './app.component';
import { AppRoutingModule } from './app-routing.module';

import { SharedModule } from './shared/shared.module';

@NgModule({
  declarations: [AppComponent],
  imports: [
    BrowserModule,
    SharedModule,
    IonicModule.forRoot(),
    AppRoutingModule,
  ],
  providers: [
    { provide: RouteReuseStrategy, useClass: IonicRouteStrategy }
  ],
  bootstrap: [AppComponent]
})
export class AppModule {}

雛形で生成されていたHomePageを調整する

最後に、整理したモジュールの構造の中に自動生成された HomePage を入れます。
生成されたときは src/app/home/ にあった HomePage の一式を src/app/pages/closed/ に移します。

そして、 src/app/pages/closed/home/home.module.ts を以下のように調整します。
FormsModule / IonicModule / CommonModule のインポートを SharedModule のインポートに置き換えました)

src/app/pages/closed/home/home.module.ts
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';

import { HomePage } from './home.page';
import { SharedModule } from '@/app/shared/shared.module';

@NgModule({
  imports: [
    SharedModule,
    RouterModule.forChild([{ path: '', component: HomePage }])
  ],
  declarations: [HomePage]
})
export class HomePageModule {}

最後に、 ClosedModuleHomePage のルーティングを定義します。

src/pages/closed/closed.module.ts
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

const routes: Routes = [
  {
    path: 'home',
    loadChildren: () => import('./home/home.module').then(m => m.HomePageModule),
  },
];

@NgModule({
  imports: [
    RouterModule.forChild(routes),
  ]
})
export class ClosedModule { }

結構調整作業が多くなってしまいました。本稿の主役はAWS Amplifyですが、せっかくなのである程度アプリケーションとしてスケールさせられる体制を整える方法まで書きたいと思っています。

認証を扱うページを生成する

先ほどバックエンドにAWSの認証基盤であるAmazon Cognitoを構築できたので、先ほど作ったIonicアプリでサインアップ・サインインできるようにします。

AWS Amplifyのすごいところは、サインイン周りを一発で実装できるコンポーネントを各フレームワークで用意してくれているところです(!)

具体的には、 <amplify-authenticator> というコンポーネントが提供されていて、これをアプリケーション内に配置するとサインインフォームが出現します。

認証をWebアプリケーションに組み込む場合、

  • サインアップ画面は /sign-up
  • サインイン画面は /sign-in
  • パスワードリマインダーは /password

のようにそれぞれURLを分けることが一般的かと思います。

しかし、今回は認証周りを <amplify-authenticator> コンポーネントを利用して行うため、認証を扱う画面は1つあれば十分です。

認証機能を配置するページを作成する

Ionic CLIを使って認証用の画面を用意します。CLIを使ってページを生成する場合、自動的にモジュールが作成され、遅延読み込みが有効になります。

認証を扱う画面は未ログイン状態でのみ閲覧可能としたいので、先ほど作成した PublicModule 以下にページのモジュールを作成します。

$ npx ionic g page pages/public/auth

これで以下のファイルが出来上がりました。

src/app/pages/public/auth/auth.page.ts
src/app/pages/public/auth/auth.page.spec.ts
src/app/pages/public/auth/auth.page.html
src/app/pages/public/auth/auth.page.scss
src/app/pages/public/auth/auth.module.ts
src/app/pages/public/auth/auth-routing.module.ts

今回はルーターの設定も auth.module.ts に書いていくことにするので、 auth-routing.module.ts は削除します。
認証用のページのモジュールである auth.module.ts は以下のように記述します。

src/app/pages/public/auth/auth.module.ts
import { RouterModule } from '@angular/router';
import { NgModule } from '@angular/core';
import { SharedModule } from '@/app/shared/shared.module';

import { AuthPage } from './auth.page';

@NgModule({
  imports: [
    SharedModule,
    RouterModule.forChild([{ path: '', component: AuthPage }]),
  ],
  declarations: [AuthPage]
})
export class AuthPageModule {}

最後に、 /auth でこのページにアクセスできるよう、 PublicModule にルーティング定義を追加します。

src/pages/public/public.module.ts
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

const routes: Routes = [
  {
    path: 'auth',
    loadChildren: () => import('./auth/auth.module').then(m => m.AuthPageModule),
  },
];

@NgModule({
  imports: [
    RouterModule.forChild(routes),
  ]
})
export class PublicModule { }

これで /auth ページの準備は完了です。 http://localhost:8100/auth にブラウザでアクセスすると以下のような画面が表示されます。

amplify_03.png

AWS Amplifyが提供してくれている認証コンポーネントを配置する

では、作成した AuthPage にAWS Amplifyが提供してくれている <amplify-authenticator-ionic> コンポーネントを配置してみます。

src/pages/public/auth/auth.page.html
<ion-header>
  <ion-toolbar>
    <ion-title>auth</ion-title>
  </ion-toolbar>
</ion-header>

<ion-content>
  <div class="ion-padding">
    <amplify-authenticator-ionic></amplify-authenticator-ionic>
  </div>
</ion-content>

Ionicを使わない素のAngularの場合、 <amplify-authenticator> コンポーネントを利用しますが、Ionicを使う場合は専用の <amplify-authenticator-ionic> コンポーネントを使うことができます。

なんとこれだけで認証周りの画面の実装が完了します。

amplify_05.png

試しにサインアップしてみる

表示されているサインイン画面の下の方の「Create account」をクリックします。

amplify_06.png

すると、サインアップ画面に遷移します。
遷移する、といっても <amplify-authenticator-ionic> コンポーネントがサインアップフォームのUIに変わるだけでルーティングは変わりません。

サインアップフォームで情報を入力します。
デフォルトの設定では、以下の項目を入力する必要があります。

  • ユーザー名
  • メールアドレス
  • パスワード(英数字 + 記号 / 8文字以上)
  • 電話番号( 090 の先頭のゼロは取る)

amplify_07.png

これで「Create Account」をクリックするとCognitoユーザープールにアカウントが作成されます。
メールアドレスの開通確認もデフォルトで用意されているので、入力したメールアドレスの受信トレイを確認します。

シンプルなメールが届いていました。

amplify_08.png

サインアップすると <amplify-authenticator-ionic> コンポーネントは自動的に以下のようなメールアドレス開通確認コード入力画面に切り替わるので、メールで届いた6桁の確認コードを入力します。

amplify_09.png

「Confirm Code」をクリックするとサインアップが完了します。サインアップ後は改めてログインする必要があります。

amplify_10.png

登録したアカウント情報でサインインすると、<amplify-authenticator-ionic> コンポーネントは以下のようなUIに切り替わりました。

amplify_11.png

(ちなみにちょいちょいconsoleに出ているエラーはアカウント情報を何度か入力し間違えたためです💦)

これでIonicアプリにAWS Amplifyを使って認証システムを組み込むことができました。
認証周りは実は結構複雑で、以下のような画面をユーザーの状態に応じて出し分ける必要があります。

  • サインアップ
  • メールアドレスの開通確認
  • サインイン
  • パスワードリマインダー
  • パスワード再設定

AWS Amplify JavaScript Frameworkを使うことでこの辺りをコンポーネント一発で構築できるのは非常に強力です。

Amplifyの認証状態をIonicアプリに同期する

さて、準備の段階で頑張って PublicModuleClosedModule を作って、未認証状態と認証状態の画面群を分けました。

サインインは <amplify-authenticator-ionic> コンポーネントで行えばよいですが、Amplifyが内部的に持っているログイン状態に応じてルーティングを変えたいです。

AppComponentにAmplifyの認証状態を監視するコードを追加する

Ionicアプリのルートコンポーネントとして、 AppComponent というコンポーネントがあるので、そこでAmplifyの認証状態を監視してルーティングに反映させようと思います。

Ionic CLIでプロジェクトを作ると、 AppComponent にはネイティブアプリ向けのスプラッシュスクリーンやステータスバーの操作処理が書かれていますが、今回は必要ないのでガンガン削除します。

完成形のみ書きます。

src/app/app.component.ts(一通り実装済み)
import { Component, OnInit } from '@angular/core';
import { NavController } from '@ionic/angular';
import { AmplifyService } from 'aws-amplify-angular';

@Component({
  selector: 'app-root',
  templateUrl: 'app.component.html',
  styleUrls: ['app.component.scss']
})
export class AppComponent implements OnInit {

  constructor(
    private navCtrl: NavController,
    private amplifyService: AmplifyService,
  ) {}

  ngOnInit() {
    this.amplifyService.authStateChange$.subscribe(authState => {
      switch (authState.state) {
        case 'signedIn':
          this.navCtrl.navigateForward(['/home']);
          break;
        case 'signedOut':
          this.navCtrl.navigateForward(['/auth']);
          break;
      }
    });
  }
}

Observablesubscribe するときは、必要なくなったときに必ず unsubscribe しないとメモリリークにつながりますが、こと AppComponent に関してはアプリケーションが生きている間はずっと存在している間はずっと存在するので、特に unsubscribe する処理は書いていません。

AmplifyをAngularから使うためには、aws-amplify パッケージから個別に AuthAPI をインポートしてもよいのですが、AngularにはDI(Dependency Injection)という強力な仕組みがあります。

aws-amplify-angular パッケージでは、そのDI経由でAmplifyを操作できるように AmplifyService というサービスクラスを提供してくれています。

また、AngularはRxJSと密結合です。AmplifyService では、Amplifyが内部的に持っているログイン状態の変化をRxJSの Observable として提供してくれています。(this.amplifyService.authState() の部分)

この状態でサインインすると、自動的に HomePage に遷移します。

とりあえずHomePageにサインアウトボタンを設置する

サインアウトも試したいので、 HomePage にサインアウトボタンを設置します。Ionicを使っているので、ヘッダーにアイコンを使ったボタンを設置するのは一瞬でできてしまいます。

src/app/pages/closed/home/home.page.html
<ion-header>
  <ion-toolbar>
    <ion-title>
      Ionic Blank
    </ion-title>

    <!-- ここから追加 -->
    <ion-buttons slot="end">
      <ion-button slot="icon-only" (click)="onSignOutButtonClicked()">
        <ion-icon name="log-out"></ion-icon>
      </ion-button>
    </ion-buttons>
    <!-- ここまで追加 -->

  </ion-toolbar>
</ion-header>

<ion-content>
  <div class="ion-padding">
    The world is your oyster.
    <p>If you get lost, the <a target="_blank" rel="noopener" href="https://ionicframework.com/docs/">docs</a> will be your guide.</p>
  </div>
</ion-content>

続いて src/app/pages/closed/home/home.page.ts にサインアウト処理を書いていきます。こちらも AmplifyService をDIして、用意されているメソッドを使えば簡単にできます。

src/app/pages/closed/home/home.page.ts
import { Component } from '@angular/core';
import { AmplifyService } from 'aws-amplify-angular';

@Component({
  selector: 'app-home',
  templateUrl: 'home.page.html',
  styleUrls: ['home.page.scss'],
})
export class HomePage {

  constructor(
    private amplifyService: AmplifyService,
  ) {}

  onSignOutButtonClicked() {
    this.amplifyService.auth().signOut();
  }
}

こんな感じで動作します。いい感じですね!

signin-sign-out.mov.gif

ここまででIonicアプリにAmplify Frameworkを組み込むことができました。

続きは続編にて:AWS AppSyncのセットアップとリクエスト

ここまでの作業をまとめると、

  • Ionic CLIを使ってIonicプロジェクトを作成
  • Amplify CLIを使ってバックエンドにAmazon Cognitoを構築
  • Amplify Frameworkをセットアップ
  • Amplify提供の <amplify-authenticator-ionic> コンポーネントを使って認証周りをサクッと構築
  • Amplifyが持つ認証状態をAngular側で監視してルーティングを切り替える

になります。

一番書きたかったのはAWS AppSyncでGraphQL APIを作成してAWS Amplify JavaScript Frameworkから叩く部分でしたが、予想以上に準備の手順が多かったのでこのあたりで一旦切りたいと思います。

今回がQiitaのアドベントカレンダー初挑戦なのでこんな感じで良いのか若干不安ですが、勢いで2日間書くことにしているので、続編は12/17の記事でお送りできたらと思います。

ざっくりですが、この後はAWS AppSyncを使ってAPIを立てて、リアルタイムで同期されるチャットアプリを作ろうと思っています。
チャットアプリはAppSyncの練習としてよく題材にされているイメージです。
awslabsにもChatQLというAngular+AppSyncのサンプルがあったり。

この後の実装はチャットアプリに寄せたものになっていく予定ですが、本記事の内容は割と汎用性のあるものに仕上げたつもりです。

AWS Amplifyでバックエンドを爆速開発できるのに加えて、Ionicでフロントエンドも爆速開発できたらより夢が広がりませんか!?

AWS Amplify Advent Calendar、明日は @moaible4qiita さんです!

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away