You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: README.md
+201-4Lines changed: 201 additions & 4 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -96,6 +96,7 @@ A **Push Service** is run by browsers to coordinate delivery of messages to subs
96
96
97
97
</details>
98
98
99
+
99
100
### Generating Keys
100
101
101
102
Before integrating WebPush into your server, you must generate one time VAPID keys to identify your server to push services with. To help we this, we provide `vapid-key-generator`, which you can install and use as needed:
@@ -116,7 +117,7 @@ To update the generator, uninstall it and re-install it after pulling from main:
116
117
% swift package experimental-install
117
118
```
118
119
119
-
Once installed, a new configuration can be generated as needed:
120
+
Once installed, a new configuration can be generated as needed. Here, we generate a configuration with `https://example.com` as our support URL for push service administrators to use to contact us when issues occur:
> If you prefer, you can also generate keys in your own code by calling `VAPID.Key()`, but remember, the key should be persisted and re-used from that point forward!
164
165
166
+
165
167
### Setup
166
168
167
-
TBD
169
+
During the setup stage of your application server, decode the VAPID configuration you created above and initialize a `WebPushManager` with it:
170
+
171
+
```swift
172
+
importWebPush
173
+
174
+
...
175
+
176
+
guard
177
+
let rawVAPIDConfiguration = ProcessInfo.processInfo.environment["VAPID-CONFIG"],
178
+
let vapidConfiguration =try?JSONDecoder().decode(VAPID.Configuration.self, from: Data(rawVAPIDConfiguration.utf8))
179
+
else { fatalError("VAPID keys are unavailable, please generate one and add it to the environment.") }
180
+
181
+
let manager =WebPushManager(
182
+
vapidConfiguration: vapidConfiguration,
183
+
backgroundActivityLogger: logger
184
+
/// If you customized the event loop group your app uses, you can set it here:
If you are not yet using [Swift Service Lifecycle](https://github.com/swift-server/swift-service-lifecycle), you can skip adding it to the service group, and it'll shut down on deinit instead. This however may be too late to finish sending over all in-flight messages, so prefer to use a ServiceGroup for all your services if you can.
199
+
200
+
You'll also want to serve a `serviceWorker.mjs` file at the root of your server (it can be anywhere, but there are scoping restrictions that are simplified by serving it at the root) to handle incoming notifications:
201
+
202
+
```js
203
+
self.addEventListener('push', function(event) {
204
+
constdata=event.data?.json() ?? {};
205
+
event.waitUntil((async () => {
206
+
/// Try parsing the data, otherwise use fallback content. DO NOT skip sending the notification, as you must display one for every push message that is received or your subscription will be dropped.
207
+
let title = data?.title??"Your App Name";
208
+
constbody= data?.body??"New Content Available!";
209
+
210
+
awaitself.registration.showNotification(title, {
211
+
body,
212
+
icon:"/notification-icon.png", /// Only some browsers support this.
213
+
data
214
+
});
215
+
})());
216
+
});
217
+
```
218
+
219
+
> [!NOTE]
220
+
> `.mjs` here allows your code to import other js modules as needed. If you are not using Vapor, please make sure your server uses the correct mime type for this file extension.
221
+
168
222
169
223
### Registering Subscribers
170
224
171
-
TBD
225
+
To register a subscriber, you'll need backend code to provide your VAPID key, and frontend code to ask the browser for a subscription on behalf of the user.
226
+
227
+
On the backend (we are assuming Vapor here), register a route that returns your VAPID public key:
228
+
229
+
```swift
230
+
import WebPush
231
+
232
+
...
233
+
234
+
/// Listen somewhere for a VAPID key request. This path can be anything you want, and should be available to all parties you with to serve push messages to.
235
+
app.get("vapidKey", use: loadVapidKey)
236
+
237
+
...
238
+
239
+
/// A wrapper for the VAPID key that Vapor can encode.
Also register a route for persisting `Subscriber`'s:
253
+
254
+
```swift
255
+
import WebPush
256
+
257
+
...
258
+
259
+
/// Listen somewhere for new registrations. This path can be anything you want, and should be available to all parties you with to serve push messages to.
let subscriptionRequest =tryrequest.content.decode(Subscriber.self, as: .jsonAPI)
272
+
273
+
// TODO: Persist subscriptionRequest!
274
+
275
+
returnSubscriptionStatus()
276
+
}
277
+
```
278
+
279
+
> [!NOTE]
280
+
> `WebPushManager` (`manager` here) is fully sendable, and should be shared with your controllers using dependency injection. This allows you to fully test your application server by relying on the provided `WebPushTesting` library in your unit tests to mock keys, verify delivery, and simulate errors.
281
+
282
+
On the frontend, register your service worker, fetch your vapid key, and subscribe on behalf of the user:
/// It is good practice to provide the applicationServerKey back here so we can track which one was used if multiple were provided during configuration.
/// If sent from a request, pass the request's logger here to maintain its metadata.
354
+
// logger: request.logger
355
+
)
356
+
} catchBadSubscriberError() {
357
+
/// The subscription is no longer valid and should be removed.
358
+
} catchMessageTooLargeError() {
359
+
/// The message was too long and should be shortened.
360
+
} catchlet error as HTTPError {
361
+
/// The push service ran into trouble. error.response may help here.
362
+
} catch {
363
+
/// An unknown error occurred.
364
+
}
365
+
```
366
+
367
+
Your service worker will receive this message, decode it, and present it to the user.
368
+
369
+
> [!NOTE]
370
+
> Although the spec supports it, most browsers do not support silent notifications, and will drop a subscription if they are used.
371
+
176
372
177
373
### Testing
178
374
179
375
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.
180
376
377
+
181
378
## Specifications
182
379
183
380
- [RFC 7515 JSON Web Signature (JWS)](https://datatracker.ietf.org/doc/html/rfc7515)
0 commit comments