tauri-apps / tauri-apps/plugins-workspace

[rfc] Notification API Refactor

Open
#2,134 10 comments 0 reactions 0 assignees View on GitHub
plugin: notification
Dominant language
Rust
Stars
1.8k
Forks
602
Avg merge
4d 14h
Merged PRs (30d)
9

Description

## Summary

Currently the Notification API has the following issues:
- `Permission` type alias has values of `"granted" | "denied" | "default"`. What is "default"? We should also have a permission that is "undetermined" for if we haven't asked for permission to show notifications (such as macOS, whereas this may default to "granted" on Windows)
- `isPermissionGranted` returns an optional bool in a promise. This should be instead the `Permission` type alias from above
- `sendNotification()` currently returns `void`; the developer does not know if the notification was successfully delivered or not

Open Questions:

- [ ] What about icon badges? Do we add those in the notification payload struct? https://github.com/tauri-apps/tauri/issues/4489

## Functions

- ✅: Considered and documented
- ⚠️: Open questions
- ⛔️: Currently an issue
- TBD: To be figured out

| Name | Windows | macOS | Linux | iOS | Android |
| --------------------- | ------- | ----- | ----- | --- | ------- |
| `checkPermission()` | | | | | |
| `requestPermission()` | | | | | |
| `sendNotification()` | | | | | |

### `checkPermission()`

**Parameters:** None

**Returns:** `Permission`

##### macOS Implementation

- Call [authorizationStatus](https://developer.apple.com/documentation/usernotifications/unnotificationsettings/1648391-authorizationstatus)
- Returns enum of [UNAuthorizationStatus](https://developer.apple.com/documentation/usernotifications/unauthorizationstatus)
- `notDetermined` = notDetermined
- `denied` = Denied
- `authorized`, `provisional`, `ephemeral` = Granted

### `requestPermission()`

Should run `checkPermission` first under the hood and then only ask permission if `notDetermined` is returned

**Parameters:** None

**Returns:** `Permission`

- First call `tauri::notification::isPermissionGranted()`, do the below if undetermined, otherwise return denied or authorised directly
- Call [requestAuthorization](https://developer.apple.com/documentation/usernotifications/unusernotificationcenter/1649527-requestauthorization)
- With `options` = [`[UNAuthorizationOptions]`](https://developer.apple.com/documentation/usernotifications/unauthorizationoptions) of `.badge`, `.sound`, `.alert`
- Returns `(bool, Error)`
- If `Error == nil bool == true && ` then permission is granted
- Else, log the error (note: it is theoretically possible for bool to return true but there to be an error or bool to return false and error to also be nil. In those cases, permission should be counted as denied)

### `sendNotification()`

If the notification wasn't able to be delivered then `Permission` should be returned. This function should NOT automatically request permission internally so that developers can react accordingly.

On iOS, notifications sent while the app is in the foreground are not shown by default. I'm not 100% sure on this behaviour on macOS but can test it on a Swift app if we need to down the road.

**Parameters:** `Notification`

**Returns:** `Result<(), Permission>`

#### macOS Implementation

- First check `requestPermission`
- Use [UNMutableNotificationContent](https://developer.apple.com/documentation/usernotifications/unmutablenotificationcontent) to build the notification
- Use [UNTimeIntervalNotificationTrigger](https://developer.apple.com/documentation/usernotifications/untimeintervalnotificationtrigger) to build the conditions/timing for the notification (`timeInterval` would be `0` for trigger now, repeats: false
- Construct a [UNNotificationRequest](https://developer.apple.com/documentation/usernotifications/scheduling_a_notification_locally_from_your_app) using a UUID, content from UNMutableNotificationContent above, and trigger from UNTimeIntervalNotificationTrigger above
- Send the notification request to the system with [UNUserNotificationCenter.add](https://developer.apple.com/documentation/usernotifications/unusernotificationcenter/1649508-add)
- passing the request created above
- Check the completion handler, if error is not nil then the notification was not sent successfully.

## Types

| Type | Name | Windows | macOS | Linux | iOS | Android |
| ------ | -------------- | ------- | ----- | ----- | --- | ------- |
| struct | `Notification` | | | | | |
| enum | `Permission` | | | | | |

### `Notification`

Type: Struct

| Field | Type | Description |
| ---------- | --------- | ----------- |
| identifier | `string` | |
| title | `string?` | |
| body | `string?` | |
| icon | `string?` | |

### `Permission`

Type: Enum

| Value | Description |
| ------------- | ----------------------------------------------------------------------------------------- |
| granted | Notification permissions have been allowed |
| denied | The user explicitely denied notifications, you need to ask the user to change accordingly |
| notDetermined | The OS hasn't been asked, you should ask permission |

## Additional context

From conversations with @betamos and @FabianLars, `notify-rust` does not currently have an API to check current notification permission. I'd recommend building this bit internally and keeping the rest of the system-interfacing logic inside `notify-rust` so we could "stabilise" the API. If we want to bring the logic in-house for notifications overall then we could do that later without introducing breaking changes to the API in theory.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.