Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
451e938
feat: push notifications (wip)
sgammon Nov 11, 2024
11854b2
chore: align versions
sgammon Nov 12, 2024
f167515
feat: initial push support for windows
sgammon Nov 16, 2024
b4109eb
chore: fmt
sgammon Nov 16, 2024
79cae51
fixup! non-windows
sgammon Nov 16, 2024
f9c378b
Merge remote-tracking branch 'tao-origin/dev' into dev
saurL Jul 31, 2025
e20037c
changed dependencies to windows-futures
saurL Jul 31, 2025
4510986
chore(deps): update rust crate dlopen2 to 0.8.0 (#1117)
renovate[bot] Aug 14, 2025
9fa248f
feat: add focusable property and Window::set_focusable (#1120)
robertrpf Aug 17, 2025
7756abb
publish new versions (#1130)
github-actions[bot] Aug 17, 2025
337dcd9
feat: MacOS: add universal applink support (#1108)
Simon-Laux Aug 18, 2025
c0a197f
publish new versions (#1131)
github-actions[bot] Aug 18, 2025
72aabd8
chore: don't publish audit PDF to crates.io (#1132)
Legend-Master Aug 20, 2025
b6b1ccf
feat: push notifications (wip)
sgammon Nov 11, 2024
49d7e2c
feat: initial push support for windows
sgammon Nov 16, 2024
b90a50d
chore: fmt
sgammon Nov 16, 2024
841a9d3
fixup! non-windows
sgammon Nov 16, 2024
d30b477
fixed merge
saurL Aug 21, 2025
f064348
Merge branch 'dev' into feat/push-notification
saurL Aug 21, 2025
0dbfb00
added register for apns function for ios
saurL Aug 21, 2025
3678b93
added function to call event on registration
saurL Aug 21, 2025
769f2df
added function to call event on registration
saurL Aug 21, 2025
92a23d7
added function to call event on registration
saurL Aug 21, 2025
a62638b
changed map non user event to return ok on failregister
saurL Aug 21, 2025
e42ec5c
test
saurL Aug 22, 2025
71dc484
undo test
saurL Aug 22, 2025
2c31465
test
saurL Aug 22, 2025
7f552e7
test
saurL Aug 22, 2025
0144df5
removed call to registerforremotenotification
saurL Aug 24, 2025
23a1c20
removed windows implementation
saurL Aug 27, 2025
e8094af
Merge branch 'feat/push-notification' of github.com:saurL/tao into fe…
saurL Aug 27, 2025
39a636f
removed feature push-notification and windows implementation
saurL Aug 27, 2025
b9396fd
removed duplicated import
saurL Aug 27, 2025
44b174e
Merge branch 'dev' into feat/push-notification
saurL Aug 27, 2025
97631c1
Discard changes to src/platform_impl/windows/event_loop.rs
FabianLars Aug 27, 2025
2d3b059
Discard changes to Cargo.lock
FabianLars Aug 27, 2025
bb1ce92
Discard changes to Cargo.toml
FabianLars Aug 27, 2025
ebfc069
Merge branch 'dev' into feat/push-notification
FabianLars Aug 27, 2025
8fb5449
Delete src/push.rs
FabianLars Sep 8, 2025
b89ec2c
Discard changes to src/lib.rs
FabianLars Sep 8, 2025
6b6fdd6
Update event.rs
FabianLars Sep 8, 2025
d367526
Update app_delegate.rs
FabianLars Sep 8, 2025
6a340d5
fmt
FabianLars Sep 8, 2025
309a574
Merge branch 'dev' into feat/push-notification
FabianLars Sep 8, 2025
565e0e2
Create push-token.md
FabianLars Sep 8, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changes/push-token.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
tao: patch
---

Add Events for Apple's Push Token registration.
19 changes: 19 additions & 0 deletions src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,19 @@ pub enum Event<'a, T: 'static> {
/// - **Other**: Unsupported.
#[non_exhaustive]
Reopen { has_visible_windows: bool },

/// Emitted when registration completes and an application push token is made available; on Apple
/// platforms, this is the APNS token.
///
/// ## Platform-specific
///
/// - **macOS**: https://developer.apple.com/documentation/appkit/nsapplicationdelegate/application(_:didregisterforremotenotificationswithdevicetoken:)?language=objc
/// - **iOS**: https://developer.apple.com/documentation/uikit/uiapplicationdelegate/application(_:didregisterforremotenotificationswithdevicetoken:)?language=objc
/// - **Other**: Unsupported.
PushRegistration(Vec<u8>),

/// Emitted when push token registration fails.
PushRegistrationError(String),
}

impl<T: Clone> Clone for Event<'static, T> {
Expand Down Expand Up @@ -170,6 +183,8 @@ impl<T: Clone> Clone for Event<'static, T> {
} => Reopen {
has_visible_windows: *has_visible_windows,
},
PushRegistration(token) => PushRegistration(token.clone()),
PushRegistrationError(error) => PushRegistrationError(error.clone()),
}
}
}
Expand All @@ -194,6 +209,8 @@ impl<'a, T> Event<'a, T> {
} => Ok(Reopen {
has_visible_windows,
}),
PushRegistration(token) => Ok(PushRegistration(token)),
PushRegistrationError(error) => Ok(PushRegistrationError(error)),
}
}

