Skip to content

Instantly share code, notes, and snippets.

@pine
Last active April 7, 2024 07:46
Show Gist options
  • Star 42 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pine/8b7e58ff7fa259df17351d959c6118f7 to your computer and use it in GitHub Desktop.
Save pine/8b7e58ff7fa259df17351d959c6118f7 to your computer and use it in GitHub Desktop.
猿でも分かるプッシュ通知

この記事は 高知工科大 Advent Calendar 2017 の17日目の記事です。
昨年度のカレンダーはこちら: 高知工科大 Advent Calendar 2016


趣味で 絵文字ジェネレーター というウェブサービスを開発しています。

まだ ⭐ を付けていない方は、この記事を読む前に Emoji-Web へ ⭐ を付けてください。
なお、:star: を付けた方は、この記事を閉じても構いません。

猿でも分かるプッシュ通知

プッシュ通知とは?

この記事を読んでいる意識の高い方は、きっとスマートフォンを持っている方でしょう。
プッシュ通知とは、チャットなどで新しいメッセージが来ると、スマートフォンで上部に出てくる例のアレを指します。

プッシュ通知は、ユーザーへ何らかの情報を "なる早" で伝えたい場合に主に利用されます。
メッセージが届いた、カレンダーのイベントが近づいた、仮想通貨の値段が急落したなどなど。

ユーザーは我儘なので、プッシュ通知を送りすぎるとすぐ通知をブロックされてしまいます。
何でもかんでもプッシュ通知を送ればいいというものではない、なかなか難しい代物でもあります。

この記事では、プッシュ通知とはそもそも何なのかという話と、プッシュ通知を実際に送ってみる方法をコードを交えて說明したいと思います。

プッシュ通知には大きく二種類がある

プッシュ通知は大きくわけて二種類あります。
ユーザーからはすべて同じプッシュ通知に見えますが、内部の処理は全く異なります。

  • ローカル通知
  • リモート通知

ローカル通知は、既に起動している (もしくは起動していた) アプリケーションからプッシュ通知を送るものです。
これは、インターネットに接続していなくても利用できます。

身近なものだと、アラームアプリで時間になったら通知されるのがこれにあたります。

リモート通知は、インターネットに接続していなければ受け取れません。
身近なものだと、チャットのメッセージ通知がこれにあたります。

参考

iOS v.s. Android

日本は世界の中でも珍しいほど iOS のシェアが高い国家です。
時期や調査方法によっても異なりますが、だいたい iOS/Android で半々か iOS の方が多い結果がでます。

このシェア比ははっきり言って異常ですが、それは一旦置いておきます。
ということで、この記事では iOS/Android のどちらのサンプルコードも書くこととします。

なお、この記事では iOS/Android は以下のバージョン以上を対象とします。
これに満たない端末は IT 文化の発展を妨げているため、今すぐ投げ捨ててください。

  • iOS 10
  • Android 5.0.2

iOS によるローカル通知

サンプルコード: https://github.com/pine/AdventCalendar_KUT_20171217_LocalPush_iOS

iOS でローカル通知を利用するには User Notifications Framework を利用します。これは iOS 10 以降で利用可能です。
iOS 9 では別の方法を使う必要がありますが、この記事では解説しません。

まず、プッシュ通知の権限を要求するためのコードを書く必要があります。
これをアプリ起動時の処理を書くことができる application(_:didFinishLaunchingWithOptions:) に記載します。

UNUserNotificationCenter.current()
    .requestAuthorization(options: [.badge, .sound, .alert], completionHandler: { (granted, error) in
        // 許可されない場合は、アプリを終了する
        if !granted {
            let alert = UIAlertController(
                title: "エラー",
                message: "プッシュ通知が拒否されています。設定から有効にしてください。",
                preferredStyle: .alert
            )
            
            // 終了処理
            let closeAction = UIAlertAction(title: "閉じる", style: .default) { _ in exit(1) }
            alert.addAction(closeAction)
            
            // ダイアログを表示
            self.window?.rootViewController?.present(alert, animated: true, completion: nil)
        }
    })

また、アプリが起動している時にも通知を飛ばすために、 UNUserNotificationCenterDelegate を実装する必要があります。application(_:didFinishLaunchingWithOptions:) にて下記記載を追加し、

