NRIネットコム Blog

NRIネットコム社員が様々な視点で、日々の気づきやナレッジを発信するメディアです

【Swift】通知処理の実装方法

記事の概要

今回の記事で紹介させていただく通知処理は以下のようなものです。

今回実装するもの

通知について

よくあるケースとしてはメールが来た時に音と共に画面上部に表示されるものです。
また画面に表示されるものとしては冒頭に貼ってあるgifを参照していただけるとわかると思います。
今回は端末内で完結する通知処理を実装してきますが、Firebaseなどを利用して、リモートから通知を行うこともできます。その際はこちらのドキュメントを参考にしてください。

実装方法

ここからは実装を行うための手順から実装方法です。

手順

今回実装する手順として
1.通知の許可を求める
2.通知処理
の手順で実装していきます。

通知の許可を求める

まず初めにApp.swiftファイルにAppDelegateを追加し、通知の許可を得る為にアラート表示する処理を行います。
全体のコードは以下の様な感じです。

import SwiftUI
import UserNotifications

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


class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate {
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool {
        UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) { (granted, _) in
            if granted {
                UNUserNotificationCenter.current().delegate = self
            }
        }
        return true
    }
}

通知を実装するにはUserNotificationsをインポートする必要があります。
UserNotificationsは画面上にバナーを表示させたり、音をだしたりしてユーザーに通知するために必要なフレームワークです。
つまりローカルだろうが、リモートだろうが通知処理をする際はUserNotificationsが必要になります。
またSwiftUIで実装する場合、初期状態ではAppDelegateの定義が無い為、自分で作る必要があります。
(※バックグラウンドでしか利用しない場合はAppDelegateを作らなくても実装できます)
AppDelegateを作成したら、その中に通知の許可を求める実装を行います。
.requestAuthorization(options: [.alert, .sound, .badge])でユーザーへ求めることができますが、通知にも様々なものがあり、optionsで定義することができます。
それぞれの内容は以下の通りです。
また今回定義した3つ以外にもあるので、必要に応じて定義していただければと思います。

options 内容
alert 通知を表示
sound 音の再生
badge バッジの表示(SNS系のアプリでよく見るアプリアイコンに通知の数を表示するもの)
carPlay CarPlay環境で通知を表示
providesAppNotificationSettings 通知設定画面へ遷移させる
provisional 一時的に通知機能を試せる(許可を得るアラートは表示されず、requestAuthorizationを実行したタイミングで通知の継続が選択できるアラートが表示される)

(上記一覧に関するdocument)

ここで一旦シミュレーターを起動すると以下の様なアラートが表示されます!

Allowを選択して通知の許可をします。
ここまでが手順1です。

任意のタイミングで許可を得るアラートを表示したい場合

上記の実装ではアプリを起動したタイミングで、通知の許可を求めるアラートが表示されます。
しかしUIApplicationDelegateを使用せず、任意のタイミングで許可を求めたい場合もあるかと思います。
そんな時は以下のメソッドを用意すると、任意のタイミングで実行することができます。
また許可の状態を得たい場合はrequestAuthorizationの結果を受け取るプロパティを用意して判定することで状態を知ることができます。

struct ContentView: View {
    var body: some View {
        VStack {
            Button {
                Task {
                    await requestAuthorization()
                }
            } label: {
                Text("通知を確認")
            }            
        }
    }
}


public func requestAuthorization() async {
    do {
        let requestResult = try await UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge])
        if requestResult {
            print("通知の許可を得られたよ")
        } else {
            print("通知の許可を得られなかったよ")
        }
    } catch {
        print(error)
    }
}

実装の様子

通知処理

これで通知の許可を求める実装ができたのでここから通知処理を実装します!

public func notification() async {
    do {
        let content = UNMutableNotificationContent()
        let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 3, repeats: false)
        //通知内容
        content.title = "ここは通知タイトル部分に表示されるよ"
        content.body = "ここは通知の説明部分に表示されるよ"
        content.sound = UNNotificationSound.default
        content.badge = 1
        //通知リクエストを作成
        let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: trigger)
        try await UNUserNotificationCenter.current().add(request)
    } catch {
        print(error)
    }
}

timeIntervalでは通知までの時間を指定することができます。(1 = 1秒)
repeatsでは繰り返し通知するか設定することができます。(※trueにする場合timeIntervalを60以上にしないとクラッシュします。)
content.soundでは通知時に出したい音を指定できます。
content.badgeはアイコン部分に表示される数字です。

※通知はバックグラウンドで行われるものとなっていますので、上記のメソッドを叩いても何もおきません。
メソッドを叩いてバックグラウンドへ移動することで通知がきます。

適当にボタンを用意して叩くと以下の様に通知が来ます。

通知の様子

フォアグラウンドで通知したい!

アプリを起動中に画面でなく、通知で状態を知らせたい場合もあるかと思います。
そんな時はフォアグラウンドで通知できるように処理を行います。
extensionでAppDelegateに追加します。

extension AppDelegate: UNUserNotificationCenterDelegate {
    func userNotificationCenter(
        _ center: UNUserNotificationCenter,
        willPresent notification: UNNotification,
        withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
            completionHandler([[.banner, .list, .sound]])
        }
}

コード全体

import SwiftUI
import UserNotifications

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


class AppDelegate: UIResponder, UIApplicationDelegate {
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool {
        UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) { (granted, _) in
            if granted {
                UNUserNotificationCenter.current().delegate = self
            }
        }
        return true
    }
}

extension AppDelegate: UNUserNotificationCenterDelegate {
    func userNotificationCenter(
        _ center: UNUserNotificationCenter,
        willPresent notification: UNNotification,
        withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
            completionHandler([[.banner, .list, .sound]])
        }
}
struct ContentView: View {
    var body: some View {
        VStack {
            Button {
                Task {
                    await notification()
                }
            } label: {
                Text("Push!!")
            }
            
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

public func notification() async {
    do {
        let content = UNMutableNotificationContent()
        let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 3, repeats: false)
        //通知内容
        content.title = "ここは通知タイトル部分に表示されるよ"
        content.body = "ここは通知の説明部分に表示されるよ"
        content.sound = UNNotificationSound.default
        content.badge = 1
        //通知リクエストを作成
        let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: trigger)
        try await UNUserNotificationCenter.current().add(request)
    } catch {
        print(error)
    }
}

これでフォアグラウンドでも通知を行うことができます。

おまけ

通知を許可するかはユーザー次第ですので、通知許可の状態によってユーザーへのアプローチを可変的に行いたいケースがあります。
そんな時はgetNotificationSettingsで状態を知ることができます。

UNUserNotificationCenter.current().getNotificationSettings { settings in
        switch settings.authorizationStatus {
        case .notDetermined:
            
        case .denied:
            
        case .authorized:
            
        case .provisional:
            
        case .ephemeral:
            
        }
    }
authorizationStatus 内容
notDetermined 許可の設定を行っていない場合
denied 通知が拒否されている場合
authorized 通知が許可されている場合
provisional Provisional Authorization が有効になっている場合
ephemeral AppClip など限られた時間内の通知が有効な場合

最後に

以上通知の実装方法でした。
誰かの役に立てば幸いです!

執筆者: 岡優志(oka yuji)
元高校教員→モバイルエンジニアで主にiOSを担当。
Twitter: oka yuji