RELATIONS Developers Blog

RELATIONS株式会社の開発ブログです。

最新のExpo SDK を使ってジオフェンスとプッシュ通知を組み合わせてみました

こんにちは、RELATIONS株式会社の大川です。
最近はWistant のUI開発とRedashを使ったデータ分析をしています。

これまで、2回に分けてExpoとFirebaseを組み合わせたプッシュ通知配信の検証をしていました。

今回は年明けに案内されたExpo SDK v32 で新たにサポートされた「バックグラウンドでの位置情報取得機能」と「ジオフェンス機能」が気になっていたので、検証のスピンオフとして位置情報をもとにしたプッシュ通知の表示を試していきます。

新たにサポートされたバックグラウンド位置情報取得とジオフェンスについて

バックグラウンドでの位置情報取得機能は、アプリの画面を開いていなくても位置情報が取得できる機能です。
(これまでのExpo SDKではアプリ画面を開いている状態での位置情報取得だけがサポートされていました。)
この機能のおかげで、例えばジョギングアプリで画面をOFFにしたまま自分が走ったコースを記録する、といったことが可能になりました。

また、ジオフェンス(特定の場所に対する仮想的な境界線)を設定し、端末がそのジオフェンスへ出入りしたときにイベントを発生させることができるようになりました。(ジオフェンス機能も、アプリのバックグラウンド状態をサポートしています。)

ジオフェンスのイメージ図
ジオフェンスのイメージ図

ネイティブアプリ開発用には既にジオフェンス機能が提供されており、iOSにデフォルトで付属しているリマインダーアプリのように場所でタスクを通知するために利用されていたり、O2O(Online to Offline)アプリで特定の場所に近い端末にのみ情報を配信する(たとえばスーパーのお得情報など)ことが可能になっていました。

この記事では特定のエリアに近づいた(=設定したジオフェンスに入った)端末にLocal Notification(スマートフォンがサーバーと通信せずに単体で配信されるプッシュ通知)を配信してみます。

ジオフェンス機能を利用したプッシュ通知の表示を検証する

(準備: Expo CLIの更新と新規プロジェクト作成)

古いバージョンのExpo CLIをインストールしたまま、 expo init コマンドで新規プロジェクトを作成すると古いExpo SDKがインストールされてしまいます。
npm install -g expo-cli で Expo CLIを更新してからプロジェクトを作成します。

パーミッションの取得

ルートコンポーネントのcomponentDidMountで Permissions.askAsync を呼び出して、iOS向けにパーミッション取得のための許可画面を表示します。
(スタンドアローンのアプリには別途設定が必要なので、Locationのドキュメントもご覧ください。)

// NOTE: iOSシミュレーター用に例外処理を省いています
async componentDidMount() {
  // (iOS向け)パーミッションを取得
  await Permissions.askAsync(Permissions.NOTIFICATIONS, Permissions.LOCATION);
  // プッシュ通知を開いた時のイベントハンドラーを登録
  Notifications.addListener(this.handleNotification);
}

上のメソッドはiOSシミュレーターでプッシュ通知のパーミッションを取得できないので 例外処理などを省いていますが、自分が実際にアプリに組み込む場合は以下のような実装になると思います。

async componentDidMount() {
  // 既存のパーミッションを取得
  const { permissions } = await Permissions.getAsync(Permissions.NOTIFICATIONS, Permissions.LOCATION);
  const currentNotificationPermission = permissions[Permissions.NOTIFICATIONS];
  const currentLocationPermission = permissions[Permissions.LOCATION];

  if (currentNotificationPermission.status !== 'granted') {
    // (iOS向け) プッシュ通知の許可をユーザーに求める
    const { status } = await Permissions.askAsync(Permissions.NOTIFICATIONS);
    if (status !== 'granted') {
      console.log('notification permission is denied');
      return;
    }
  }

  // プッシュ通知を開いた時のイベントハンドラーを登録
  Notifications.addListener(this.handleNotification);

  if (currentLocationPermission !== 'granted') {
    // (iOS向け) 位置情報利用の許可をユーザーに求める
    await Permissions.askAsync(Permissions.LOCATION);
  }
}

