【SwiftUI】通知機能の実装方法!ローカル通知とリモート通知の違い

この記事からわかること

  • Swift UIプッシュ通知実装する方法
  • ローカル通知リモート通知違い
  • 通知の許可申請方法
  • UNMutableNotificationContentとは?
  • ローカル通知を実装する流れ
  • 通知が表示されない原因
  • フォアグラウンドで通知を表示させるには?

index

[open]

\ アプリをリリースしました /

みんなの誕生日

友達や家族の誕生日をメモ!通知も届く-みんなの誕生日-

posted withアプリーチ

SwiftUIで開発しているアプリに通知機能を実装する方法をまとめていきたいと思います。

iOSアプリの通知機能

iOSアプリではユーザーに対して注意を引くために通知機能を実装することが可能です。できることは音を鳴らしたり、以下のようなプッシュ通知(アラート)を送信したり、アイコンにバッジをつけたりすることが可能です。

【SwiftUI】通知機能の実装方法!ローカル通知が届いている要素

プッシュ通知は基本的にはバックグラウンド(アプリが停止している状態)で実行されますが、フォアグラウンドで実装することも可能です。

iOSアプリのプッシュ通知機能にはさらにローカル通知とリモート通知の2種類に分かれます。

ローカル通知

ローカル通知とはオフラインで実行される通知機能のことを指します。デバイス内からプッシュ通知リクエストを送信し、デバイス内で処理されて通知が届くような仕組みになっています。

ローカル通知は事前準備などは必要なく、Swift内でコードを記述するだけで実装することができます。

通知リクエストを送信するタイミングはアプリ内からしか操作できません。リマインダーやアラーム機能など自分でセットして使用するプッシュ通知などはこのローカル通知を使用して実装されています。

リモート通知

リモート通知とはオンラインで実行される通知機能のことを指します。ローカル通知とは異なり、プッシュ通知リクエストをオンライン上から送信し、デバイス内で処理されて通知が届くような仕組みになっています。

仕組みをもう少し詳しくみて見ると以下の通りになります。

【SwiftUI】通知機能の実装方法!リモート通知の仕組み

参考文献:リモート通知サーバーのセットアップ

APNs(Apple Push Notification service)と呼ばれるAppleが運営するサービスを介してプッシュ通知は配信されます。そのためにはまずデバイスの登録を行い、デバイストークンを発行、そのトークンをサーバーに登録することでそのサーバーから通知リクエストを送信することが可能になります。

リモート通知を使用することでユーザーの操作などではなく、開発者や運営者の任意のタイミングでアプリに対して通知を送信することができるようになります。

おすすめ記事:【Swift】リモート通知実装用の証明書(cer)とプロビジョニングプロファイルの作り方

SwiftUIでのローカル通知の実装方法

今回はSwiftUIを使用している場合にボタン押下時にプッシュ通知(ローカル通知)が表示されるような仕組みを実装していきたいと思います。

ローカル通知の実装のポイント

  1. 通知の許可申請
  2. 通知機能の実装

ローカル通知の実装方法は以下の公式リファレンスページを参考にしています。

参考文献:UNMutableNotificationContentクラス

1.通知の許可申請

まずはユーザーにアプリからの通知を許可してもらう必要があります。ユーザーに対して通知の許可申請を送るにはUNUserNotificationCenterクラスを使用します。詳しい使い方は以下の記事をご覧ください。

通知の許可申請を行う処理はイニシャライザの中に組み込んでおきます


struct ContentView: View {
    
    init(){
        let center = UNUserNotificationCenter.current()
        center.requestAuthorization(options: .alert) { granted, error in
            if granted {
                print("許可されました!")
            }else{
                print("拒否されました...")
            }
        }
    }
    
    var body: some View {
        Button(action: {
          // ここに通知送信の処理を記述
        }, label: {
            Text("通知を送信")
        })
    }
}

アプリを起動すると以下のようなポップアップが表示されユーザーに対して通知の許可の有無を問います

