rust_macios/user_notifications/
un_user_notification_center_delegate.rs

1#![allow(trivial_casts)]
2
3use std::sync::Once;
4
5use block::{ConcreteBlock, IntoConcreteBlock};
6use objc::{
7    declare::ClassDecl,
8    msg_send,
9    runtime::{Class, Object, Sel},
10    sel, sel_impl, Encode,
11};
12
13use crate::objective_c_runtime::{class, id, traits::PNSObject};
14
15use super::{
16    UNNotification, UNNotificationResponse, UNUserNotificationCenter,
17    UNUSER_NOTIFICATION_CENTER_PTR,
18};
19
20#[repr(u64)]
21#[derive(Debug)]
22/// Constants indicating how to present a notification in a foreground app.
23pub enum UNNotificationPresentationOptions {
24    ///
25    None = 0,
26    /// Apply the notification's badge value to the app’s icon.
27    Badge = (1 << 0),
28    /// Play the sound associated with the notification.
29    Sound = (1 << 1),
30    /// Display the alert using the content provided by the notification.
31    #[deprecated = "Use 'List | Banner' instead."]
32    Alert = (1 << 2),
33    /// Show the notification in Notification Center.
34    List = (1 << 3),
35    /// Present the notification as a banner.
36    Banner = (1 << 4),
37}
38
39/// An interface for processing incoming notifications and responding to notification actions.
40pub trait PUNUserNotificationCenterDelegate: PNSObject {
41    /* Handling the Selection of Custom Actions
42     */
43
44    /// Asks the delegate to process the user's response to a delivered notification.
45    fn user_notification_center_did_receive_notification_response_with_completion_handler<F>(
46        &mut self,
47        center: &UNUserNotificationCenter,
48        response: &UNNotificationResponse,
49        completion_handler: F,
50    ) where
51        F: IntoConcreteBlock<(), Ret = ()> + 'static,
52    {
53        let block = ConcreteBlock::new(completion_handler);
54        let block = block.copy();
55
56        unsafe {
57            msg_send![self.m_self(), userNotificationCenter:center.m_self() didReceiveNotificationResponse:response.m_self() withCompletionHandler: block]
58        }
59    }
60
61    /* Receiving Notifications
62     */
63
64    /// Asks the delegate how to handle a notification that arrived while the app was running in the foreground.
65    fn user_notification_center_will_present_notification_with_completion_handler<F>(
66        &mut self,
67        center: &UNUserNotificationCenter,
68        notification: &UNNotification,
69        completion_handler: F,
70    ) where
71        F: IntoConcreteBlock<(UNNotificationPresentationOptions,), Ret = ()> + 'static,
72    {
73        let block = ConcreteBlock::new(completion_handler);
74        let block = block.copy();
75
76        unsafe {
77            msg_send![self.m_self(), userNotificationCenter:center.m_self() willPresentNotification:notification.m_self() withCompletionHandler: block]
78        }
79    }
80
81    /* Displaying Notification Settings
82     */
83
84    /// Asks the delegate to display the in-app notification settings.
85    fn user_notification_center_open_settings_for_notification(
86        &mut self,
87        center: &UNUserNotificationCenter,
88        notification: &UNNotification,
89    ) {
90        unsafe {
91            msg_send![self.m_self(), userNotificationCenter: center.m_self() openSettingsForNotification: notification.m_self()]
92        }
93    }
94}
95
96/// A handy method for grabbing our [`UNUserNotificationCenterDelegate`] from the pointer. This is different from our
97/// standard `utils` version as this doesn't require `RefCell` backing.
98fn user_notification_center<T>(this: &mut Object) -> &mut T {
99    unsafe {
100        let app_ptr: usize = *this.get_ivar(UNUSER_NOTIFICATION_CENTER_PTR);
101        let app = app_ptr as *mut T;
102        &mut *app
103    }
104}
105
106extern "C" fn user_notification_center_did_receive_notification_response_with_completion_handler<
107    T: PUNUserNotificationCenterDelegate,
108    F: Fn() + Send + Sync + 'static,
109>(
110    this: &mut Object,
111    _: Sel,
112    _: id,
113    center: UNUserNotificationCenter,
114    response: UNNotificationResponse,
115    completion_handler: F,
116) {
117    user_notification_center::<T>(this)
118        .user_notification_center_did_receive_notification_response_with_completion_handler(
119            &center,
120            &response,
121            completion_handler,
122        )
123}
124
125extern "C" fn user_notification_center_will_present_notification_with_completion_handler<
126    T: PUNUserNotificationCenterDelegate,
127    F: Fn(UNNotificationPresentationOptions) + Send + Sync + 'static,
128>(
129    this: &mut Object,
130    _: Sel,
131    _: id,
132    center: UNUserNotificationCenter,
133    notification: UNNotification,
134    completion_handler: F,
135) {
136    user_notification_center::<T>(this)
137        .user_notification_center_will_present_notification_with_completion_handler(
138            &center,
139            &notification,
140            completion_handler,
141        )
142}
143
144extern "C" fn user_notification_center_open_settings_for_notification<
145    T: PUNUserNotificationCenterDelegate,
146>(
147    this: &mut Object,
148    _: Sel,
149    _: id,
150    center: UNUserNotificationCenter,
151    notification: UNNotification,
152) {
153    user_notification_center::<T>(this)
154        .user_notification_center_open_settings_for_notification(&center, &notification)
155}
156
157/// Registers an `NSObject` application delegate, and configures it for the various callbacks and
158/// pointers we need to have.
159pub fn register_user_notification_center_delegate_class<
160    T: PUNUserNotificationCenterDelegate + PUNUserNotificationCenterDelegate,
161    F1: Fn() + Send + Sync + 'static + Encode,
162    F2: Fn(UNNotificationPresentationOptions) + Send + Sync + 'static + Encode,
163>() -> *const Class {
164    static mut DELEGATE_CLASS: *const Class = 0 as *const Class;
165    static INIT: Once = Once::new();
166
167    INIT.call_once(|| unsafe {
168        let superclass = class!(NSObject);
169        let mut decl = ClassDecl::new("RSTUNUserNotificationCenterDelegate", superclass).unwrap();
170
171        decl.add_ivar::<usize>(UNUSER_NOTIFICATION_CENTER_PTR);
172
173        decl.add_method(
174            sel!(userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler:),
175            user_notification_center_did_receive_notification_response_with_completion_handler::<
176                T,
177                F1,
178            > as extern "C" fn(&mut Object, _, _, _, _, _),
179        );
180
181        decl.add_method(
182            sel!(userNotificationCenter:willPresentNotification:withCompletionHandler:),
183            user_notification_center_will_present_notification_with_completion_handler::<T, F2>
184                as extern "C" fn(&mut Object, _, _, _, _, _),
185        );
186
187        decl.add_method(
188            sel!(userNotificationCenter:openSettingsForNotification:),
189            user_notification_center_open_settings_for_notification::<T>
190                as extern "C" fn(&mut Object, _, _, _, _),
191        );
192
193        DELEGATE_CLASS = decl.register();
194    });
195
196    unsafe { DELEGATE_CLASS }
197}