Expand All @@ -220,6 +237,8 @@ impl<'a, T> Event<'a, T> {
} => Some(Reopen {
has_visible_windows,
}),
PushRegistration(token) => Some(PushRegistration(token)),
PushRegistrationError(error) => Some(PushRegistrationError(error)),
}
}
}
Expand Down
78 changes: 77 additions & 1 deletion src/platform_impl/ios/view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -690,7 +690,83 @@ pub fn create_delegate_class() {
sel!(applicationWillTerminate:),
will_terminate as extern "C" fn(_, _, _),
);

decl.add_method(
sel!(application:didRegisterForRemoteNotificationsWithDeviceToken:),
did_register_for_apns as extern "C" fn(_, _, _, _),
);
decl.add_method(
sel!(application:didFailToRegisterForRemoteNotificationsWithError:),
did_fail_to_register_for_apns as extern "C" fn(_, _, _, _),
);
decl.register();
}
}

// application(_:didRegisterForRemoteNotificationsWithDeviceToken:)
extern "C" fn did_register_for_apns(_: &Object, _: Sel, _: id, token_data: id) {
trace!("Triggered `didRegisterForRemoteNotificationsWithDeviceToken`");
let token_bytes = unsafe {
if token_data.is_null() {
trace!("Token data is null; ignoring");
return;
}
let is_nsdata: bool = msg_send![token_data, isKindOfClass:class!(NSData)];
if !is_nsdata {
trace!("Token data is not an NSData object");
return;
}
let bytes: *const u8 = msg_send![token_data, bytes];
let length: usize = msg_send![token_data, length];
std::slice::from_raw_parts(bytes, length).to_vec()
};
did_register_push_token(token_bytes);
trace!("Completed `didRegisterForRemoteNotificationsWithDeviceToken`");
}

// application(_:didFailToRegisterForRemoteNotificationsWithError:)
extern "C" fn did_fail_to_register_for_apns(_: &Object, _: Sel, _: id, err: *mut Object) {
trace!("Triggered `didFailToRegisterForRemoteNotificationsWithError`");

let error_string = unsafe {
if err.is_null() {
"Unknown error (null error object)".to_string()
} else {
// Verify it's an NSError
let is_error: bool = msg_send![err, isKindOfClass:class!(NSError)];
if !is_error {
trace!("Invalid error object type for push registration failure");
return;
}

// Get the localizedDescription
let description: *mut Object = msg_send![err, localizedDescription];
if description.is_null() {
trace!("Error had no description");
return;
}

// Convert NSString to str
let utf8: *const u8 = msg_send![description, UTF8String];
let len: usize = msg_send![description, lengthOfBytesUsingEncoding:4];
let bytes = std::slice::from_raw_parts(utf8, len);
String::from_utf8_lossy(bytes).to_string()
}
};

did_fail_to_register_push_token(error_string);
trace!("Completed `didFailToRegisterForRemoteNotificationsWithError`");
}

fn did_register_push_token(token_data: Vec<u8>) {
unsafe {
app_state::handle_nonuser_event(EventWrapper::StaticEvent(Event::PushRegistration(
token_data,
)));
}
}

