vConsole を使ってLIFF(LINE Front-end Framework) の開発を加速する

# どんな記事か

LIFF（LINE Front-end Framework）アプリ上でも簡単にログなどのデバッグ情報を表示できる素晴らしいツール「vConsole」を紹介します。

# 概要
LINE Messaging API には、Bot とWeb アプリを連携するための機能として[LIFF](https://developers.line.biz/ja/docs/liff/) があります。
LIFF は、LINE Messaging API で提供されているJavaScript のLIFF ライブラリを読み込んで、Bot 上のブラウザ（Web VIEW) からWebアプリを呼び出すことにより、LINE ユーザーの表示名取得や、Bot へメッセージを送信できるなど、Bot の表現力とBot との連携がスムーズに出来る便利な機能です。

また、LINE より新しくリリースされた「[LINE Things](https://developers.line.biz/ja/docs/line-things/)」は、このLIFF からBLE(Bluetooth Low Energy) デバイスを制御や、デバイスに接続されたセンサー値を取得が可能となるLINE Bot の使い方を広げることを目指したIoTプラットフォームです。

ただ、LIFF の開発面ではLINE アプリ内のWeb VIEWで実行されることもあり、Google Chrome など通常のWebブラウザでの実行と違って、ブラウザのデベロッパーツールが利用できません。
そのため、画面上にデバッグ用情報を表示するなどして開発されるケースが多いのではないでしょうか。私もその一人です。

それが、今回紹介する「vConsole」を使うことで、画面にわざわざデバッグ用情報を表示せずとも簡単にデバッグできるようになります。

なお、本記事で使うソースコードは[こちら](https://github.com/sumihiro3/liff-vconsole)に格納しています。

# 利用する環境

* [LIFF](https://developers.line.biz/ja/docs/liff/overview/)
    * LINE Front-end Framework
* [vConsole](https://github.com/Tencent/vConsole)
    * Tencent 社が提供しているライブラリです

# LIFF の開発方法
LIFF の使い方や開発方法は、LINE の[公式ドキュメント](https://developers.line.biz/ja/docs/liff/developing-liff-apps/)が分かりやすいので、そちらを参考にしてください。

# vConsole のインストール
インストール手順は[vConsole のREADME](https://github.com/Tencent/vConsole/blob/dev/README.md) にあるとおり、[リリースバージョン](https://github.com/Tencent/vConsole/releases/latest) （記事執筆時点：[v3.3.0]( https://github.com/Tencent/vConsole/releases/tag/v3.3.0) ）をダウンロードしてHTMLで読み込むか、npm を使ってインストールできます。
ここでは、HTML 上でvConsole のライブラリを読み込みます。

# vConsole の導入
適当なディレクトリにHTMLファイルを配置し、その配下のlib ディレクトリ内に、ダウンロードしたvConsole のJS（/dist/vconsole.min.js）ファイルを配置してください。
HTMLファイルでvConsole のライブラリを読み込んで、初期化するだけで準備OKです！

```html
<!DOCTYPE html>
<html lang="ja">
<head>
	<title>My LIFF app</title>
	<meta charset="utf-8">
	<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no, viewport-fit=cover">
	<!-- vConsole -->
	<script src="./lib/vconsole.min.js"></script>
	<!-- LIFF -->
	<script src="https://d.line-scdn.net/liff/1.0/sdk.js"></script>
</head>
<body>
</body>
<script>
window.onload = function(){
	initVConsole();

}
// Initialize vConsole
function initVConsole() {
	window.vConsole = new window.VConsole({
		defaultPlugins: ['system', 'network', 'element', 'storage'],
		maxLogNumber: 1000,
		onReady: function() {
			console.log('vConsole is ready.');
		},
		onClearLog: function() {
			console.log('on clearLog');
		}
	});
}
</script>
</html>
````

# vConsole を使ってみる
先ほどのHTMLファイルをブラウザで開いてみましょう。（ローカルファイルとして開くだけでOKです）
画面の右下に「vConsole」というボタンがあるので、早速押してみましょう。
![vConsole](https://sumihiro3.github.io/liff-vconsole/post_images/01_first_demo.png)

ボタンを押すとコンソール画面が開き、そこに「vConsole is ready.」という文字列が表示されて入れば成功です！
![vConsole](https://sumihiro3.github.io/liff-vconsole/post_images/02_first_demo.png)

# LIFF アプリにvConsole を組み込む
今度はLIFF アプリにvConsole を組み込んでみましょう。
LIFF の初期化に成功したかどうかをログに出力します。

```html
<!DOCTYPE html>
<html lang="ja">
<head>
	<title>My LIFF app</title>
	<meta charset="utf-8">
	<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no, viewport-fit=cover">
	<!-- vConsole -->
	<script src="./lib/vconsole.min.js"></script>
	<!-- LIFF -->
	<script src="https://d.line-scdn.net/liff/1.0/sdk.js"></script>
</head>
<body>
</body>
<script>
window.onload = function(){
	initVConsole();
	initLiff();

}
// Initialize vConsole
function initVConsole() {
	window.vConsole = new window.VConsole({
		defaultPlugins: ['system', 'network', 'element', 'storage'],
		maxLogNumber: 1000,
		onReady: function() {
			console.log('vConsole is ready.');
		},
		onClearLog: function() {
			console.log('on clearLog');
		}
	});
}

// Initialize LIFF
function initLiff() {
	console.log('going to initialize LIFF');
	liff.init(
		data => {
			console.log('LIFF initialized!');
			// Now you can call LIFF API
			const userId = data.context.userId;
			liff.getProfile().then(profile => {
				const userDisplayName = profile.displayName
				console.info('User name is', userDisplayName);
			})
			.catch((err) => {
				console.error('LIFF getProfile failed', err);
			});
		},
		err => {
			console.error('LIFF initialization failed', err);
		}
	);
}
</script>
</html>
```

initLiff という関数では、通常のコンソールにログを出すのと同じく

```javascript
console.log('going to initialize LIFF');
```
としているだけですが、これでvConsole のコンソール上にログが出力されます。

このままブラウザでローカルファイルとして開くと、vConsole のコンソール上にLIFF の初期化エラーなどのログが表示されます。
![vConsole](https://sumihiro3.github.io/liff-vconsole/post_images/03_second_demo.png)
これはLIFF アプリとして開いているわけではないので、エラーとなって当然です。


# LIFF アプリの登録
HTTPS 接続で公開できるHTTPサーバーや、AWS S3、GitHub Pages などにHTMLファイルとvConsole のライブラリを配置してください。
配置したらそのURLを使って、LIFF アプリを登録できます。
LIFF アプリの登録方法は、[公式ドキュメント](https://developers.line.biz/ja/docs/liff/registering-liff-apps/)に詳しく書かれていますので、そちらの手順に沿ってください。

LIFF アプリを登録したら、発行されるLIFF URL （"line://" で始まるURL）を開発中のBot に貼り付ける（テキストメッセージとして送信する）などして、LINE アプリからLIFF URL を開いてください。

LIFF の初期化に成功していれば、vConsole コンソール（LIFF アプリの右下にあるvConsole ボタンで開く）に、あなたがLINE で設定している名前が表示されます。
![LIFF](https://sumihiro3.github.io/liff-vconsole/post_images/04_liff.png)

初期化に失敗していれば、先ほどと同じようにエラーログが出六されていますので、そちらを基にデバッグしてくださいｗ

# もう少しLIFF っぽく
折角ですので、LIFF アプリっぽく、Bot へテキストメッセージを送信するようにしてみましょう。
先ほどのHTML ファイルを以下のように編集してください。

```html
<!DOCTYPE html>
<html lang="ja">
<head>
	<title>My LIFF app</title>
	<meta charset="utf-8">
	<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no, viewport-fit=cover">
	<!-- vConsole -->
	<script src="./lib/vconsole.min.js"></script>
	<!-- LIFF -->
	<script src="https://d.line-scdn.net/liff/1.0/sdk.js"></script>
	<!-- Bootstrap -->
	<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
</head>
<body>
<div class="container">
	<div class="row">
		<div class="col-sm-6" style="padding: 6px;">
			<div class="card">
				<div class="card-header">Print log</div>
				<div class="card-body">
					<p class="card-text">ログを出力します。</p>
					<a href="#" class="btn btn-secondary" onclick="console.log('Print log');">Print log</a>
				</div>
			</div>
		</div>
		<div class="col-sm-6" style="padding: 6px;">
			<div class="card">
				<div class="card-header">Print info log</div>
				<div class="card-body">
					<p class="card-text">Info ログを出力します。</p>
					<a href="#" class="btn btn-info" onclick="console.info('Print info log');">Print info log</a>
				</div>
			</div>
		</div>
	</div>
	<div class="row">
		<div class="col-sm-6" style="padding: 6px;">
			<div class="card-header">Print warn log</div>
			<div class="card">
				<div class="card-body">
					<p class="card-text">Warn ログを出力します。</p>
					<a href="#" class="btn btn-warning" onclick="console.warn('Print warn log');">Print warn log</a>
				</div>
			</div>
		</div>
		<div class="col-sm-6" style="padding: 6px;">
			<div class="card">
				<div class="card-header">Print error log</div>
				<div class="card-body">
					<p class="card-text">Error ログを出力します。</p>
					<a href="#" class="btn btn-danger" onclick="console.error('Print error log');">Print error log</a>
				</div>
			</div>
		</div>
	</div>
	<div class="row">
		<div class="col-sm-3"></div>
		<div class="col-sm-6" style="padding: 6px;">
			<div class="card">
				<div class="card-header">Send TextMessage</div>
				<div class="card-body">
					<p class="card-text">Bot にテキストメッセージを送信します。</p>
					<a href="#" class="btn btn-primary" onclick="sendTextMessage();">Send message</a>
				</div>
			</div>
		</div>
		<div class="col-sm-3"></div>
	</div>
</div>
</body>
<script>
window.onload = function(){
	initVConsole();
	initLiff();
}
// Initialize vConsole
function initVConsole() {
	window.vConsole = new window.VConsole({
		defaultPlugins: ['system', 'network', 'element', 'storage'],
		maxLogNumber: 1000,
		onReady: function() {
			console.log('vConsole is ready.');
		},
		onClearLog: function() {
			console.log('on clearLog');
		}
	});
}

// Initialize LIFF
function initLiff() {
	console.log('going to initialize LIFF');
	liff.init(
		data => {
			console.log('LIFF initialized!');
			// Now you can call LIFF API
			const userId = data.context.userId;
			liff.getProfile().then(profile => {
				const userDisplayName = profile.displayName
				console.info('User name is', userDisplayName);
			})
			.catch((err) => {
				console.error('LIFF getProfile failed', err);
			});
		},
		err => {
			console.error('LIFF initialization failed', err);
		}
	);
}

function sendTextMessage() {
	liff.getProfile().then(profile => {
		const userDisplayName = profile.displayName
		liff.sendMessages([
			{
				type: 'text',
				text: '私の名前は' + userDisplayName + 'です！'
			}
		])
		.then(() => {
			console.log('message sent');
		})
		.catch((err) => {
			console.error('error', err);
		});	})
	.catch((err) => {
		console.error('LIFF getProfile failed', err);
	});
}
</script>
</html>
```

サーバーに再度配置して、LINE からLIFF アプリを開きなおすと、ボタンがいくつか表示されます。
各種ログ（log, info, warn, error）を出力するボタンが上から順にあり、一番下にテキストメッセージを送信するボタンがあります。
![LIFF](https://sumihiro3.github.io/liff-vconsole/post_images/05_liff_log01.PNG)

![LIFF](https://sumihiro3.github.io/liff-vconsole/post_images/06_liff_log02.PNG)

一番下のボタンを押すと、Bot にあなたがLINE で設定している名前が入ったテキストメッセージが送信されます。

![LIFF](https://sumihiro3.github.io/liff-vconsole/post_images/08_liff_textmessage.PNG)

ログもしっかり出力されていますね！
![LIFF](https://sumihiro3.github.io/liff-vconsole/post_images/07_liff_log03.PNG)

とても簡単ですね！
開発が終わったら、vConsole の初期化処理などを消してあげれば、他の部分のコードに手を入れることもないのでリリース対応も簡単にできますね！

もちろん、vConsole はLIFF 開発専用ではなく、他のWeb アプリでも使うことができます。
ブラウザのデベロッパーツールが利用できない状況などで、とても使えるツールだと思います。

# 最後に

先日（3/2-3/3）に行われた[LINE Things ハッカソン@大阪](https://code4osaka.connpass.com/event/116814/)に参加して、貸借りしたい人・企業をマッチングするレンタルボックス型サービス「ShareThings」のプロトタイプを開発してLINE賞とTwilio賞をいただきました！！
<blockquote class="twitter-tweet" data-partner="tweetdeck"><p lang="ja" dir="ltr"><a href="https://twitter.com/hashtag/linethings?src=hash&amp;ref_src=twsrc%5Etfw">#linethings</a> ハッカソンで、貸借りしたい人・企業をマッチングするShareThings というレンタルボックス型サービスを開発して <a href="https://twitter.com/hashtag/LINE?src=hash&amp;ref_src=twsrc%5Etfw">#LINE</a> 賞・ <a href="https://twitter.com/hashtag/Twilio?src=hash&amp;ref_src=twsrc%5Etfw">#Twilio</a> 賞を頂きました！<br>過去最高に余裕が無かったですが、とても楽しかったです。<br>色々勉強になりました。ありがとうございました。 <a href="https://t.co/QbRXXLosvf">pic.twitter.com/QbRXXLosvf</a></p>&mdash; sumihiro3 (@sumihiro3) <a href="https://twitter.com/sumihiro3/status/1102181629995307008?ref_src=twsrc%5Etfw">March 3, 2019</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

デモ動画はこちら
<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr"><a href="https://twitter.com/hashtag/linethings?src=hash&amp;ref_src=twsrc%5Etfw">#linethings</a> ハッカソン、LINE 賞は借り物 BOX を LINE Things で連携した &quot;かりっぱ&quot; でした。2日で LINE Pay 連携もできてて完成度高かった！おめでとうございます。 <a href="https://t.co/CMOeBazyf6">pic.twitter.com/CMOeBazyf6</a></p>&mdash; Hirotaka Kawata (@hktechno) <a href="https://twitter.com/hktechno/status/1102141640905416704?ref_src=twsrc%5Etfw">2019年3月3日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

ハッカソンのレポートは[こちら](https://engineering.linecorp.com/ja/blog/line-things-hackathon-report-osaka/)


今回紹介したvConsole はハッカソン後に知ったので、その時に知っていればもっと開発が楽になっていたと思います。
今後もLINE Things などでLIFF アプリを開発する際、vConsole は手放せないツールとなりそうです。