ジオフェンスを設定する

ジオフェンスを設定するには、Location.startGeofencingAsync メソッドを呼び出します。

// ボタンが押されたときにジオフェンスを設定する
onPressStartGeofencing() {
  Location.startGeofencingAsync(GEOFENCING_ON_ENTER, [{
    latitude: 35.661561,
    longitude: 139.707883,
    radius: 50,
    notifyOnEnter: true,
    notifyOnExit: false,
  }]);
}

startGeofencingAsyncの第1引数はジオフェンスへの出入りがあったときに実行されるタスクの名前です。別途同じ名前でタスク定義を行うのでGEOFENCING_ON_ENTERという文字列定数を用意しています。 第2引数はリージョンオブジェクトの配列です。各リージョンオブジェクトは以下の内容を含みます。
(参考: Geofencing / Location

  • identifier: リージョンを識別するためのID(デフォルトは自動でUUIDベースの文字列が割り当てられる)
  • latitude: 緯度
  • longitude: 経度
  • radius: ジオフェンスの半径(メートル指定)
  • notifyOnEnter: ジオフェンス内に入った場合にタスクをトリガーするためのフラグ
  • notifyOnExit: ジオフェンスから出た場合にタスクをトリガーするためのフラグ

実行されるタスクの定義

ジオフェンスへの出入りがあったときに実行されるタスクは、アプリがバックグラウンド状態でも実行できるようにグローバルスコープでTaskManager.defineTaskを呼び出して定義しています。

タスクが実行されたときのコールバック関数には、eventTypeとトリガーになったリージョン情報が渡されます。
eventTypeを確認し、ジオフェンスに入った場合であればNotifications.presentLocalNotificationAsyncメソッドを呼び出してLocal Notificationを表示させています。

import React from 'react';
import { StyleSheet, Text, View, Button } from 'react-native';
import { Notifications, Permissions, Location, TaskManager } from 'expo';

// タスク名
const GEOFENCING_ON_ENTER = 'geofencingOnEnter';

// タスク定義
TaskManager.defineTask(GEOFENCING_ON_ENTER, ({ data: { eventType, region }, error }) => {
  if (error) {
    console.error(error.message);
    return;
  }
  // ジオフェンス内に入ったイベントであれば、プッシュ通知を表示
  if (eventType === Location.GeofencingEventType.Enter) {
    Notifications.presentLocalNotificationAsync({
      title: 'test geofence notification',
      body: 'geofence notification',
      data: {
        message: 'geofence notification message',
      },
    });
  }
});

//(以下、ルートコンポーネント)

iOSシミュレーターでのデバッグ

実装したExpoプロジェクトは、端末の移動をシミュレーションするためにiOSシミュレーターで実行します。

iOS シミュレーターのメニューから Debug > Location > Custom Location...を選択し、 端末の緯度・経度をジオフェンス内に指定することで、プッシュ通知が表示されるようになります。

iOS シミュレーターの位置情報カスタマイズ画面
iOS シミュレーターの位置情報カスタマイズ画面

iOSシミュレーターに配信されたプッシュ通知
iOSシミュレーターに配信されたプッシュ通知

まとめ

この記事では、Expo SDK v32で新たにサポートされたバックグラウンドでの位置情報取得機能とジオフェンス機能を利用して、特定のエリアに近づいた端末にプッシュ通知を表示する検証をしました。

Expoではネイティブ機能を利用したライブラリを組み込みにくいのですが、Expo SDKのアップデートによって色々なネイティブ機能がカバーされていくことで、そのデメリットが薄れていくんだろうなと感じました。 これからも気になる機能があればどんどん試していこうと思います。