UNUserNotificationCenter.current().delegate = self

UNUserNotificationCenterDelegate を実装します。

extension AppDelegate: UNUserNotificationCenterDelegate {
    func userNotificationCenter(
        _ center: UNUserNotificationCenter,
        willPresent notification: UNNotification,
        withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void)
    {
        // アプリ起動時も通知を行う
        completionHandler([ .badge, .sound, .alert ])
    }
}

ここまで終わると、下記のコードでプッシュ通知が発火できるようになります。

let content = UNMutableNotificationContent()
content.title = "れんちょんからのお知らせ"
content.body = "にゃんぱすー"
content.sound = UNNotificationSound.default()

// 通知に画像を追加
if let path = Bundle.main.path(forResource: "Renchon", ofType: "jpg") {
    content.attachments = [try! UNNotificationAttachment(identifier: "renchon", url: URL(fileURLWithPath: path), options: nil)]
}

// 直ぐに通知を表示
let request = UNNotificationRequest(identifier: "immediately", content: content, trigger: nil)
UNUserNotificationCenter.current().add(request, withCompletionHandler: nil)

実行すると、下記のように通知が表示されます。

Android によるローカル通知

サンプルコード: https://github.com/pine/AdventCalendar_KUT_20171217_LocalPush_Android

Android で ローカル通知を利用するには、サポートライブラリにある NotificationCompat.Builder クラスを利用します。Android のネイティブ API である Notification.Builder を使ってもできますが、下位互換のあるサポートライブラリを使うのが一般的です。

どういった互換処理かはソースコードを参照してください
https://android.googlesource.com/platform/frameworks/support/+/jb-dev/v4/java/android/support/v4/app/NotificationCompat.java

NotificationCompat.Builder を使ってプッシュ通知を行うには、以下のようなコードを記述します。

// Android 8 (Oreo) への対応
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    val manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
    val channel = NotificationChannel("default", "Default", NotificationManager.IMPORTANCE_DEFAULT)
    channel.description = "Default channel"
    manager.createNotificationChannel(channel)
}

// プッシュ通知を表示
val intent = MainActivity.createIntent(this)
val contentIntent = PendingIntent.getActivity(applicationContext, 0, intent, PendingIntent.FLAG_ONE_SHOT)

val notification = NotificationCompat.Builder(this, "default")
        .setSmallIcon(R.drawable.ic_launcher_foreground)
        .setContentTitle("れんちょんからのお知らせ")
        .setContentText("にゃんぱすー")
        .setContentIntent(contentIntent)
        .setAutoCancel(true)
        .setLargeIcon(BitmapFactory.decodeResource(resources, R.drawable.ic_renchon))
        .build()

val manager = NotificationManagerCompat.from(this)
manager.notify(1, notification)

リモート通知の仕組み

次はリモート通知について說明します。 リモート通知は、端末へ通知を送りたいサーバーなどから端末へ通知を送る手段です。

リモート通知を送りたい場合、端末と直接コネクションを貼るのではなく、Apple や Google が提供するプッシュ通知サーバーに対してプッシュ通知を送信することを依頼します。Apple は APNs (Apple Push Notification service)、Google は GCM/FCM というサービス名でプッシュ通知サービスを提供しています。APNs や GCM/FCM は端末との TCP コネクションを貼っており、プッシュ通知の配信を一括して代行しています。

Apple や Google が提供するこれらのサービスを直接利用する方法がひとつの方法です。他には、これらのプッシュ通知のサーバーを一段ラップした API を提供するサービスを使う方法があります。

Firebase によるリモート通知

リモート通知を行うには、Google が公式に提供している Firebase Cloud Message (FCM) を使うのが便利です。Android に対しては直接プッシュ通知を配信、iOS 端末に対しても APNs 経由で配信ができます。

FCM を使ったプッシュ通知の具体的なコードも書く予定だったのですが、間に合わなかったため今回は見送ります。

まとめ

プッシュ通知には、ローカル通知とリモート通知の2種類があります。 ローカル通知はアプリから直接発火、リモート通知は Google や Apple が提供するサーバーを経由して配信されます。

プッシュ通知は簡単に実装できるので、ぜひ試してみてください!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment