Ionic + Firebaseで記録するカレンダーアプリを作る　その９ オートログイン　ストレージにユーザーデータを保存

#完成イメージ
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">こんな感じ。 <a href="https://t.co/CvOFjFq2th">pic.twitter.com/CvOFjFq2th</a></p>&mdash; げん げんと (@gento34165638) <a href="https://twitter.com/gento34165638/status/1190431795877007362?ref_src=twsrc%5Etfw">November 2, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

#はじめに
環境は以下の通り
・ionic4
・Nativeとの橋渡しには[capacitor](https://capacitor.ionicframework.com)を使用
・カレンダーについては[ionic2-calendar](https://github.com/twinssbc/Ionic2-Calendar#readme)というライブラリーを使用

前回はログインユーザーのデータ取得を実装した。
:point_right:前回の記事は[こちら](https://qiita.com/kokogento/items/f4f7475fabfd7258dd2b)

#概要
実機で確かめるとわかるが、今の所なんと**一度アプリを終了させるともう一度ログインする必要があるのだ！！**

[その5 ユーザー作成と認証](https://qiita.com/kokogento/items/90ae35c2c587c69c58de)から前回の記事で、かなりアプリっぽくはなったが、起動するたびにログインが必要なアプリはあるだろうか？（ファイナンス系はそうだが。。）

毎回ログインさせたく無い時に使うのがネイティブストレージだ。簡単に言えばスマホが持ってる容量にデータを保存させるって事。

やる事は2つ。
**①ログインかサインアップをしたら、ネイティブストレージにユーザーデータを保存。
②そして次回以降、もしネイティブストレージにユーザーデータがあれば自動でログインさせる。**

#プラグインのインストール
公式は[こちら](https://ionicframework.com/docs/native/native-storage)

```terminal
$ ionic cordova plugin add cordova-plugin-nativestorage
$ npm install @ionic-native/native-storage
```

#アカウント作成時にネイティブストレージに保存
まず新規ユーザーは`signup.page`でアカウントを作成する。そこでユーザーデータをストレージに保存する。

```typescript:signup.page.ts
import { Router } from '@angular/router';
import { AuthService } from './../auth/auth.service';
import { Component, OnInit } from '@angular/core';
import { AngularFireAuth } from '@angular/fire/auth';
import { ToastController, AlertController } from '@ionic/angular';
import { NativeStorage } from '@ionic-native/native-storage/ngx'; // 追加

@Component({
  selector: 'app-signup',
  templateUrl: './signup.page.html',
  styleUrls: ['./signup.page.scss']
})
export class SignupPage implements OnInit {
  signup: {
    email: string;
    password: string;
    name: string;
  } = {
      email: '',
      password: '',
      name: ''
    };

  constructor(
    private router: Router,
    private afAuth: AngularFireAuth,
    private toastCtrl: ToastController,
    public authService: AuthService,
    private nativeStorage: NativeStorage, // 追加
    private alertCtrl: AlertController
  ) { }

  ngOnInit() { }

  signUp() {
    this.afAuth.auth
      .createUserWithEmailAndPassword(this.signup.email, this.signup.password)
      .then(created => {
        const newUser = created.user;
        newUser.updateProfile({
          displayName: this.signup.name,
          photoURL: ''
        })
          .then(async () => {
            console.log('signup ' + this.signup.name);
            const toast = await this.toastCtrl.create({
              message: `${created.user.displayName}さんを登録しました`,
              duration: 3000
            });
         
        // 追加
            this.nativeStorage.setItem('email_user', {
              uid: newUser.uid,
              name: newUser.displayName,
              email: newUser.email
            });
            this.authService.updateUserData(created.user);
            this.router.navigateByUrl('/home');
            await toast.present();
          });
      })
      .catch(error => {
        console.log(error);
        const code = error.code;
        let message = 'エラーが発生しました。もう一度お試しください。。。';
        if (code === 'auth/email-already-in-use') {
          message = 'このメールアドレスは既に登録されています。。。';
        }
        this.showAlert(message);
      });
  }

  goLogin() {
    this.router.navigateByUrl('/auth');
  }

  private showAlert(message: string) {
    this.alertCtrl
      .create({
        header: 'ユーザー登録に失敗',
        message: message,
        buttons: ['OK']
      })
      .then(alertEl => alertEl.present());
  }
}
```

ここではemailとパスワードでログインしているので、`email_user`という名前で保存。

次はログインの時。

#ログイン時にネイティブストレージに保存
仮にスマホを新しくした人がいたとする。当然アカウントがあるので`auth.page`でログインする。
なので、ログイン時もネイティブストレージにユーザーデータを保存したいと思う。


```typescript:auth.page.ts
import { AuthService } from './auth.service';
import { Router } from '@angular/router';
import { Component, OnInit } from '@angular/core';
import { ToastController } from '@ionic/angular';
import { AngularFireAuth } from '@angular/fire/auth';
import { LoadingController } from '@ionic/angular';
import { NgForm } from '@angular/forms';
import { NativeStorage } from '@ionic-native/native-storage/ngx'; // 追加


@Component({
  selector: 'app-auth',
  templateUrl: './auth.page.html',
  styleUrls: ['./auth.page.scss']
})
export class AuthPage implements OnInit {
  login: {
    email: string;
    password: string;
  } = {
    email: '',
    password: ''
  };

  constructor(
    private router: Router,
    private authService: AuthService,
    private toastCtrl: ToastController,
    private afAuth: AngularFireAuth,
    private loadingCtrl: LoadingController,
    private nativeStorage: NativeStorage // 追加
  ) {}

  ngOnInit() {}

  async userLogin() {
    const loading = await this.loadingCtrl.create({
      message: 'ログイン中です'
    });
    this.presentLoading(loading);
    this.authService.login(this.login.email, this.login.password).then(() => {
 // 追加
      this.afAuth.auth.onAuthStateChanged(user => {
        if (user != null) {
          this.nativeStorage.setItem('email_user', {
            uid: user.uid,
            name: user.displayName,
            email: user.email
          });
        }
      });
      loading.dismiss();
    });
  }

  gotoSignup() {
    this.router.navigateByUrl('/signup');
  }

  gotoReset() {
    this.router.navigateByUrl('/reset-password');
  }

  async presentLoading(loading) {
    return await loading.present();
  }
}
```

#ログアウト時にストレージからデータを削除
ログアウトしてもストレージにユーザーデータが残っていれば、オートログインされてしまう。
それではログアウトの意味が無いので、ログアウト時にストレージからデータを削除したいと思う。

**また、スマホで起動した場合のオートログインを`initializeApp()`で実装。**

```typescript
import { AngularFireAuth } from '@angular/fire/auth';
import { Router } from '@angular/router';
import { Component } from '@angular/core';
import { AuthService, User } from './auth/auth.service';
import { Platform, ToastController, AlertController } from '@ionic/angular';
import { SplashScreen } from '@ionic-native/splash-screen/ngx';
import { StatusBar } from '@ionic-native/status-bar/ngx';
import { NativeStorage } from '@ionic-native/native-storage/ngx';

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

  constructor(
    private platform: Platform,
    private splashScreen: SplashScreen,
    private statusBar: StatusBar,
    private router: Router,
    private afAuth: AngularFireAuth,
    private authService: AuthService,
    private toastCtrl: ToastController,
    private nativeStorage: NativeStorage,
    private alertCtrl: AlertController
  ) {
    this.initializeApp();

    this.afAuth.auth.onAuthStateChanged(user => {
      if (user != null) {
        this.currentUser = user;
      } else {
        this.router.navigateByUrl('/auth');
      }
    });
  }

  initializeApp() {
    this.platform.ready().then(() => {
      // すでにログインしているユーザーか確認
      this.nativeStorage.getItem('email_user').then(
        data => {
          // もしストレージからデータを取得できたらアクセスさせる
          this.router.navigateByUrl('/home');
          this.splashScreen.hide();
        },
        err => {
          this.router.navigateByUrl('/auth');
          this.splashScreen.hide();
        }
      );
      this.statusBar.styleDefault();
    });
  }

  async logout() {
    const alert = await this.alertCtrl.create({
      header: 'ログアウトしますか？',
      message: '再度ログインするのは面倒ではありませんか？',
      buttons: [
        {
          text: 'キャンセル',
          role: 'cancel',
          handler: blah => {
            console.log('Confirm Cancel: blah');
          }
        },
        {
          text: 'ログアウト',
          cssClass: 'cancel-button',
          handler: async () => {
            this.authService.logout().then(async () => {
          // 追加
              this.nativeStorage.remove('email_user');
              const toast = await this.toastCtrl.create({
                message: 'ログアウトしました',
                duration: 3000
              });
              await toast.present();
            });
            this.router.navigateByUrl('/auth');
          }
        }
      ]
    });
    await alert.present();
  }
}
```



#最後に
今回で一通りは終わり！アプリとして最低限必要なことはできたと思う。。




各ストアで配信されてます。
興味があれば使ってみてください。:relieved:

<a href="https://apps.apple.com/jp/app/%E6%80%A7%E6%B4%BB%E3%82%AB%E3%83%AC%E3%83%B3%E3%83%80%E3%83%BC/id1484151444?mt=8">Apple Store</a>

[Google Play](https://play.google.com/store/apps/details?id=com.hCalendar.app)