UNUserNotificationCenter.current().requestAuthorizationメソッドを使って通知の許可を申請している様子

引数optionsには申請したい通知の種類を指定します。複数指定する場合は配列形式[.alert, .sound, .badge]で指定可能です。

UNAuthorizationOptions構造体のプロティ

struct UNAuthorizationOptions {
  static var alert: UNAuthorizationOptions { get }       // アラート
  static var sound: UNAuthorizationOptions { get }       // サウンド
  static var badge: UNAuthorizationOptions { get }       // バッジ
  static var carPlay: UNAuthorizationOptions { get }     // carPlay
  static var provisional: UNAuthorizationOptions { get } // 通知機能のお試し用(通知許可ポップアップは非表示)
}

3.通知機能の実装

通知機能をメソッドとして作成していきます。まずが完成コードを見てみます。


func sendNotificationRequest(){
    let content = UNMutableNotificationContent()
    content.title = "通知のタイトルです"
    content.body = "通知の内容です"
    
    let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 5, repeats: false)
    let request = UNNotificationRequest(identifier: "通知No.1", content: content, trigger: trigger)
    UNUserNotificationCenter.current().add(request)
}

あとはこのメソッドをボタンのアクション部分で呼び出せば完了です。これはバックグラウンドで実行される通知処理なので、ボタンを押したらホーム画面に戻って5秒後に通知が来ることを確認してみてください。

Button(action: {
    sendNotificationRequest()
}, label: {
    Text("通知を送信")
})

UNMutableNotificationContentクラス

公式:UNMutableNotificationContentクラス

UNMutableNotificationContent通知の内容を作成するクラスです。このクラスのプロパティに通知に載せるタイトルや内容、サウンドなどを渡します。

流れ

  1. インスタンス化し空のローカル通知を作成
  2. 通知の内容を構築
  3. トリガー条件を定義
  4. 通知本体とトリガー条件を含めたリクエストを構築
  5. リクエストを追加

1.インスタンス化し空のローカル通知を作成 & 2.通知の内容を構築

まずはインスタンス化してローカル通知を作成していきます。ここに通知に載せたい内容を渡します。

let content = UNMutableNotificationContent()
content.title = "通知のタイトルです"
content.body = "通知の内容です"

UNTimeIntervalNotificationTrigger

3.トリガー条件を定義

トリガー条件はUNTimeIntervalNotificationTriggerクラスを使って定義します。引数timeInterval通知を表示させるタイミングを秒数単位で指定します。以下の場合は5秒後に通知が表示されます。

let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 5, repeats: false)

repeatsには通知処理を繰り返すかどうかを指定できます。trueにする場合は60秒以上を設定しないと「time interval must be at least 60 if repeating」というエラーを吐いてアプリがクラッシュしてしまいます。

UNNotificationRequest

4.通知本体とトリガー条件を含めたリクエストを構築

UNNotificationRequestクラスを使用してリクエスト情報を構築します。identifierには一意となる識別子を設定し、ローカル通知とトリガーを渡します。

let request = UNNotificationRequest(identifier: "通知No.1", content: content, trigger: trigger)

5.リクエストを追加

最後にユーザーに通知許可申請を送る際にも使用したUNUserNotificationCenterクラスのaddメソッドを使用してローカル通知の配信をスケジュールに追加することができます。

UNUserNotificationCenter.current().add(request)

これでローカル通知の実装は完了です。シミュレーターでも正常に動作しますので試してみてください。

provisionalを指定すると通知が表示されない

optionsの引数に通知機能をお試しできる.provisionalを指定するとシミュレーターでは通知が表示されませんでした。

let center = UNUserNotificationCenter.current()
center.requestAuthorization(options: .provisional) { granted, error in
    if granted {
        print("許可されました!")
    }else{
        print("拒否されました...")
    }
}

シミュレーターで通知機能のチェックをしたい場合は普通に.alertなどを指定すれば正常に動作します。

App Delegateを使って通知許可申請

