From 140d19209c97feabb111aaa9f24b288a4e643b15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20K=C3=B6the?= Date: Fri, 18 Sep 2026 14:16:45 +0200 Subject: [PATCH] docs: add DocC guide and expand API docs Adds a new DocC top-level article for NotificationManager with usage guidance and topic links, and updates README with instructions to build package documentation in Xcode. It also expands Swift doc comments across the public API to clarify parameters, return values, thrown errors, and behavior differences between throwing async and fire-and-forget overloads. --- README.md | 5 ++ .../NotificationManager.md | 84 ++++++++++++++++++ .../NotificationManager.swift | 87 +++++++++++++++++-- 3 files changed, 171 insertions(+), 5 deletions(-) create mode 100644 Sources/NotificationManager/NotificationManager.docc/NotificationManager.md diff --git a/README.md b/README.md index 1ec4561..3dae14f 100644 --- a/README.md +++ b/README.md @@ -204,6 +204,11 @@ if #available(iOS 16.0, macOS 13.0, visionOS 1.0, *) { } ``` +## Documentation + +Open the package in Xcode and choose **Product > Build Documentation** to browse +the complete API documentation. + ## Contributing Bug reports, feature requests, and pull requests are welcome through the GitHub repository. diff --git a/Sources/NotificationManager/NotificationManager.docc/NotificationManager.md b/Sources/NotificationManager/NotificationManager.docc/NotificationManager.md new file mode 100644 index 0000000..a88aabc --- /dev/null +++ b/Sources/NotificationManager/NotificationManager.docc/NotificationManager.md @@ -0,0 +1,84 @@ +# ``NotificationManager`` + +Manage local notifications with a small, async-first Swift API. + +NotificationManager wraps the system notification center and provides APIs for +requesting authorization, scheduling notifications, querying their state, and +removing them. The package does not deliver notifications itself; delivery is +handled by `UserNotifications`. + +## Overview + +Before scheduling local notifications, request authorization at a point where +the user understands why notifications are needed: + +```swift +import NotificationManager + +do { + let granted = try await NotificationManager.requestAuthorizationThrowing() + + if granted { + try await NotificationManager.scheduleNotification( + id: "task-reminder", + title: "Reminder", + body: "Your task is due.", + timeInterval: 60 + ) + } +} catch { + // Handle authorization or scheduling errors. +} +``` + +The throwing asynchronous APIs are recommended for new code because they make +validation and notification-center errors observable. Non-async +fire-and-forget overloads are available for compatibility, but handle errors by +printing them from an asynchronous task. + +## Topics + +### Authorization + +- ``NotificationManager/requestAuthorizationThrowing()`` +- ``NotificationManager/requestAuthorization()->_`` +- ``NotificationManager/requestAuthorization()->()`` +- ``NotificationManager/requestAuthorization(for:)`` +- ``NotificationManager/getAuthorizationStatus()`` + +### Scheduling + +- ``NotificationManager/scheduleNotification(id:title:body:timeInterval:)-1ha42`` +- ``NotificationManager/scheduleNotification(id:title:body:timeInterval:)-1ipdm`` +- ``NotificationManager/scheduleNotification(id:title:body:triggerDate:)-8oxhh`` +- ``NotificationManager/scheduleNotification(id:title:body:triggerDate:)-2zqr2`` +- ``NotificationManager/scheduleRepeatNotification(id:title:body:timeInterval:)-9tois`` +- ``NotificationManager/scheduleRepeatNotification(id:title:body:timeInterval:)-2q2t7`` + +### Querying Notifications + +- ``NotificationManager/getPendingNotificationRequests()`` +- ``NotificationManager/getPendingNotificationRequestIDs()`` +- ``NotificationManager/getDeliveredNotifications()`` +- ``NotificationManager/getDeliveredNotificationIDs()`` + +### Updating and Removing Notifications + +- ``NotificationManager/replaceNotificationRequestFromId(id:newTitle:newBody:newDate:)`` +- ``NotificationManager/removePendingNotificationRequests(ids:)`` +- ``NotificationManager/removeAllPendingNotificationRequests()`` +- ``NotificationManager/removeDeliveredNotifications(ids:)`` +- ``NotificationManager/removeAllDeliveredNotificationRequests()`` + +### Badge + +- ``NotificationManager/setBadge(badge:)-5ir8o`` +- ``NotificationManager/setBadge(badge:)-7tvfe`` +- ``NotificationManager/resetBadge()-yomw`` +- ``NotificationManager/resetBadge()-7ejrf`` + +Badge APIs are available on iOS 16+, macOS 13+, and visionOS 1+. + +### Errors + +- ``NotificationManagerError`` diff --git a/Sources/NotificationManager/NotificationManager.swift b/Sources/NotificationManager/NotificationManager.swift index 07d68bd..a561644 100644 --- a/Sources/NotificationManager/NotificationManager.swift +++ b/Sources/NotificationManager/NotificationManager.swift @@ -64,6 +64,10 @@ public struct NotificationManager { // MARK: Authorization /// Requests authorization for alerts, sounds, and badges. + /// + /// This fire-and-forget overload prints authorization errors instead of + /// returning them to the caller. Prefer the throwing asynchronous overload + /// when the result or error needs to be handled explicitly. public static func requestAuthorization() { Task { do { @@ -76,6 +80,8 @@ public struct NotificationManager { /// Requests authorization for alerts, sounds, and badges. /// - Returns: Whether the user granted authorization. + /// - Note: Authorization errors are printed and result in `false`. Use + /// ``requestAuthorizationThrowing()`` to propagate errors. public static func requestAuthorization() async -> Bool { do { return try await center.requestAuthorization(options: defaultAuthorizationOptions) @@ -87,12 +93,14 @@ public struct NotificationManager { /// Requests authorization for alerts, sounds, and badges. /// - Returns: Whether the user granted authorization. + /// - Throws: An error from the system notification center. public static func requestAuthorizationThrowing() async throws -> Bool { try await center.requestAuthorization(options: defaultAuthorizationOptions) } /// Requests authorization for alerts, sounds, and badges. /// - Returns: Whether the user granted authorization. + /// - Throws: An error from the system notification center. @available(*, deprecated, renamed: "requestAuthorizationThrowing()") public static func requestAuthorizationThrowable() async throws -> Bool { try await requestAuthorizationThrowing() @@ -101,12 +109,14 @@ public struct NotificationManager { /// Requests authorization for the supplied options. /// - Parameter options: The notification authorization options to request. /// - Returns: Whether the user granted authorization. + /// - Throws: An error from the system notification center. @discardableResult public static func requestAuthorization(for options: UNAuthorizationOptions) async throws -> Bool { try await center.requestAuthorization(options: options) } /// Retrieves the current notification authorization status. + /// - Returns: The current authorization status reported by the system. public static func getAuthorizationStatus() async -> UNAuthorizationStatus { await center.authorizationStatus() } @@ -114,6 +124,13 @@ public struct NotificationManager { // MARK: Schedule /// Schedules a notification for a future date and reports scheduling errors. + /// - Parameters: + /// - id: A stable identifier for the notification request. + /// - title: The title shown in the notification. + /// - body: The body shown in the notification. + /// - triggerDate: The future date at which the notification should be delivered. + /// - Throws: ``NotificationManagerError/triggerDateMustBeInFuture`` or an + /// error from the system notification center. public static func scheduleNotification( id: String, title: String, @@ -134,7 +151,14 @@ public struct NotificationManager { ) } - /// Schedules a notification for a future date. + /// Schedules a notification for a future date without waiting for completion. + /// - Parameters: + /// - id: A stable identifier for the notification request. + /// - title: The title shown in the notification. + /// - body: The body shown in the notification. + /// - triggerDate: The future date at which the notification should be delivered. + /// - Note: Validation and scheduling errors are printed instead of returned + /// to the caller. public static func scheduleNotification(id: String, title: String, body: String, triggerDate: Date) { Task { do { @@ -146,6 +170,13 @@ public struct NotificationManager { } /// Schedules a notification after a positive number of seconds and reports scheduling errors. + /// - Parameters: + /// - id: A stable identifier for the notification request. + /// - title: The title shown in the notification. + /// - body: The body shown in the notification. + /// - timeInterval: The delay before delivery, in seconds. + /// - Throws: ``NotificationManagerError/invalidTimeInterval`` or an error + /// from the system notification center. public static func scheduleNotification( id: String, title: String, @@ -161,7 +192,14 @@ public struct NotificationManager { ) } - /// Schedules a notification after a positive number of seconds. + /// Schedules a notification after a positive number of seconds without waiting for completion. + /// - Parameters: + /// - id: A stable identifier for the notification request. + /// - title: The title shown in the notification. + /// - body: The body shown in the notification. + /// - timeInterval: The delay before delivery, in seconds. + /// - Note: Validation and scheduling errors are printed instead of returned + /// to the caller. public static func scheduleNotification(id: String, title: String, body: String, timeInterval: Int) { Task { do { @@ -173,6 +211,15 @@ public struct NotificationManager { } /// Schedules a repeating notification and reports scheduling errors. + /// - Parameters: + /// - id: A stable identifier for the notification request. + /// - title: The title shown in the notification. + /// - body: The body shown in the notification. + /// - timeInterval: The interval between deliveries, in seconds. It must + /// be at least 60 seconds. + /// - Throws: ``NotificationManagerError/invalidTimeInterval``, + /// ``NotificationManagerError/repeatingTimeIntervalTooShort``, or an + /// error from the system notification center. public static func scheduleRepeatNotification( id: String, title: String, @@ -188,7 +235,15 @@ public struct NotificationManager { ) } - /// Schedules a repeating notification. Repeating intervals must be at least 60 seconds. + /// Schedules a repeating notification without waiting for completion. + /// - Parameters: + /// - id: A stable identifier for the notification request. + /// - title: The title shown in the notification. + /// - body: The body shown in the notification. + /// - timeInterval: The interval between deliveries, in seconds. It must + /// be at least 60 seconds. + /// - Note: Validation and scheduling errors are printed instead of returned + /// to the caller. public static func scheduleRepeatNotification(id: String, title: String, body: String, timeInterval: Int) { Task { do { @@ -231,27 +286,32 @@ public struct NotificationManager { // MARK: Fetch /// Fetches all pending local notification requests. + /// - Returns: The requests that are scheduled and awaiting delivery. public static func getPendingNotificationRequests() async -> [UNNotificationRequest] { await center.pendingNotificationRequests() } /// Fetches the identifiers of all pending local notification requests. + /// - Returns: The identifiers of requests that are awaiting delivery. public static func getPendingNotificationRequestIDs() async -> [String] { await center.pendingNotificationRequests().map(\.identifier) } /// Fetches the identifiers of all pending local notification requests. + /// - Returns: The identifiers of requests that are awaiting delivery. @available(*, deprecated, renamed: "getPendingNotificationRequestIDs()") public static func getPendingNotificationRequestsIds() async -> [String] { await getPendingNotificationRequestIDs() } /// Fetches all delivered local notifications. + /// - Returns: The notifications that the system has delivered to the app. public static func getDeliveredNotifications() async -> [UNNotification] { await center.deliveredNotifications() } /// Fetches the identifiers of all delivered local notifications. + /// - Returns: The identifiers of notifications delivered by the system. public static func getDeliveredNotificationIDs() async -> [String] { await center.deliveredNotifications().map(\.request.identifier) } @@ -259,6 +319,13 @@ public struct NotificationManager { // MARK: Update /// Replaces an existing pending notification. If the identifier does not exist, nothing happens. + /// - Parameters: + /// - id: The identifier of the pending request to replace. + /// - newTitle: The replacement notification title. + /// - newBody: The replacement notification body. + /// - newDate: The future delivery date for the replacement request. + /// - Throws: ``NotificationManagerError/triggerDateMustBeInFuture`` or an + /// error from the system notification center. public static func replaceNotificationRequestFromId( id: String, newTitle: String, @@ -298,11 +365,13 @@ public struct NotificationManager { } /// Removes pending notifications with the supplied identifiers. + /// - Parameter ids: The identifiers of pending requests to remove. public static func removePendingNotificationRequests(ids: [String]) { center.removePendingNotificationRequests(withIdentifiers: ids) } /// Removes delivered notifications with the supplied identifiers. + /// - Parameter ids: The identifiers of delivered notifications to remove. public static func removeDeliveredNotifications(ids: [String]) { center.removeDeliveredNotifications(withIdentifiers: ids) } @@ -310,12 +379,18 @@ public struct NotificationManager { // MARK: Badge /// Updates the application's badge count. + /// - Parameter badge: The number to display on the app icon. Pass zero to + /// remove the badge. + /// - Throws: An error from the system notification center. @available(iOS 16.0, macOS 13.0, visionOS 1.0, *) public static func setBadge(badge: Int) async throws { try await center.setBadgeCount(badge) } - /// Updates the application's badge count. + /// Updates the application's badge count without waiting for completion. + /// - Parameter badge: The number to display on the app icon. Pass zero to + /// remove the badge. + /// - Note: Errors are printed instead of returned to the caller. @available(iOS 16.0, macOS 13.0, visionOS 1.0, *) public static func setBadge(badge: Int) { Task { @@ -328,12 +403,14 @@ public struct NotificationManager { } /// Resets the application's badge count. + /// - Throws: An error from the system notification center. @available(iOS 16.0, macOS 13.0, visionOS 1.0, *) public static func resetBadge() async throws { try await center.setBadgeCount(0) } - /// Resets the application's badge count. + /// Resets the application's badge count without waiting for completion. + /// - Note: Errors are printed instead of returned to the caller. @available(iOS 16.0, macOS 13.0, visionOS 1.0, *) public static func resetBadge() { Task {