ASP.NET Core Blazor WebAssembly で プッシュ通知を実装しよう

前回、ASP.NET Core Blazor (以下 Blazor) の WebAssembly で PWA プロジェクトを作成する方法についてご紹介しました。本記事では PWA の機能のひとつであるブラウザのプッシュ通知の実装について、解説していきます。

<a href="https://blogs.jp.infragistics.com/entry/2020/12/25/103353" target="_blank">前回の記事はこちら</a>

(※本記事では Microsoft Visual Studio 2019 を使用します。また、確認用のブラウザとして Google Chrome を使用しています。)

## プッシュ通知とは

プッシュ通知は特にモバイルアプリでよく利用されている、サービス側からユーザー側に対して通知を行うことのできる機能です。ユーザ―の端末画面上に目立つ形で通知が表示されるため、有効に使えば強力なマーケティングツールになるかと思います。
それではさっそく実装を行っていきましょう。

## プッシュ通知の実装

### 新規プロジェクト作成

<a href="https://blogs.jp.infragistics.com/entry/2020/12/25/103353" target="_blank">前回の記事</a> を参考に PWA プロジェクトを作成します。

### 新規 JS ファイルの作成

本記事ではプッシュ通知に関する処理は全て JavaScript で行っていきます。新規に `app.js` というファイルを `wwwroot` ディレクトリの中に作成し、そのファイルを読み込むために `wwwroot/index.html` に追記します。

