iOSアプリでユーザーから通知の許可をもらうにはUNUserNotificationCenter.requestAuthorization()を呼ぶ必要がある。このメソッドはアラートを表示してユーザーに許可/却下を選択させる。
iOS 18以降ではアプリをロックしたり非表示にしたりできる*1。アプリを非表示にすると、アイコンだけでなく通知も非表示になる。
これがrequestAuthorization()の挙動に影響を与えていそうな事例を見つけたので実験する。
環境
コード
do { let granted = try await UNUserNotificationCenter.current().requestAuthorization() print("granted:", granted) let settings = await UNUserNotificationCenter.current().notificationSettings() print("settings:", settings) } catch { print("error:", error) }
手順
アプリをインストールしただけの状態(以下A)とロックした状態(以下B)で許可、却下、キャンセル*2した。ロック&非表示にした状態(以下C)では、そもそもアラートが表示されないので何もしなかった。
結果
4行目のsettings: UNNotificationSettingsは以下のようになった。
- Aの許可、Bの許可で同じ
- Aの却下、Bの却下で同じ
- Aのキャンセル、Bのキャンセル、Cで同じ
ログ
アプリをインストールしただけの状態
許可
granted: true settings: <UNNotificationSettings: 0x000000000; authorizationStatus: Authorized, notificationCenterSetting: Enabled, soundSetting: NotSupported, badgeSetting: NotSupported, lockScreenSetting: Enabled, carPlaySetting: NotSupported, remoteNotifications: Enabled, announcementSetting: Disabled, criticalAlertSetting: NotSupported, timeSensitiveSetting: NotSupported, alertSetting: Enabled, scheduledDeliverySetting: Disabled, directMessagesSetting: NotSupported, summarizationSetting: Enabled, prioritizationSetting: Enabled, showsPreviewsSetting: WhenAuthenticated, alertStyle: Banner, groupingSetting: Default providesAppNotificationSettings: No>
却下
granted: false settings: <UNNotificationSettings: 0x000000000; authorizationStatus: Denied, notificationCenterSetting: Disabled, soundSetting: NotSupported, badgeSetting: NotSupported, lockScreenSetting: Disabled, carPlaySetting: NotSupported, remoteNotifications: Disabled, announcementSetting: Disabled, criticalAlertSetting: NotSupported, timeSensitiveSetting: NotSupported, alertSetting: Disabled, scheduledDeliverySetting: Disabled, directMessagesSetting: NotSupported, summarizationSetting: Enabled, prioritizationSetting: Enabled, showsPreviewsSetting: WhenAuthenticated, alertStyle: None, groupingSetting: Default providesAppNotificationSettings: No>
キャンセル
granted: false settings: <UNNotificationSettings: 0x000000000; authorizationStatus: NotDetermined, notificationCenterSetting: NotSupported, soundSetting: NotSupported, badgeSetting: NotSupported, lockScreenSetting: NotSupported, carPlaySetting: NotSupported, remoteNotifications: NotSupported, announcementSetting: NotSupported, criticalAlertSetting: NotSupported, timeSensitiveSetting: NotSupported, alertSetting: NotSupported, scheduledDeliverySetting: NotSupported, directMessagesSetting: NotSupported, summarizationSetting: NotSupported, prioritizationSetting: NotSupported, showsPreviewsSetting: Always, alertStyle: None, groupingSetting: Default providesAppNotificationSettings: No>
ロックした状態
許可
granted: true settings: <UNNotificationSettings: 0x000000000; authorizationStatus: Authorized, notificationCenterSetting: Enabled, soundSetting: NotSupported, badgeSetting: NotSupported, lockScreenSetting: Enabled, carPlaySetting: NotSupported, remoteNotifications: Enabled, announcementSetting: Disabled, criticalAlertSetting: NotSupported, timeSensitiveSetting: NotSupported, alertSetting: Enabled, scheduledDeliverySetting: Disabled, directMessagesSetting: NotSupported, summarizationSetting: Enabled, prioritizationSetting: Enabled, showsPreviewsSetting: WhenAuthenticated, alertStyle: Banner, groupingSetting: Default providesAppNotificationSettings: No>
却下
granted: false settings: <UNNotificationSettings: 0x000000000; authorizationStatus: Denied, notificationCenterSetting: Disabled, soundSetting: NotSupported, badgeSetting: NotSupported, lockScreenSetting: Disabled, carPlaySetting: NotSupported, remoteNotifications: Disabled, announcementSetting: Disabled, criticalAlertSetting: NotSupported, timeSensitiveSetting: NotSupported, alertSetting: Disabled, scheduledDeliverySetting: Disabled, directMessagesSetting: NotSupported, summarizationSetting: Enabled, prioritizationSetting: Enabled, showsPreviewsSetting: WhenAuthenticated, alertStyle: None, groupingSetting: Default providesAppNotificationSettings: No>
キャンセル
granted: false settings: <UNNotificationSettings: 0x000000000; authorizationStatus: NotDetermined, notificationCenterSetting: NotSupported, soundSetting: NotSupported, badgeSetting: NotSupported, lockScreenSetting: NotSupported, carPlaySetting: NotSupported, remoteNotifications: NotSupported, announcementSetting: NotSupported, criticalAlertSetting: NotSupported, timeSensitiveSetting: NotSupported, alertSetting: NotSupported, scheduledDeliverySetting: NotSupported, directMessagesSetting: NotSupported, summarizationSetting: NotSupported, prioritizationSetting: NotSupported, showsPreviewsSetting: Always, alertStyle: None, groupingSetting: Default providesAppNotificationSettings: No>
ロック&非表示にした状態
granted: false settings: <UNNotificationSettings: 0x000000000; authorizationStatus: NotDetermined, notificationCenterSetting: NotSupported, soundSetting: NotSupported, badgeSetting: NotSupported, lockScreenSetting: NotSupported, carPlaySetting: NotSupported, remoteNotifications: NotSupported, announcementSetting: NotSupported, criticalAlertSetting: NotSupported, timeSensitiveSetting: NotSupported, alertSetting: NotSupported, scheduledDeliverySetting: NotSupported, directMessagesSetting: NotSupported, summarizationSetting: NotSupported, prioritizationSetting: NotSupported, showsPreviewsSetting: Always, alertStyle: None, groupingSetting: Default providesAppNotificationSettings: No>
論
特に最後の結果が重要で、このようなコードの場合、Cの端末では何も起きないpre-alert viewが表示されてしまう。
switch settings.authorizationStatus { case .notDetermined: presentPreAlertView(onDismiss: { try await UNUserNotificationCenter.current().requestAuthorization() }) default: break }
冒頭に書いた影響されていそうな挙動はまさにそれだったし、原因も思ったとおりだったので満足した。
Aのキャンセル、Bのキャンセル、CでUNNotificationSettingsが全く同じだし、ロックや非表示の状態を取るAPIも無さそうなので、現状ではこの挙動を解消することはできないと思う*3。場当たり的に対処するなら、pre-alert viewを表示した回数をUserDefaultsにでも持っておいて、しきい値を超えたら表示しないようにすればよいと思う。非表示にしたアプリの通知は表示されないから許可を貰う必要も無い。
*1:https://support.apple.com/en-au/guide/iphone/iph00f208d05
*2:ホーム画面に移動するジェスチャでキャンセルできる
*3:ユーザー的には一度非表示を解除してアラートに答えるとよいはず