From b159c70e414b7719fbd29dfc39ccc2db1d800c95 Mon Sep 17 00:00:00 2001 From: Dimitri Bouniol Date: Wed, 25 Dec 2024 13:52:00 -0800 Subject: [PATCH 1/4] Added testing documentation --- README.md | 40 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 7b8703d..cb51adf 100644 --- a/README.md +++ b/README.md @@ -373,8 +373,46 @@ Your service worker will receive this message, decode it, and present it to the ### Testing -The `WebPushTesting` module can be used to obtain a mocked `WebPushManager` instance that allows you to capture all messages that are sent out, or throw your own errors to validate your code functions appropriately. Only import `WebPushTesting` in your testing targets. +The `WebPushTesting` module can be used to obtain a mocked `WebPushManager` instance that allows you to capture all messages that are sent out, or throw your own errors to validate your code functions appropriately. +> [!IMPORTANT] +> Only import `WebPushTesting` in your testing targets. + +```swift +import Testing +import WebPushTesting + +@Test func sendSuccessfulNotifications() async throws { + try await confirmation { requestWasMade in + let mockedManager = WebPushManager.makeMockedManager { message, subscriber, topic, expiration, urgency in + #expect(message.string == "hello") + #expect(subscriber.endpoint.absoluteString == "https://example.com/expectedSubscriber") + #expect(subscriber.vapidKeyID == .mockedKeyID1) + #expect(topic == nil) + #expect(expiration == .recommendedMaximum) + #expect(urgency == .high) + requestWasMade() + } + + let myController = MyController(pushManager: mockedManager) + try await myController.sendNotifications() + } +} + +@Test func catchBadSubscriptions() async throws { + /// Mocked managers accept multiple handlers, and will cycle through them each time a push message is sent: + let mockedManager = WebPushManager.makeMockedManager(messageHandlers: + { _, _, _, _, _ in throw BadSubscriberError() }, + { _, _, _, _, _ in }, + { _, _, _, _, _ in throw BadSubscriberError() }, + ) + + let myController = MyController(pushManager: mockedManager) + #expect(myController.subscribers.count == 3) + try await myController.sendNotifications() + #expect(myController.subscribers.count == 1) +} +``` ## Specifications From 67dfbc7e21c2ee3e69c8b3aa35f6374fdff0745a Mon Sep 17 00:00:00 2001 From: Dimitri Bouniol Date: Wed, 25 Dec 2024 14:26:13 -0800 Subject: [PATCH 2/4] Added more references to read me --- README.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/README.md b/README.md index cb51adf..86ce6bc 100644 --- a/README.md +++ b/README.md @@ -416,6 +416,7 @@ import WebPushTesting ## Specifications +- [RFC 6454 The Web Origin Concept](https://datatracker.ietf.org/doc/html/rfc6454) - [RFC 7515 JSON Web Signature (JWS)](https://datatracker.ietf.org/doc/html/rfc7515) - [RFC 7519 JSON Web Token (JWT)](https://datatracker.ietf.org/doc/html/rfc7519) - [RFC 8030 Generic Event Delivery Using HTTP Push](https://datatracker.ietf.org/doc/html/rfc8030) @@ -423,8 +424,22 @@ import WebPushTesting - [RFC 8291 Message Encryption for Web Push](https://datatracker.ietf.org/doc/html/rfc8291) - [RFC 8292 Voluntary Application Server Identification (VAPID) for Web Push](https://datatracker.ietf.org/doc/html/rfc8292) + - [Push API Working Draft](https://www.w3.org/TR/push-api/) + +## Other Resources + +- [Apple Developer — Sending web push notifications in web apps and browsers](https://developer.apple.com/documentation/usernotifications/sending-web-push-notifications-in-web-apps-and-browsers) +- [WWDC22 — Meet Web Push for Safari](https://developer.apple.com/videos/play/wwdc2022/10098/) +- [WebKit — Meet Web Push](https://webkit.org/blog/12945/meet-web-push/) +- [WebKit — Web Push for Web Apps on iOS and iPadOS](https://webkit.org/blog/13878/web-push-for-web-apps-on-ios-and-ipados/) +- [MDN — Push API](https://developer.mozilla.org/en-US/docs/Web/API/Push_API) +- [MDN — Service Worker API](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API) +- [web.dev — The Web Push Protocol](https://web.dev/articles/push-notifications-web-push-protocol) +- [Sample Code — ServiceWorker Cookbook](https://github.com/mdn/serviceworker-cookbook/tree/master/push-simple) +- [Web Push: Data Encryption Test Page](https://mozilla-services.github.io/WebPushDataTestPage/) + ## Contributing Contribution is welcome! Please take a look at the issues already available, or start a new discussion to propose a new feature. Although guarantees can't be made regarding feature requests, PRs that fit within the goals of the project and that have been discussed beforehand are more than welcome! From 5b37d65b5072c6a6ec522dc5fb7a5b7fed480398 Mon Sep 17 00:00:00 2001 From: Dimitri Bouniol Date: Wed, 25 Dec 2024 14:28:37 -0800 Subject: [PATCH 3/4] Updated RFC links to be easier to read --- README.md | 14 +++++----- Sources/WebPush/Errors/PushServiceError.swift | 4 +-- Sources/WebPush/Helpers/URL+Origin.swift | 4 +-- Sources/WebPush/Subscriber.swift | 2 +- Sources/WebPush/Topic.swift | 2 +- Sources/WebPush/VAPID/VAPID.swift | 2 +- .../WebPush/VAPID/VAPIDConfiguration.swift | 2 +- Sources/WebPush/VAPID/VAPIDKey.swift | 2 +- Sources/WebPush/VAPID/VAPIDToken.swift | 26 +++++++++---------- Sources/WebPush/WebPushManager.swift | 8 +++--- 10 files changed, 33 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index 86ce6bc..7b89080 100644 --- a/README.md +++ b/README.md @@ -416,13 +416,13 @@ import WebPushTesting ## Specifications -- [RFC 6454 The Web Origin Concept](https://datatracker.ietf.org/doc/html/rfc6454) -- [RFC 7515 JSON Web Signature (JWS)](https://datatracker.ietf.org/doc/html/rfc7515) -- [RFC 7519 JSON Web Token (JWT)](https://datatracker.ietf.org/doc/html/rfc7519) -- [RFC 8030 Generic Event Delivery Using HTTP Push](https://datatracker.ietf.org/doc/html/rfc8030) -- [RFC 8188 Encrypted Content-Encoding for HTTP](https://datatracker.ietf.org/doc/html/rfc8188) -- [RFC 8291 Message Encryption for Web Push](https://datatracker.ietf.org/doc/html/rfc8291) -- [RFC 8292 Voluntary Application Server Identification (VAPID) for Web Push](https://datatracker.ietf.org/doc/html/rfc8292) +- [RFC 6454 — The Web Origin Concept](https://datatracker.ietf.org/doc/html/rfc6454) +- [RFC 7515 — JSON Web Signature (JWS)](https://datatracker.ietf.org/doc/html/rfc7515) +- [RFC 7519 — JSON Web Token (JWT)](https://datatracker.ietf.org/doc/html/rfc7519) +- [RFC 8030 — Generic Event Delivery Using HTTP Push](https://datatracker.ietf.org/doc/html/rfc8030) +- [RFC 8188 — Encrypted Content-Encoding for HTTP](https://datatracker.ietf.org/doc/html/rfc8188) +- [RFC 8291 — Message Encryption for Web Push](https://datatracker.ietf.org/doc/html/rfc8291) +- [RFC 8292 — Voluntary Application Server Identification (VAPID) for Web Push](https://datatracker.ietf.org/doc/html/rfc8292) - [Push API Working Draft](https://www.w3.org/TR/push-api/) diff --git a/Sources/WebPush/Errors/PushServiceError.swift b/Sources/WebPush/Errors/PushServiceError.swift index e6b76f8..a4fda6b 100644 --- a/Sources/WebPush/Errors/PushServiceError.swift +++ b/Sources/WebPush/Errors/PushServiceError.swift @@ -11,8 +11,8 @@ import Foundation /// An unknown Push Service error was encountered. /// -/// - SeeAlso: [RFC 8030 Generic Event Delivery Using HTTP Push](https://datatracker.ietf.org/doc/html/rfc8030) -/// - SeeAlso: [RFC 8292 Voluntary Application Server Identification (VAPID) for Web Push](https://datatracker.ietf.org/doc/html/rfc8292) +/// - SeeAlso: [RFC 8030 — Generic Event Delivery Using HTTP Push](https://datatracker.ietf.org/doc/html/rfc8030) +/// - SeeAlso: [RFC 8292 — Voluntary Application Server Identification (VAPID) for Web Push](https://datatracker.ietf.org/doc/html/rfc8292) /// - SeeAlso: [Sending web push notifications in web apps and browsers — Review responses for push notification errors](https://developer.apple.com/documentation/usernotifications/sending-web-push-notifications-in-web-apps-and-browsers#Review-responses-for-push-notification-errors) public struct PushServiceError: LocalizedError, Sendable { /// The HTTP response that was returned from the push service.. diff --git a/Sources/WebPush/Helpers/URL+Origin.swift b/Sources/WebPush/Helpers/URL+Origin.swift index 612c541..3a6e645 100644 --- a/Sources/WebPush/Helpers/URL+Origin.swift +++ b/Sources/WebPush/Helpers/URL+Origin.swift @@ -13,8 +13,8 @@ extension URL { /// /// This implementation is similar to the [WHATWG Standard](https://url.spec.whatwg.org/#concept-url-origin), except that it uses the unicode form of the host, and is limited to HTTP and HTTPS schemas. /// - /// - SeeAlso: [RFC 8292 Voluntary Application Server Identification (VAPID) for Web Push §2. Application Server Self-Identification](https://datatracker.ietf.org/doc/html/rfc8292#section-2) - /// - SeeAlso: [RFC6454 The Web Origin Concept §6.1. Unicode Serialization of an Origin](https://datatracker.ietf.org/doc/html/rfc6454#section-6.1) + /// - SeeAlso: [RFC 8292 — Voluntary Application Server Identification (VAPID) for Web Push §2. Application Server Self-Identification](https://datatracker.ietf.org/doc/html/rfc8292#section-2) + /// - SeeAlso: [RFC 6454 — The Web Origin Concept §6.1. Unicode Serialization of an Origin](https://datatracker.ietf.org/doc/html/rfc6454#section-6.1) var origin: String { /// Note that we need the unicode variant, which only URLComponents provides. let components = URLComponents(url: self, resolvingAgainstBaseURL: true) diff --git a/Sources/WebPush/Subscriber.swift b/Sources/WebPush/Subscriber.swift index 2a6fdc5..8844608 100644 --- a/Sources/WebPush/Subscriber.swift +++ b/Sources/WebPush/Subscriber.swift @@ -31,7 +31,7 @@ public protocol SubscriberProtocol: Sendable { /// The set of cryptographic secrets shared by the browser (is. user agent) along with a subscription. /// -/// - SeeAlso: [RFC 8291 Message Encryption for Web Push §2.1. Key and Secret Distribution](https://datatracker.ietf.org/doc/html/rfc8291#section-2.1) +/// - SeeAlso: [RFC 8291 — Message Encryption for Web Push §2.1. Key and Secret Distribution](https://datatracker.ietf.org/doc/html/rfc8291#section-2.1) public struct UserAgentKeyMaterial: Sendable { /// The underlying type of an authentication secret. public typealias Salt = Data diff --git a/Sources/WebPush/Topic.swift b/Sources/WebPush/Topic.swift index c8991bc..8ffc959 100644 --- a/Sources/WebPush/Topic.swift +++ b/Sources/WebPush/Topic.swift @@ -15,7 +15,7 @@ import Foundation /// /// - Important: Since topics are sent in the clear to push services, they must be securely hashed. You must use a stable random value for this, such as the subscriber's ``UserAgentKeyMaterial/authenticationSecret``. This is fine for most applications, though you may wish to use a different key if your application requires it. /// -/// - SeeAlso: [RFC 8030 Generic Event Delivery Using HTTP §5.4. Replacing Push Messages](https://datatracker.ietf.org/doc/html/rfc8030#section-5.4) +/// - SeeAlso: [RFC 8030 — Generic Event Delivery Using HTTP §5.4. Replacing Push Messages](https://datatracker.ietf.org/doc/html/rfc8030#section-5.4) public struct Topic: Hashable, Sendable, CustomStringConvertible { /// The topic value to use. public let topic: String diff --git a/Sources/WebPush/VAPID/VAPID.swift b/Sources/WebPush/VAPID/VAPID.swift index b361d4f..47a7ed2 100644 --- a/Sources/WebPush/VAPID/VAPID.swift +++ b/Sources/WebPush/VAPID/VAPID.swift @@ -13,5 +13,5 @@ public typealias VoluntaryApplicationServerIdentification = VAPID /// A set of types for Voluntary Application Server Identification, also known as VAPID. /// -/// - SeeAlso: [RFC 8292 Voluntary Application Server Identification (VAPID) for Web Push](https://datatracker.ietf.org/doc/html/rfc8292) +/// - SeeAlso: [RFC 8292 — Voluntary Application Server Identification (VAPID) for Web Push](https://datatracker.ietf.org/doc/html/rfc8292) public enum VAPID: Sendable {} diff --git a/Sources/WebPush/VAPID/VAPIDConfiguration.swift b/Sources/WebPush/VAPID/VAPIDConfiguration.swift index 9cda94b..fa77ff1 100644 --- a/Sources/WebPush/VAPID/VAPIDConfiguration.swift +++ b/Sources/WebPush/VAPID/VAPIDConfiguration.swift @@ -214,7 +214,7 @@ extension VAPID.Configuration { /// This allows administrators of push services to contact you should an issue arise with your application server. /// /// - Note: Although the specification notes that this field is optional, some push services may refuse connection from serers without contact information. - /// - SeeAlso: [RFC 8292 Voluntary Application Server Identification (VAPID) for Web Push §2.1. Application Server Contact Information](https://datatracker.ietf.org/doc/html/rfc8292#section-2.1) + /// - SeeAlso: [RFC 8292 — Voluntary Application Server Identification (VAPID) for Web Push §2.1. Application Server Contact Information](https://datatracker.ietf.org/doc/html/rfc8292#section-2.1) public enum ContactInformation: Hashable, Codable, Sendable { /// A URL-based contact method, such as a support page on your website. case url(URL) diff --git a/Sources/WebPush/VAPID/VAPIDKey.swift b/Sources/WebPush/VAPID/VAPIDKey.swift index 3ca349e..29a9f3e 100644 --- a/Sources/WebPush/VAPID/VAPIDKey.swift +++ b/Sources/WebPush/VAPID/VAPIDKey.swift @@ -98,7 +98,7 @@ extension VAPID.Key: Identifiable { /// The public key component in a format suitable for user agents to consume. /// /// - SeeAlso: [Push API Working Draft §7.2. `PushSubscriptionOptions` Interface](https://www.w3.org/TR/push-api/#dom-pushsubscriptionoptions-applicationserverkey) - /// - SeeAlso: [RFC 8292 Voluntary Application Server Identification (VAPID) for Web Push §3.2. Public Key Parameter ("k")](https://datatracker.ietf.org/doc/html/rfc8292#section-3.2) + /// - SeeAlso: [RFC 8292 — Voluntary Application Server Identification (VAPID) for Web Push §3.2. Public Key Parameter ("k")](https://datatracker.ietf.org/doc/html/rfc8292#section-3.2) public var id: ID { ID(privateKey.publicKey.x963Representation.base64URLEncodedString()) } diff --git a/Sources/WebPush/VAPID/VAPIDToken.swift b/Sources/WebPush/VAPID/VAPIDToken.swift index 460e702..430177b 100644 --- a/Sources/WebPush/VAPID/VAPIDToken.swift +++ b/Sources/WebPush/VAPID/VAPIDToken.swift @@ -12,9 +12,9 @@ import Foundation extension VAPID { /// An internal representation the token and authorization headers used self-identification. /// - /// - SeeAlso: [RFC 8292 Voluntary Application Server Identification (VAPID) for Web Push §2. Application Server Self-Identification](https://datatracker.ietf.org/doc/html/rfc8292#section-2) - /// - SeeAlso: [RFC 7515 JSON Web Signature (JWS)](https://datatracker.ietf.org/doc/html/rfc7515) - ///- SeeAlso: [RFC 7519 JSON Web Token (JWT)](https://datatracker.ietf.org/doc/html/rfc7519) + /// - SeeAlso: [RFC 8292 — Voluntary Application Server Identification (VAPID) for Web Push §2. Application Server Self-Identification](https://datatracker.ietf.org/doc/html/rfc8292#section-2) + /// - SeeAlso: [RFC 7515 — JSON Web Signature (JWS)](https://datatracker.ietf.org/doc/html/rfc7515) + ///- SeeAlso: [RFC 7519 — JSON Web Token (JWT)](https://datatracker.ietf.org/doc/html/rfc7519) struct Token: Hashable, Codable, Sendable { /// The coding keys used to encode the token. enum CodingKeys: String, CodingKey { @@ -26,25 +26,25 @@ extension VAPID { /// The audience claim, which encodes the origin of the ``Subscriber/endpoint`` /// /// - SeeAlso: ``/Foundation/URL/origin`` - /// - SeeAlso: [RFC 7519 JSON Web Token (JWT) §4.1.3. "aud" (Audience) Claim](https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.3) - /// - SeeAlso: [RFC 8292 Voluntary Application Server Identification (VAPID) for Web Push §2. Application Server Self-Identification](https://datatracker.ietf.org/doc/html/rfc8292#section-2) + /// - SeeAlso: [RFC 7519 — JSON Web Token (JWT) §4.1.3. "aud" (Audience) Claim](https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.3) + /// - SeeAlso: [RFC 8292 — Voluntary Application Server Identification (VAPID) for Web Push §2. Application Server Self-Identification](https://datatracker.ietf.org/doc/html/rfc8292#section-2) var audience: String /// The subject claim, which encodes contact information for the application server. /// - /// - SeeAlso: [RFC 7519 JSON Web Token (JWT) §4.1.2. "sub" (Subject) Claim](https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.2) - /// - SeeAlso: [RFC 8292 Voluntary Application Server Identification (VAPID) for Web Push §2.1. Application Server Contact Information](https://datatracker.ietf.org/doc/html/rfc8292#section-2.1) + /// - SeeAlso: [RFC 7519 — JSON Web Token (JWT) §4.1.2. "sub" (Subject) Claim](https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.2) + /// - SeeAlso: [RFC 8292 — Voluntary Application Server Identification (VAPID) for Web Push §2.1. Application Server Contact Information](https://datatracker.ietf.org/doc/html/rfc8292#section-2.1) var subject: Configuration.ContactInformation /// The expiry claim, which encodes the number of seconds after 1970/01/01 when the token expires. /// - /// - SeeAlso: [RFC 7519 JSON Web Token (JWT) §4.1.4. "exp" (Expiration Time) Claim](https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.4) - /// - SeeAlso: [RFC 8292 Voluntary Application Server Identification (VAPID) for Web Push §2. Application Server Self-Identification](https://datatracker.ietf.org/doc/html/rfc8292#section-2) + /// - SeeAlso: [RFC 7519 — JSON Web Token (JWT) §4.1.4. "exp" (Expiration Time) Claim](https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.4) + /// - SeeAlso: [RFC 8292 — Voluntary Application Server Identification (VAPID) for Web Push §2. Application Server Self-Identification](https://datatracker.ietf.org/doc/html/rfc8292#section-2) var expiration: Int /// The standard header including the type and algorithm. /// - /// - SeeAlso: [RFC 8292 Voluntary Application Server Identification (VAPID) for Web Push §2. Application Server Self-Identification](https://datatracker.ietf.org/doc/html/rfc8292#section-2) + /// - SeeAlso: [RFC 8292 — Voluntary Application Server Identification (VAPID) for Web Push §2. Application Server Self-Identification](https://datatracker.ietf.org/doc/html/rfc8292#section-2) static let jwtHeader = Array(#"{"typ":"JWT","alg":"ES256"}"#.utf8).base64URLEncodedString() /// Initialize a token with the specified claims. @@ -93,7 +93,7 @@ extension VAPID { self = token } - /// - SeeAlso: [RFC 7515 JSON Web Signature (JWS) §3. JSON Web Signature (JWS) Overview](https://datatracker.ietf.org/doc/html/rfc7515#section-3) + /// - SeeAlso: [RFC 7515 — JSON Web Signature (JWS) §3. JSON Web Signature (JWS) Overview](https://datatracker.ietf.org/doc/html/rfc7515#section-3) func generateJWT(signedBy signingKey: some VAPIDKeyProtocol) throws -> String { let header = Self.jwtHeader @@ -108,7 +108,7 @@ extension VAPID { /// Generate an `Authorization` header. /// - /// - SeeAlso: [RFC 8292 Voluntary Application Server Identification (VAPID) for Web Push §3. VAPID Authentication Scheme](https://datatracker.ietf.org/doc/html/rfc8292#section-3) + /// - SeeAlso: [RFC 8292 — Voluntary Application Server Identification (VAPID) for Web Push §3. VAPID Authentication Scheme](https://datatracker.ietf.org/doc/html/rfc8292#section-3) func generateAuthorization(signedBy signingKey: some VAPIDKeyProtocol) throws -> String { let token = try generateJWT(signedBy: signingKey) let key = signingKey.id @@ -123,6 +123,6 @@ protocol VAPIDKeyProtocol: Identifiable, Sendable { associatedtype Signature: ContiguousBytes /// Returns a JWS signature for the message. - /// - SeeAlso: [RFC 7515 JSON Web Signature (JWS)](https://datatracker.ietf.org/doc/html/rfc7515) + /// - SeeAlso: [RFC 7515 — JSON Web Signature (JWS)](https://datatracker.ietf.org/doc/html/rfc7515) func signature(for message: some DataProtocol) throws -> Signature } diff --git a/Sources/WebPush/WebPushManager.swift b/Sources/WebPush/WebPushManager.swift index e7c327b..cb0ada1 100644 --- a/Sources/WebPush/WebPushManager.swift +++ b/Sources/WebPush/WebPushManager.swift @@ -637,7 +637,7 @@ public actor WebPushManager: Sendable { .withUnsafeBytes(AES.GCM.Nonce.init(data:)) /// Encrypt the padded payload into a single record. - /// - SeeAlso: [RFC 8188 Encrypted Content-Encoding for HTTP](https://datatracker.ietf.org/doc/html/rfc8188) + /// - SeeAlso: [RFC 8188 — Encrypted Content-Encoding for HTTP](https://datatracker.ietf.org/doc/html/rfc8188) let encryptedRecord = try AES.GCM.seal(paddedPayload, using: contentEncryptionKey, nonce: nonce) /// Attach the header with our public key and salt, along with the authentication tag. @@ -784,7 +784,7 @@ extension WebPushManager: Service { extension WebPushManager { /// A duration in seconds used to express when push messages will expire. /// - /// - SeeAlso: [RFC 8030 Generic Event Delivery Using HTTP §5.2. Push Message Time-To-Live](https://datatracker.ietf.org/doc/html/rfc8030#section-5.2) + /// - SeeAlso: [RFC 8030 — Generic Event Delivery Using HTTP §5.2. Push Message Time-To-Live](https://datatracker.ietf.org/doc/html/rfc8030#section-5.2) public struct Expiration: Hashable, Comparable, Codable, ExpressibleByIntegerLiteral, AdditiveArithmetic, Sendable { /// The number of seconds represented by this expiration. public let seconds: Int @@ -800,7 +800,7 @@ extension WebPushManager { /// /// If the user agent is unavailable, a push message with a zero TTL expires and is never delivered. /// - /// - SeeAlso: [RFC 8030 Generic Event Delivery Using HTTP §5.2. Push Message Time-To-Live](https://datatracker.ietf.org/doc/html/rfc8030#section-5.2) + /// - SeeAlso: [RFC 8030 — Generic Event Delivery Using HTTP §5.2. Push Message Time-To-Live](https://datatracker.ietf.org/doc/html/rfc8030#section-5.2) public static let dropIfUndeliverable: Self = .zero /// Initialize an expiration with a number of seconds. @@ -868,7 +868,7 @@ extension WebPushManager { extension WebPushManager { /// The urgency with which to deliver a push message. /// - /// - SeeAlso: [RFC 8030 Generic Event Delivery Using HTTP §5.3. Push Message Urgency](https://datatracker.ietf.org/doc/html/rfc8030#section-5.3) + /// - SeeAlso: [RFC 8030 — Generic Event Delivery Using HTTP §5.3. Push Message Urgency](https://datatracker.ietf.org/doc/html/rfc8030#section-5.3) public struct Urgency: Hashable, Comparable, Sendable, CustomStringConvertible { /// The internal raw value that is encoded in this type's place when calling ``description``. let rawValue: String From 069ab862aa396451cfa78f6a696068743b214695 Mon Sep 17 00:00:00 2001 From: Dimitri Bouniol Date: Wed, 25 Dec 2024 14:29:11 -0800 Subject: [PATCH 4/4] Updated suggested version in readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7b89080..2903f0a 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ Please check the [releases](https://github.com/mochidev/swift-webpush/releases) dependencies: [ .package( url: "https://github.com/mochidev/swift-webpush.git", - .upToNextMinor(from: "0.3.3") + .upToNextMinor(from: "0.4.0") ), ], ...