[![Image from Gyazo](https://i.gyazo.com/4021e87b8b81fe89e45133a698ef79c1.png)](https://gyazo.com/4021e87b8b81fe89e45133a698ef79c1)

```html
<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
    <title>BlazorAppPush</title>
    <base href="/" />
    <link href="css/bootstrap/bootstrap.min.css" rel="stylesheet" />
    <link href="css/app.css" rel="stylesheet" />
    <link href="BlazorAppPush.styles.css" rel="stylesheet" />
    <link href="manifest.json" rel="manifest" />
    <link rel="apple-touch-icon" sizes="512x512" href="icon-512.png" />
</head>

<body>
    <div id="app">Loading...</div>

    <div id="blazor-error-ui">
        An unhandled error has occurred.
        <a href="" class="reload">Reload</a>
        <a class="dismiss">🗙</a>
    </div>
    <script src="_framework/blazor.webassembly.js"></script>
    <script src="app.js"></script>
    <script>navigator.serviceWorker.register('service-worker.js');</script>

</body>

</html>
```

Blazor 側ではこの `app.js` の中の関数を呼び出すことが出来ます。例えば以下のような JavaScript の関数を、

```javascript
(function () {
    window.TestFunc = {
        log () {
            console.log("test");
        },
        sum (x, y) {
            return x + y;
        }
    };
)}
```

IJSRuntime を使用して以下のように利用可能です。

```cs
@inject IJSRuntime JSRuntime

@code {
    protected override async Task OnInitializedAsync()
    {
        var sum = await JSRuntime.InvokeAsync<number>("TestFunc.sum", 1, 2); //戻り値がある場合
        await JSRuntime.InvokeVoidAsync("TestFunc.log"); //戻り値がない場合
    }
}
```

Blazor で .NET メソッドから JavaScript 関数を呼び出す方法の詳細に関しては、<a href="https://docs.microsoft.com/ja-jp/aspnet/core/blazor/call-javascript-from-dotnet?view=aspnetcore-5.0" target="_blank">公式ドキュメント</a> をご参照ください。

### プッシュ通知認証の処理

プッシュ通知を特定のブラウザに対して送信できるようにするためには、ユーザー側からプッシュ通知を受け取る事の許可を得る必要があります。
Web API の <a href="https://developer.mozilla.org/ja/docs/Web/API/Notification/requestPermission" target="_blank">Notification.requestPermission()</a> で通知許可の要求を行うことが出来ます。
ユーザーの許可を得たら、<a href="https://developer.mozilla.org/ja/docs/Web/API/PushManager/subscribe" target="_blank">PushManager.subscribe()</a> を使用し、service worker がプッシュ通知を購読するように指定します。JS 側のコードは以下のようになります。

```javascript
(function () {

    const applicationServerPublicKey = 'BJJpNlDugifqq-PmyA1v_Z3Bowiv2r5dO1zCqqBwWeHCqfBvXIFNjXgifxIOH3KkqXxAv5giaP11woKJStQrJys';

    window.PushNotifications = {
        isSupported: async () => { //ブラウザがプッシュ通知をサポートしているか判定
            if ("Notification" in window)
                return true;
            return false;
        },
        askPermission: async () => {　//ユーザーがプッシュ通知を許可したか判定
            return new Promise((resolve, reject) => {
                Notification.requestPermission((permission) => {
                    resolve(permission);
                });
            });
        },
        requestSubscription: async () => {　//service worker がプッシュ通知を購読するように指定
            const worker = await navigator.serviceWorker.getRegistration();
            const existingSubscription = await worker.pushManager.getSubscription();
            if (!existingSubscription) {
                const newSubscription = await subscribe(worker);
            }
        }
    };

    async function subscribe(worker) {
        try {
            return await worker.pushManager.subscribe({
                userVisibleOnly: true,
                applicationServerKey: applicationServerPublicKey
            });
        } catch (error) {
            if (error.name === 'NotAllowedError') {
                return null;
            }
            throw error;
        }
    }

})();

```

これらの関数を `index.razor` の OnInitializedAsync() で呼び出すことで、トップページ表示時に通知の許可を求めるダイアログを表示し、ユーザーが許可をした場合、購読処理をするという実装が出来ます。

```cs
@code {
    private bool isSupportedByBrowser;
    private string permission;
    protected override async Task OnInitializedAsync()
    {
        isSupportedByBrowser = await JSRuntime.InvokeAsync<bool>("PushNotifications.isSupported");
        permission = await JSRuntime.InvokeAsync<string>("PushNotifications.askPermission");
        await JSRuntime.InvokeVoidAsync("PushNotifications.requestSubscription");
    }
}
```

注意すべき点として、PushManager.subscribe() のオプションパラメーターとして、applicationServerKey というものを渡しています。
ブラウザによって多少仕様が異なりますが、Chrome の場合はこちらは必須項目となります。applicationServerKey の生成は、<a href="https://github.com/web-push-libs/web-push#command-line" target="_blank">ライブラリ</a> をインストールして、コマンドラインから行うこともできますが、今回は以下のページにアクセスしてオンラインで生成されたものを使用しました。

<a href="https://web-push-codelab.glitch.me/" target="_blank">Push Companion</a>

### プッシュ通知送信の処理

最後にプッシュ通知を実際に送信する処理を実装していきます。`app.js` に以下のように `create()` 関数を追加します。

```javascript
 window.PushNotifications = {
    create() {
        var text = 'これはプッシュ通知テストです。';
        var notification = new Notification('プッシュ通知テスト', { body: text });
    }
};
```

`index.razor` 側では送信ボタンをクリックすることで `create()` 関数を呼び出すように設定しました。この時、ブラウザがプッシュ通知に対応していて、かつユーザーが許可をした場合にのみ送信ボタン表示されるようにしています。

```cs
@if (isSupportedByBrowser && permission == "granted")
{
    <div class="row">
        <div class="col-auto">
            <button type="button" class="btn btn-primary" @onclick="CreateNotifcationAsync">プッシュ通知を実行</button>
        </div>
    </div>
}

@code {
    private bool isSupportedByBrowser;
    private string permission;
    protected override async Task OnInitializedAsync()
    {
        isSupportedByBrowser = await JSRuntime.InvokeAsync<bool>("PushNotifications.isSupported");
        permission = await JSRuntime.InvokeAsync<string>("PushNotifications.askPermission");
        await JSRuntime.InvokeVoidAsync("PushNotifications.requestSubscription");
    }
    private async Task CreateNotifcationAsync()
    {
        await JSRuntime.InvokeVoidAsync("PushNotifications.create");
    }
}
```

今回は最低限の実装として、Web API の <a href="https://developer.mozilla.org/ja/docs/Web/API/Notification">Notification()</a> を使用してフロントエンドで完結していますが、実際のアプリケーション制作では購読完了の際にブラウザから返される通知先エンドポイントなどの情報を保存しておき、任意のタイミングでバックエンドからプッシュ通知送信の処理をするパターンがほとんどかと思います。Blazor のバックエンドでプッシュ通知を送信する方法に関しては、<a href="https://github.com/dotnet-presentations/blazor-workshop/blob/master/docs/09-progressive-web-app.md#displaying-notifications" target="_blank">マイクロソフトの公式サンプル</a> にて実装済みのソースが確認できますのでご参照ください。

### サンプルアプリの実行

以上でプッシュ通知機能を確認するための最低限のアプリケーションを作成することができました。さっそく実行してみましょう。

[![Image from Gyazo](https://i.gyazo.com/646118571b9c05361f33b424df9abf44.png)](https://gyazo.com/646118571b9c05361f33b424df9abf44)

アプリケーションを実行するとこのように通知許可のダイアログが表示されます。

[![Image from Gyazo](https://i.gyazo.com/d7d69db63763d74f274a616b41d87805.png)](https://gyazo.com/d7d69db63763d74f274a616b41d87805)

通知を許可すると、ブラウザのプッシュ通知サポートとユーザー許可の条件判定をクリアすることによってこのような表示になります。

[![Image from Gyazo](https://i.gyazo.com/ebf54135fee639dda340a5500247bb01.png)](https://gyazo.com/ebf54135fee639dda340a5500247bb01)

プッシュ通知の実行ボタンをクリックすると、このように通知の表示が確認できました。

## まとめ

本記事では Blazor WebAssembly でのプッシュ通知の実装方法に関して、出来るだけ簡潔にご紹介しました。プッシュ通知に興味のある方にとって、本記事がご参考になれば幸いです。
今回作成したサンプルアプリケーションは <a href="https://kb.jp.infragistics.com/wp-content/uploads/2021/01/BlazorAppPush.zip" target="_blank">こちら</a> からダウンロードいただけます。

#### Ignite UI for Blazor トライアル版を利用するには

インフラジスティックスでは充実した UI コンポーネントライブラリーを収録し、データリッチでレスポンシブなWebアプリケーションをより迅速に構築することを可能にする <a href="https://jp.infragistics.com/products/ignite-ui" target="_blank">Ignite UI</a> を開発しており、Blazor 対応の <a href="https://jp.infragistics.com/products/ignite-ui-blazor" target="_blank">Ignite UI for Blazor</a> もリリースしています。<br>
Ignite UI for Blazor はトライアル版での試用が可能です。<br>
トライアル版を利用するためには <a href="https://iam.infragistics.com/Account/Register" target="_blank">こちらのページ</a> よりアカウントの作成を行ってください。登録より30日間、弊社のテクニカルサポートをご利用いただくことが出来ますのでお気軽にお問い合わせください。<br>

また、製品をご購入をご検討のお客様は <a href="https://iam.infragistics.com/Account/Register" target="_blank">こちらのページ</a> よりお気軽にお問い合わせください。