fn did_fail_to_register_push_token(err: String) {
unsafe {
app_state::handle_nonuser_event(EventWrapper::StaticEvent(Event::PushRegistrationError(err)));
}
}
65 changes: 65 additions & 0 deletions src/platform_impl/macos/app_delegate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use objc2::runtime::{
use objc2_foundation::{
NSArray, NSError, NSString, NSUserActivity, NSUserActivityTypeBrowsingWeb, NSURL,
};

use std::{
cell::{RefCell, RefMut},
ffi::{CStr, CString},
Expand Down Expand Up @@ -83,6 +84,14 @@ lazy_static! {
sel!(applicationSupportsSecureRestorableState:),
application_supports_secure_restorable_state as extern "C" fn(_, _, _) -> _,
);
decl.add_method(
sel!(application:didRegisterForRemoteNotificationsWithDeviceToken:),
did_register_for_apns as extern "C" fn(_, _, _, _),
);
decl.add_method(
sel!(application:didFailToRegisterForRemoteNotificationsWithError:),
did_fail_to_register_for_apns as extern "C" fn(_, _, _, _),
);
decl.add_ivar::<*mut c_void>(&CString::new(AUX_DELEGATE_STATE_NAME).unwrap());

AppDelegateClass(decl.register())
Expand Down Expand Up @@ -125,6 +134,7 @@ extern "C" fn dealloc(this: &Object, _: Sel) {

extern "C" fn did_finish_launching(this: &Object, _: Sel, _: id) {
trace!("Triggered `applicationDidFinishLaunching`");

AppState::launched(this);
trace!("Completed `applicationDidFinishLaunching`");
}
Expand Down Expand Up @@ -221,3 +231,58 @@ extern "C" fn application_supports_secure_restorable_state(_: &Object, _: Sel, _
trace!("Completed `applicationSupportsSecureRestorableState`");
YES
}

// application(_:didRegisterForRemoteNotificationsWithDeviceToken:)
extern "C" fn did_register_for_apns(_: &Object, _: Sel, _: id, token_data: id) {
trace!("Triggered `didRegisterForRemoteNotificationsWithDeviceToken`");
let token_bytes = unsafe {
if token_data.is_null() {
trace!("Token data is null; ignoring");
return;
}
let is_nsdata: bool = msg_send![token_data, isKindOfClass:class!(NSData)];
if !is_nsdata {
trace!("Token data is not an NSData object");
return;
}
let bytes: *const u8 = msg_send![token_data, bytes];
let length: usize = msg_send![token_data, length];
std::slice::from_raw_parts(bytes, length).to_vec()
};
AppState::did_register_push_token(token_bytes);
trace!("Completed `didRegisterForRemoteNotificationsWithDeviceToken`");
}

// application(_:didFailToRegisterForRemoteNotificationsWithError:)
extern "C" fn did_fail_to_register_for_apns(_: &Object, _: Sel, _: id, err: *mut Object) {
trace!("Triggered `didFailToRegisterForRemoteNotificationsWithError`");

let error_string = unsafe {
if err.is_null() {
"Unknown error (null error object)".to_string()
} else {
// Verify it's an NSError
let is_error: bool = msg_send![err, isKindOfClass:class!(NSError)];
if !is_error {
trace!("Invalid error object type for push registration failure");
return;
}

// Get the localizedDescription
let description: *mut Object = msg_send![err, localizedDescription];
if description.is_null() {
trace!("Error had no description");
return;
}

// Convert NSString to str
let utf8: *const u8 = msg_send![description, UTF8String];
let len: usize = msg_send![description, lengthOfBytesUsingEncoding:4];
let bytes = std::slice::from_raw_parts(utf8, len);
String::from_utf8_lossy(bytes).to_string()
}
};

AppState::did_fail_to_register_push_token(error_string);
trace!("Completed `didFailToRegisterForRemoteNotificationsWithError`");
}
10 changes: 10 additions & 0 deletions src/platform_impl/macos/app_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,16 @@ impl AppState {
(_, ControlFlow::Poll) => HANDLER.waker().start(),
}
}

pub fn did_register_push_token(token_data: Vec<u8>) {
HANDLER.handle_nonuser_event(EventWrapper::StaticEvent(Event::PushRegistration(
token_data,
)));
}

pub fn did_fail_to_register_push_token(err: String) {
HANDLER.handle_nonuser_event(EventWrapper::StaticEvent(Event::PushRegistrationError(err)));
}
}

/// A hack to make activation of multiple windows work when creating them before
Expand Down
Loading