diff --git a/README.md b/README.md
index e00317f..caed0fa 100644
--- a/README.md
+++ b/README.md
@@ -463,6 +463,21 @@ Registers the app for push notifications. On Android this retrieves the FCM devi
- Android: FCM device token
- Linux: UnifiedPush endpoint URL (the URL your backend POSTs payloads to)
+On iOS the APNs token is returned to the caller but is not also emitted through
+the generic Tauri event channel by default. Apps that intentionally handle the
+raw token in JavaScript can opt into the legacy `push-token` event in
+`tauri.conf.json`:
+
+```json
+{
+ "plugins": {
+ "notifications": {
+ "exposePushTokenEvents": true
+ }
+ }
+}
+```
+
### `listDistributors()` **(Linux / UnifiedPush only)**
Lists every running UnifiedPush distributor by its D-Bus bus name (e.g. `org.unifiedpush.Distributor.ntfy`). Returns an empty array when none is installed — that's the signal to ask the user to install one from .
diff --git a/ios/Sources/AppDelegateSwizzler.swift b/ios/Sources/AppDelegateSwizzler.swift
index 9a2d59f..d0087c1 100644
--- a/ios/Sources/AppDelegateSwizzler.swift
+++ b/ios/Sources/AppDelegateSwizzler.swift
@@ -77,7 +77,9 @@ final class PushForwarder: NSObject, UIApplicationDelegate {
didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
let hex = deviceToken.map { String(format: "%02x", $0) }.joined()
AppDelegateSwizzler.plugin?.handlePushTokenReceived(hex)
- try? AppDelegateSwizzler.plugin?.trigger("push-token", data: ["token": hex])
+ if AppDelegateSwizzler.plugin?.exposePushTokenEvents == true {
+ try? AppDelegateSwizzler.plugin?.trigger("push-token", data: ["token": hex])
+ }
AppDelegateSwizzler.callOriginalDidRegister(
self,
#selector(UIApplicationDelegate.application(_:didRegisterForRemoteNotificationsWithDeviceToken:)),
diff --git a/ios/Sources/NotificationPlugin.swift b/ios/Sources/NotificationPlugin.swift
index 4f494a3..0792613 100644
--- a/ios/Sources/NotificationPlugin.swift
+++ b/ios/Sources/NotificationPlugin.swift
@@ -163,11 +163,13 @@ struct SetPushMessageListenerActiveArgs: Decodable {
struct PluginConfig: Decodable {
let actionTypes: [ActionType]?
+ let exposePushTokenEvents: Bool?
}
class NotificationPlugin: Plugin {
let notificationHandler = NotificationHandler()
let notificationManager = NotificationManager()
+ var exposePushTokenEvents = false
#if ENABLE_PUSH_NOTIFICATIONS
// Completion handler for push token registration
@@ -185,9 +187,11 @@ class NotificationPlugin: Plugin {
public override func load(webview: WKWebView) {
super.load(webview: webview)
- if let config = try? parseConfig(PluginConfig.self),
- let actionTypes = config.actionTypes {
- makeCategories(actionTypes)
+ if let config = try? parseConfig(PluginConfig.self) {
+ if let actionTypes = config.actionTypes {
+ makeCategories(actionTypes)
+ }
+ exposePushTokenEvents = config.exposePushTokenEvents ?? false
}
#if ENABLE_PUSH_NOTIFICATIONS
diff --git a/ios/Tests/PluginTests/PluginTests.swift b/ios/Tests/PluginTests/PluginTests.swift
index 7db31cc..9c3d108 100644
--- a/ios/Tests/PluginTests/PluginTests.swift
+++ b/ios/Tests/PluginTests/PluginTests.swift
@@ -13,6 +13,16 @@ final class NotificationTests: XCTestCase {
XCTAssertEqual(config.actionTypes?.first?.id, "sable-message")
XCTAssertEqual(config.actionTypes?.first?.actions.first?.id, "sable-reply")
XCTAssertEqual(config.actionTypes?.first?.actions.first?.input, true)
+ XCTAssertNil(config.exposePushTokenEvents)
+ }
+
+ func testPluginConfigCanExplicitlyExposePushTokenEvents() throws {
+ let config = try JSONDecoder().decode(
+ PluginConfig.self,
+ from: Data(#"{"exposePushTokenEvents":true}"#.utf8)
+ )
+
+ XCTAssertEqual(config.exposePushTokenEvents, true)
}
func testSetPushMessageListenerActiveArgsDecodes() throws {
diff --git a/src/lib.rs b/src/lib.rs
index 80948df..8f87bde 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -21,6 +21,12 @@ pub struct PluginConfig {
/// `aps.category` is received before JavaScript initializes.
#[serde(default)]
pub action_types: Vec,
+ /// Emit the raw APNs registration token through the generic Tauri event
+ /// channel. Disabled by default because Rust callers already receive the
+ /// token from `register_for_push_notifications` and may keep it outside
+ /// the renderer trust boundary.
+ #[serde(default)]
+ pub expose_push_token_events: bool,
#[cfg(target_os = "windows")]
pub windows: WindowsConfig,
}