先程は通知許可申請をContentView構造体のイニシャライザの中に組み込みましたがこれではこのページが表示されたタイミングでないと許可申請が表示されません。

アプリを起動したタイミングでの申請が望ましいと思います。これを解決するためにApp Delegateを使用します。詳しい使い方は以下の記事を参考にしてください。

【SwiftUI】App Delegateとは?実装方法と使い方

App Delegateを使って通知許可申請を行う流れ

  1. AppDelegateクラスの生成
  2. アプリ起動時に処理される部分に通知許可申請を追加
  3. <プロジェクト>App.swiftにデリゲートを登録

1.AppDelegateクラスの生成

まずは必要となるAppDelegateクラスを作成します。SwiftUIではUIKitと違い「AppDelegate .swift」ファイルがないので自作していきます。


import Foundation
import UIKit

class AppDelegate:NSObject,UIApplicationDelegate{
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
        print("アプリが起動したよ")
        return true
    }
}

2.アプリ起動時に処理される部分に通知許可申請を追加

続いて通知許可申請するためのコードをapplicationメソッドの中に記述します。


import Foundation
import UIKit

class AppDelegate:NSObject,UIApplicationDelegate{
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
      let center = UNUserNotificationCenter.current()
        center.requestAuthorization(options: [.alert, .sound, .badge]) { granted, error in
            if granted {
                print("許可されました!")
            }else{
                print("拒否されました...")
            }
        }
        return true
    }
}

3.<プロジェクト>App.swiftにデリゲートを登録

最後に「<プロジェクト>App.swift」内から先ほど作成したAppDelegateクラスを@UIApplicationDelegateAdaptorを付与して定義すれば完成です。


import SwiftUI

@main
struct HelloWorldApp: App {
    
    @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
    
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

フォアグラウンドで通知を表示させる

公式リファレンス:userNotificationCenterメソッド

フォアグラウンドで通知を表示させたい場合AppDelegateクラスにUNUserNotificationCenterDelegateプロトコルを準拠させ、userNotificationCenterメソッドを実装し、UNUserNotificationCenterクラスのデリゲートにクラスインスタンスを渡します。


import Foundation
import UIKit

class AppDelegate:NSObject,UIApplicationDelegate,UNUserNotificationCenterDelegate{
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
        
        let center = UNUserNotificationCenter.current()
        center.requestAuthorization(options: [.alert, .sound, .badge]) { granted, error in
            if granted {
                print("許可されました!")
                UNUserNotificationCenter.current().delegate = self
            }else{
                print("拒否されました...")
            }
        }
        return true
    }
    
    func userNotificationCenter(
        _ center: UNUserNotificationCenter,
        willPresent notification: UNNotification,
        withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
            completionHandler([[.banner, .list, .sound]])
    }
}

これで通知がフォアグラウンドでも表示されるようになります。

通知のタップを検知する

Swiftで通知がタップされたことを検知するにはUNUserNotificationCenterDelegateのデリゲートメソッドの1つであるuserNotificationCenter(_:,didReceive:,withCompletionHandler:)を利用します。


extension AppDelegate :UNUserNotificationCenterDelegate{

    func userNotificationCenter(
        _ center: UNUserNotificationCenter,
        didReceive response: UNNotificationResponse,
        withCompletionHandler completionHandler: @escaping () -> Void) {

        print("タップされたよ")
        completionHandler()
    }
}

またこの通知のタップはローカル通知だけでなくリモート通知でも動作するようです。

おすすめ記事:【Swift UI】アプリアイコンにバッジを付与する方法!applicationIconBadgeNumber

Androidでの実装はこちら

まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。

ご覧いただきありがとうございました。

私がSwift UI学習に使用した参考書

searchbox

スポンサー

ProFile

ame

趣味:読書,プログラミング学習,サイト制作,ブログ

IT嫌いを克服するためにITパスを取得しようと勉強してからサイト制作が趣味に変わりました笑
今はCMSを使わずこのサイトを完全自作でサイト運営中〜

New Article

index