LIFFプラグインがリリースされたので使ってみた

# はじめに

昨年のLINE DEVELOPER DAY 2021での[発表](https://linedevday.linecorp.com/2021/ja/sessions/142/)で予告されていたLIFFのプラグイン機能がLIFF v2.19.1でリリースされました。LIFFプラグインを使うとLIFF SDKに独自のAPIを追加したり、LIFF APIの挙動を変更したりできます。

簡単な使い方がLINE Developersの[LIFFプラグイン](https://developers.line.biz/ja/docs/liff/liff-plugin/)ドキュメントで公開されているので早速使ってみます。

# LIFFスターターアプリをセットアップする

とりあえずLIFFプラグイン機能を使うためのサンプルコードとして[LIFF Starter v2](https://github.com/line/line-liff-v2-starter)をベースにしようと思います。[LIFFスターターアプリのドキュメント](https://developers.line.biz/ja/docs/liff/trying-liff-app/#how-to-start-liff-starter-app)を参考に、以下のように適当なディレクトリにLIFF Starterをcloneします。

```shell
git clone https://github.com/line/line-liff-v2-starter.git
cd line-liff-v2-starter/src/vanilla/
```

現時点でLIFF Starterが使っているLIFFのバージョンは`2.16.0`だったのでLIFFプラグインが実装されている`2.19.1`に更新します。

```package.json
  "dependencies": {
    "@line/liff": "2.19.1"
  },
```

アプリを起動したときにLIFFのバージョンを確認するため`index.js`に`getVersion()`を仕込んでおきます

```index.js
document.addEventListener("DOMContentLoaded", function() {
  console.info("LIFF Version: ", liff.getVersion());
  liff
    .init({ liffId: process.env.LIFF_ID })
    .then(() => {
        console.log("Success! you can do something with LIFF API here.")
    })
    .catch((error) => {
        console.log(error)
    })
});
```

アプリを起動します

```
yarn
yarn dev
```

`localhost:3000`にアクセスすると以下のように表示されます

![スクリーンショット 2022-04-23 13.33.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61995/741cde0e-004a-2c48-d2a0-d4205f7dbef0.png)

Inspector等を開いてコンソールを確認すると以下のように表示されています。

```
LIFF Version:  2.19.1
index.js:19 Error: liffId is necessary for liff.init()
    at new e (index.es.js:34:1779)
    at m (index.es.js:34:1910)
    at eval (index.es.js:23:19789)
    at step (tslib.es6.js:129:23)
    at Object.eval [as next] (tslib.es6.js:110:53)
    at eval (tslib.es6.js:103:71)
    at new Promise (<anonymous>)
    at __awaiter (tslib.es6.js:99:12)
    at He (index.es.js:23:19573)
    at eval (index.es.js:23:22942)
index.js:519 [webpack-dev-server] Hot Module Replacement enabled.
index.js:519 [webpack-dev-server] Live Reloading enabled.
```

LIFFのバージョンが`2.19.1`になっていますね。その下にエラーが表示されていますが、これは`liff.init()`でLIFF IDを設定してないためです。LIFFプラグイン機能はLIFF IDを設定していなくてもとりあえず動かすことができるのでこのまま行きます。

# LIFFプラグインを使ってみる

[LIFFプラグインを使用する](https://developers.line.biz/ja/docs/liff/liff-plugin/#use-liff-plugin)を参考に`index.js`を以下のようにします

```index.js
import './index.css';
import liff from '@line/liff';

document.addEventListener("DOMContentLoaded", function() {
  console.log("LIFF Version: ", liff.getVersion());

  class GreetPlugin {
    constructor() {
      this.name = "greet";
    }

    install() {
      return {
        hello: this.hello,
      };
    }

    hello() {
      console.log("Hello, LIFF Plugin!");
    }
  }

  liff.use(new GreetPlugin());
  liff.$greet.hello();

  liff
    .init({ liffId: process.env.LIFF_ID })
    .then(() => {
        console.log("Success! you can do something with LIFF API here.")
    })
    .catch((error) => {
      // console.log(error)
    })
});
```

プラグインである`GreetPlugin`クラスを作成して`liff.use()`に渡し、`liff.$greet.hello()`を実行します。正常に実行されるとLIFF SDKが`GreetPlugin::hello()`を呼び出してコンソールに`Hello, LIFF Plugin!`と表示されるはずですね。LIFF IDが設定されていないために出るエラーを非表示にするため`liff.catch（）`内をコメントアウトしておきます。

上記を実行する前にLIFF Starterのbabel環境を修正します。というのがLIFF Starterの設定が古いため上記をそのまま実行するとトランスパイルエラーが発生したのでその対策が必要だからです。

`@babel/plugin-transform-classes`をインストールします

```shell
yarn add -D @babel/plugin-transform-classes
```

`.babelrc`を以下のように更新します。

```.babelrc
{
  "presets": ["env"],
  "plugins": ["@babel/plugin-transform-classes"]
}
```

アプリを実行します。

```
yarn dev
```

コンソールに以下のように実行されれば成功です。

```shell
LIFF Version:  2.19.1
index.js:37 Hello, LIFF Plugin!
index.js:519 [webpack-dev-server] Hot Module Replacement enabled.
index.js:519 [webpack-dev-server] Live Reloading enabled.
```

# フックを使ってみる

フックはLIFFアプリ処理中の特定のタイミングで事前に登録されたコールバックを実行する仕組みです。これによってLIFFプラグインがLIFFアプリ内のライフサイクルに介入することができます。詳しくは[フックについて](https://developers.line.biz/ja/docs/liff/liff-plugin/#hook)に詳説されています。

下記のサンプルでは`before hook`を使用してLIFFアプリ初期化前に`GreetPlugin::initBefore`を呼び出し1秒後にコンソールにメッセージを表示する処理を行っています。

```index.js
import './index.css';
import liff from '@line/liff';

document.addEventListener("DOMContentLoaded", function() {
  console.log("LIFF Version: ", liff.getVersion());

  class GreetPlugin {
    constructor() {
      this.name = "greet";
    }

    install(context) {
      context.hooks.init.before(this.initBefore);
      return {
        hello: this.hello,
      };
    }

    hello() {
      console.log("Hello, LIFF Plugin!");
    }

    initBefore() {
      return new Promise((resolve) => {
        setTimeout(() => {
          console.info("Called the hook from GreetPlugin after waiting 1 sec.");
          return Promise.resolve();
        }, 1000);
      });
    }
  }

  liff.use(new GreetPlugin());
  liff.$greet.hello();

  liff
    .init({ liffId: process.env.LIFF_ID })
    .then(() => {
        console.log("Success! you can do something with LIFF API here.")
    })
    .catch((error) => {
      // console.log(error)
    })
});
```

実行するとコンソールに以下のように表示されます。

```shell
LIFF Version:  2.19.1
index.js:38 Hello, LIFF Plugin!
index.js:519 [webpack-dev-server] Hot Module Replacement enabled.
index.js:519 [webpack-dev-server] Live Reloading enabled.
index.js:45 Called the hook from GreetPlugin after waiting 1 sec.
```

![スクリーンショット 2022-04-23 13.18.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61995/af32862e-8c49-c3cc-569a-5be492ebd5fb.png)

現時点で提供されている`before hook`、`after hook`は`liff.init()`に依存するhookであり両方とも[非同期フック](https://developers.line.biz/ja/docs/liff/liff-plugin/#async-hook)であるためコールバックは`Promise`を返す必要があります。

# おわりに

LIFF StarterアプリをベースにLIFF SDKに新たに実装されたLIFFプラグインを使ってみました。LIFF SDKに様々な機能をインストールできるようになるので、LIFF自体のアップデートやサードパーティからのプラグイン公開が楽しみになりますね。

ニュースではプラグインリリースに伴って[LIFF Inspector](https://developers.line.biz/ja/news/2022/04/18/release-liff-2-19-1/#liff-inspector-20220418)と[LIFF Mock](https://developers.line.biz/ja/news/2022/04/18/release-liff-2-19-1/#liff-mock-20220418)が近日公開される予定とのことです。どちらもLIFFアプリの~~クソめんどくさい開発やデバッグとかローカルでテストできないとか~~開発効率を向上させてくれそうなものなので公開が楽しみです。