Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
262 changes: 36 additions & 226 deletions src/pages/docs/push/getting-started/apns.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ meta_keywords: "Push Notifications Swift, Ably Push, APNs, iOS Push, Swift push

This guide will get you started with Ably Push Notifications in a new SwiftUI application.

You'll learn how to set up your `AppDelegate` to manage push notifications, register devices with Ably, send push notifications, subscribe to channel-based push, handle incoming notifications, and implement location-based push notifications.
You'll learn how to set up your `AppDelegate` to manage push notifications, register devices with Ably, publish push notifications, subscribe to channel-based push, handle incoming notifications, and implement location-based push notifications.

<Aside data-type='note'>
Using an AI coding assistant? [Teach it Ably](/docs/platform/ai-llms#agent-skills) with Agent Skills for all popular AI coding agents. Run `claude plugin add ably/agent-skills` or `npx skills add ably/agent-skills` to get started.
Expand All @@ -16,16 +16,16 @@ Using an AI coding assistant? [Teach it Ably](/docs/platform/ai-llms#agent-skill

1. [Sign up](https://ably.com/signup) for an Ably account.
2. Create a [new app](https://ably.com/accounts/any/apps/new), and create your first API key in the **API Keys** tab of the dashboard.
3. Your API key will need the `publish` and `subscribe` capabilities. For sending push notifications from your app, you'll also need the `push-admin` capability.
4. For channel-based push, add a rule for the channel with **Push notifications enabled** checked. In the dashboard left sidebar: **Configuration** → **Rules** → **Add** or **Edit** a rule,
then enable the Push notifications option. See [rules](/docs/channels#rules) for details.
5. Install [Xcode](https://developer.apple.com/xcode/).
6. You'll need a real iOS device to test push notifications (the simulator doesn't support APNs).
7. Set up Apple Push Notification service (APNs) certificates through the [Apple Developer Portal](https://developer.apple.com/).
* Your API key needs the `publish` and `subscribe` capabilities.
* Also add the `push-admin` capability if you're using the same API key to publish a push notification. In production this would more likely be a server using a different API key.
3. Add a rule to a channel so you can test sending push notifications via a channel. Select [**Rules**](https://ably.com/accounts/any/apps/any/app_namespaces) in the Ably dashboard, add a new rule and enable the **Push notifications** option.
4. Install [Xcode](https://developer.apple.com/xcode/).
5. You'll need a real iOS device to test push notifications (the simulator doesn't support APNs).
6. Set up Apple Push Notification service (APNs) certificates through the [Apple Developer Portal](https://developer.apple.com/).

### (Optional) Install Ably CLI <a id="install-cli"/>

Use the [Ably CLI](https://github.com/ably/cli) as an additional client to quickly test Pub/Sub features and push notifications.
Use the [Ably CLI](/docs/platform/tools/cli) as an additional client to quickly test Pub/Sub features and push notifications.

1. Install the Ably CLI:

Expand Down Expand Up @@ -205,7 +205,7 @@ func didDeactivateAblyPush(_ error: ARTErrorInfo?) {

Now you are ready to receive push notifications.

## Step 3: Receive push notifications <a id="step-3"/>
### Handle push notifications <a id="step-2-handle"/>

Use `UNUserNotificationCenterDelegate` methods to receive push notifications.
You've set the notification center delegate in the `application:didFinishLaunchingWithOptions` method.
Expand Down Expand Up @@ -239,8 +239,9 @@ func userNotificationCenter(_ center: UNUserNotificationCenter,
```
</Code>

Push notifications can be sent either directly to your `deviceId` (or `clientId`),
or posted to a channel, in which case you first need to subscribe your device to that channel:
## Step 3: Subscribe to channel push notifications <a id="step-3"/>

To subscribe your device to a channel so it can receive channel-based push notifications, add the following methods to your `AppDelegate` class:

<Code>
```swift
Expand Down Expand Up @@ -274,23 +275,6 @@ func unsubscribeFromChannel(_ channelName: String) {
```
</Code>

Sending push notifications using `deviceId` or `clientId` requires the `push-admin` capability for your API key. Use this method for testing purposes. In a production environment, you would typically send push notifications from your backend server (by posting messages with `push` `extras` field to a channel).

To test push notifications in your app, you can use [Ably dashboard](https://ably.com/dashboard), [Apple developer dashboard](https://icloud.developer.apple.com/dashboard/) or [Ably CLI](/docs/platform/tools/cli).

To publish to your client ID using the [Ably CLI](/docs/platform/tools/cli) paste the following command into your terminal:

<Code>
```shell
ably push publish --client-id push-tutorial-client \
--title "Test push" \
--body "Hello from CLI!" \
--data '{"foo":"bar","baz":"qux"}'
```
</Code>

For sending pushes via a channel, we need some actual UI to be able to subscribe to this channel. So, let's build one.

## Step 4: Build the UI <a id="step-4"/>

First, in your `PushTutorialApp.swift`, add `@UIApplicationDelegateAdaptor` wrapped `appDelegate` property
Expand Down Expand Up @@ -546,217 +530,43 @@ struct ChannelSection: View {
</Code>

Build and run your app in Xcode on a real device. You will see the UI with sections to activate
push notifications and subscribe to channels. Tap the "Activate Push" button and wait until the status message
displays the received device token. Try sending push using client ID or device ID as shown earlier.
You can get your device ID from the device details button (don't confuse it with the device token):

![Screenshot of the Swift push tutorial application](../../../../images/content/screenshots/getting-started/apns-swift-getting-started-guide.png)

### Send push via channel <a id="step-4-send-channel"/>
push notifications and subscribe to channels.

To test pushes via channel, subscribe to "Channel 1" in the UI and publish a push notification to the channel using the [Ably CLI](/docs/platform/tools/cli):
## Step 5: Publish a push notification <a id="step-5"/>

<Code>
```shell
ably push publish --channel exampleChannel1 \
--title "Hello" \
--body "World!" \
--message '{"name":"greeting","data":"Hello World!"}'
```
</Code>

If you unsubscribe from this channel in the app's UI, you will no longer receive push notifications for that channel.
Send the same command again and verify that no notification is received.
Tap **Activate Push** and wait until the status message displays the received device token. You can get your device ID from the **Get Device Details** button (don't confuse it with the device token).

You can also send push notifications right from your app. The next step will show you how.
### Publish directly to your device <a id="step-5-direct"/>

## Step 5: Send push with code <a id="step-5"/>
Publish a push notification directly to your client ID (or device ID using `--device-id` instead of `--client-id`) via the [Ably CLI](/docs/platform/tools/cli):

Just as you can send push notifications through the Ably CLI or dashboard, you can also send them directly from your app
using `deviceId` (or `clientId`), or channel publishing methods. For channel publishing, you don't need the admin capabilities
for your API key.

Add the following methods to your `AppDelegate` class:

<Code>
```swift
// MARK: - Send Push Notifications

/// Send push notification to a specific device ID
func sendPushToDevice() {
let recipient = [
"deviceId": realtime.device.id
]
let data = [
"notification": [
"title": "Push Tutorial",
"body": "Hello from device ID!"
],
"data": [
"foo": "bar",
"baz": "qux"
]
]
realtime.push.admin.publish(recipient, data: data) { error in
print("Publish result: \(error?.localizedDescription ?? "Success")")
}
}

/// Send push notification to a specific client ID
func sendPushToClient() {
let recipient = [
"clientId": realtime.auth.clientId ?? "push-tutorial-client"
]
let data = [
"notification": [
"title": "Push Tutorial",
"body": "Hello from client ID!"
],
"data": [
"foo": "bar",
"baz": "qux"
]
]
realtime.push.admin.publish(recipient, data: data) { error in
print("Publish result: \(error?.localizedDescription ?? "Success")")
}
}
```
</Code>

Sending to a channel is just publishing a message on a channel with a `push` `extras` field:

<Code>
```swift
/// Send push notification to a specific channel by publishing a message with a push extras field
func sendPushToChannel(_ channelName: String) {
let message = ARTMessage(name: "example", data: "Hello from channel!")
message.extras = [
"push": [
"notification": [
"title": "Channel Push",
"body": "Sent push to \(channelName)"
],
"data": [
"foo": "bar",
"baz": "qux"
]
]
] as any ARTJsonCompatible

realtime.channels.get(channelName).publish([message]) { error in
if let error {
print("Error sending push to \(channelName) with error: \(error.localizedDescription)")
} else {
print("Sent push to \(channelName)")
}
}
}
<Code fixed="true">
```shell
ably push publish --client-id push-tutorial-client \
--title "Test push" \
--body "Hello from CLI!" \
--data '{"foo":"bar","baz":"qux"}'
```
</Code>

Now add buttons for these methods in the new `SendPushSection` view struct:

<Code>
```swift
// MARK: - Send Push Section

struct SendPushSection: View {
let appDelegate: AppDelegate
@Binding var statusMessage: String
@Binding var selectedChannel: String

var body: some View {
VStack(alignment: .leading, spacing: 12) {
Text("Send Push Notifications")
.font(.headline)

VStack(spacing: 10) {
Button(action: {
appDelegate.sendPushToDevice()
statusMessage = "Sending push to device ID..."
}) {
HStack {
Image(systemName: "phone.badge.checkmark")
Text("Send Push to Device ID")
}
.frame(maxWidth: .infinity)
.padding()
.background(Color.purple)
.foregroundStyle(.white)
.cornerRadius(8)
}
### Publish via a channel <a id="step-5-channel"/>

Button(action: {
appDelegate.sendPushToClient()
statusMessage = "Sending push to client ID..."
}) {
HStack {
Image(systemName: "person.crop.circle.badge.checkmark")
Text("Send Push to Client ID")
}
.frame(maxWidth: .infinity)
.padding()
.background(Color.orange)
.foregroundStyle(.white)
.cornerRadius(8)
}
Subscribe to "Channel 1" in the UI, then publish a push notification to the channel using the [Ably CLI](/docs/platform/tools/cli):

HStack(spacing: 8) {
Menu {
Button(titleForChannel("exampleChannel1")) {
selectedChannel = "exampleChannel1"
}
Button(titleForChannel("exampleChannel2")) {
selectedChannel = "exampleChannel2"
}
} label: {
HStack {
Image(systemName: "line.3.horizontal.decrease.circle")
Text(titleForChannel(selectedChannel))
}
.frame(maxWidth: .infinity)
.padding()
.background(Color.gray.opacity(0.2))
.cornerRadius(8)
}

Button(action: {
appDelegate.sendPushToChannel(selectedChannel)
statusMessage = "Sending push to channel: \(selectedChannel)..."
}) {
HStack {
Image(systemName: "checkmark.circle.fill")
Text("Send")
}
.frame(maxWidth: .infinity)
.padding()
.background(Color.cyan)
.foregroundStyle(.white)
.cornerRadius(8)
}
}
}
}
.padding()
.background(Color.white)
.cornerRadius(12)
.shadow(radius: 2)
}
}
<Code fixed="true">
```shell
ably push publish --channel exampleChannel1 \
--title "Hello" \
--body "World!" \
--message '{"name":"greeting","data":"Hello World!"}'
```
</Code>

Update your `ContentView` body to include this section (add it just after the `ChannelSection`):
![Screenshot of the Swift push tutorial application](../../../../images/content/screenshots/getting-started/apns-swift-getting-started-guide.png)

<Code>
```swift
// Send Push Section
SendPushSection(appDelegate: appDelegate, statusMessage: $statusMessage, selectedChannel: $selectedChannel)
```
</Code>
If you unsubscribe from this channel in the app's UI, you will no longer receive push notifications for that channel. Run the same command again and verify that no notification is received.

Build and run your app again. Use the added section to send push notifications.
To see the full list of options for publishing push notifications with the Ably CLI, run `ably push publish --help` or see the [Ably CLI push documentation](/docs/cli/push). To publish push notifications from your own server code instead of the CLI, see [Push notification publishing](https://ably.com/docs/push/publish).

## Step 6: Location pushes <a id="step-6"/>

Expand All @@ -769,7 +579,7 @@ Add `Location (when in use)`, `Location (Always)`, `Location Push Service Extens
Add `Location Push Service Extension` target as described at the [Apple Developer Portal](https://developer.apple.com/documentation/CoreLocation/creating-a-location-push-service-extension).
For simplicity, use **Automatically manage signing**, so all needed identifiers are created for you by
Xcode (with XC prefix in their display name). Your Location Push Service Extension should have a bundle
identifier of your app with a suffix of extension's product name (e.g., `the.company.TheApp.TheExtension`).
identifier of your app with a suffix of extension's product name (for example, `the.company.TheApp.TheExtension`).

Add these methods to your `AppDelegate` class:

Expand Down Expand Up @@ -861,7 +671,7 @@ func didUpdateAblyPush(_ error: ARTErrorInfo?) {
```
</Code>

### Receiving location pushes <a id="step-5-receiving"/>
### Receive location pushes <a id="step-6-receiving"/>

Once you've added the location push extension to your project, Xcode gives you a default implementation of the
`LocationPushService.swift` file in your extension target.
Expand Down
Loading
Loading