Skip to content

Commit 4626afb

Browse files
iOS 16.1 Live Activity support added (#164)
* -- iOS 16.1 Live Activity support added * - live activity added to APNSPushType * - updated suggested changes * -- updated ReadMe * - fixed topic update * PR FeedBAck Co-authored-by: Nasir Mehmood <[email protected]>
1 parent c3ac9f5 commit 4626afb

7 files changed

+286
-2
lines changed

README.md

+19
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ A non-blocking Swift module for sending remote Apple Push Notification requests
1111
- [Foundations](#foundations)
1212
- [Getting Started](#getting-started)
1313
- [Sending a simple notification](#sending-a-simple-notification)
14+
- [Sending Live Activity Update](#sending-live-activity-update)
1415
- [Authentication](#authentication)
1516
- [Logging](#logging)
1617
- [**Background Activity Logger**](#background-activity-logger)
@@ -96,6 +97,24 @@ try await client.sendAlertNotification(
9697
)
9798
```
9899

100+
## Sending Live Activity Update
101+
It requires sending `ContentState` matching with the live activity configuration to successfully update activity state. `ContentState` needs to conform to `Encodable`
102+
103+
```swift
104+
try await client.sendLiveActivityNotification(
105+
.init(
106+
expiration: .immediately,
107+
priority: .immediately,
108+
appID: "com.app.bundle",
109+
contentState: ContentState,
110+
event: .update,
111+
timestamp: Int(Date().timeIntervalSince1970)
112+
),
113+
activityPushToken: activityPushToken,
114+
deadline: .distantFuture
115+
)
116+
```
117+
99118
## Authentication
100119
`APNSwift` provides two authentication methods. `jwt`, and `TLS`.
101120

Sources/APNSwift/APNSPushType.swift

+5
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ public struct APNSPushType: Hashable {
2323
case complication
2424
case fileprovider
2525
case mdm
26+
case liveactivity
2627
}
2728

2829
/// The underlying raw value that is send to APNs.
@@ -91,4 +92,8 @@ public struct APNSPushType: Hashable {
9192
///
9293
/// - Important: If you set this push type, you must use the topic from the UID attribute in the subject of your MDM push certificate.
9394
public static let mdm = Self(configuration: .mdm)
95+
96+
/// Use the live activity push type to update your live activity.
97+
///
98+
public static let liveactivity = Self(configuration: .liveactivity)
9499
}

Sources/APNSwift/Alert/APNSAlertNotification.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,8 @@ public struct APNSAlertNotification<Payload: Encodable>: Encodable {
8484
///
8585
/// If the value is `1`, the system passes the notification to your notification service app extension before delivery.
8686
/// Use your extension to modify the notification’s content.
87-
// Semantically this would make sense to be a Bool. However, APNS accepts any number here so
88-
// we need to allow this as well.
87+
/// Semantically this would make sense to be a Bool. However, APNS accepts any number here so
88+
/// we need to allow this as well.
8989
public var mutableContent: Double? {
9090
get {
9191
self.aps.mutableContent
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the APNSwift open source project
4+
//
5+
// Copyright (c) 2022 the APNSwift project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of APNSwift project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
import NIOCore
16+
import Logging
17+
18+
extension APNSClient {
19+
/// Sends a live activity notification.
20+
///
21+
/// - Parameters:
22+
/// - notification: The notification to send.
23+
///
24+
/// - deviceToken: The hexadecimal bytes use to send live activity notification. Your app receives the bytes for this activity token
25+
/// from `pushTokenUpdates` async property of a live activity.
26+
///
27+
/// - deadline: Point in time by which sending the notification to APNs must complete.
28+
///
29+
/// - logger: The logger to use for sending this notification.
30+
@discardableResult
31+
@inlinable
32+
public func sendLiveActivityNotification<ContentState: Encodable>(
33+
_ notification: APNSLiveActivityNotification<ContentState>,
34+
deviceToken: String,
35+
deadline: NIODeadline,
36+
logger: Logger = _noOpLogger
37+
) async throws -> APNSResponse {
38+
return try await self.send(
39+
payload: notification,
40+
deviceToken: deviceToken,
41+
pushType: .liveactivity,
42+
apnsID: notification.apnsID,
43+
expiration: notification.expiration,
44+
priority: notification.priority,
45+
topic: notification.topic,
46+
deadline: deadline,
47+
logger: logger
48+
)
49+
}
50+
}
51+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the APNSwift open source project
4+
//
5+
// Copyright (c) 2022 the APNSwift project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of APNSwift project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
import struct Foundation.UUID
16+
17+
/// An alert notification.
18+
///
19+
/// It is **important** that you do not encode anything with the key `aps`.
20+
public struct APNSLiveActivityNotification<ContentState: Encodable>: Encodable {
21+
enum CodingKeys: CodingKey {
22+
case aps
23+
}
24+
25+
/// The fixed content to indicate that this is a background notification.
26+
private var aps: APNSLiveActivityNotificationAPSStorage<ContentState>
27+
28+
/// Timestamp when sending notification
29+
public var timestamp: Int {
30+
get {
31+
return self.aps.timestamp
32+
}
33+
34+
set {
35+
self.aps.timestamp = newValue
36+
}
37+
}
38+
39+
/// Event type e.g. update
40+
public var event: APNSLiveActivityNotificationEvent {
41+
get {
42+
return APNSLiveActivityNotificationEvent(rawValue: self.aps.event)
43+
}
44+
45+
set {
46+
self.aps.event = newValue.rawValue
47+
}
48+
}
49+
50+
/// The dynamic content of a Live Activity.
51+
public var contentState: ContentState {
52+
get {
53+
return self.aps.contentState
54+
}
55+
56+
set {
57+
self.aps.contentState = newValue
58+
}
59+
}
60+
61+
/// A canonical UUID that identifies the notification. If there is an error sending the notification,
62+
/// APNs uses this value to identify the notification to your server. The canonical form is 32 lowercase hexadecimal digits,
63+
/// displayed in five groups separated by hyphens in the form 8-4-4-4-12. An example UUID is as follows:
64+
/// `123e4567-e89b-12d3-a456-42665544000`.
65+
///
66+
/// If you omit this, a new UUID is created by APNs and returned in the response.
67+
public var apnsID: UUID?
68+
69+
/// The date when the notification is no longer valid and can be discarded. If this value is not `none`,
70+
/// APNs stores the notification and tries to deliver it at least once,
71+
/// repeating the attempt as needed if it is unable to deliver the notification the first time.
72+
/// If the value is `immediately`, APNs treats the notification as if it expires immediately
73+
/// and does not store the notification or attempt to redeliver it.
74+
public var expiration: APNSNotificationExpiration
75+
76+
/// The priority of the notification.
77+
public var priority: APNSPriority
78+
79+
/// The topic for the notification. In general, the topic is your app’s bundle ID/app ID.
80+
public var topic: String
81+
82+
/// Initializes a new ``APNSLiveActivityNotification``.
83+
///
84+
/// - Important: Your dynamic payload will get encoded to the root of the JSON payload that is send to APNs.
85+
/// It is **important** that you do not encode anything with the key `aps`
86+
///
87+
/// - Parameters:
88+
/// - expiration: The date when the notification is no longer valid and can be discarded.
89+
/// - priority: The priority of the notification.
90+
/// - appID: Your app’s bundle ID/app ID. This will be suffixed with `.push-type.liveactivity`.
91+
/// - apnsID: A canonical UUID that identifies the notification.
92+
/// - contentState: Updated content-state of live activity
93+
/// - timestamp: Timestamp when sending notification
94+
/// - event: event type e.g. update
95+
public init(
96+
expiration: APNSNotificationExpiration,
97+
priority: APNSPriority,
98+
appID: String,
99+
contentState: ContentState,
100+
event: APNSLiveActivityNotificationEvent,
101+
timestamp: Int,
102+
apnsID: UUID? = nil
103+
) {
104+
self.init(
105+
expiration: expiration,
106+
priority: priority,
107+
topic: appID + ".push-type.liveactivity",
108+
contentState: contentState,
109+
event: event,
110+
timestamp: timestamp
111+
)
112+
}
113+
114+
115+
/// Initializes a new ``APNSLiveActivityNotification``.
116+
///
117+
/// - Important: Your dynamic payload will get encoded to the root of the JSON payload that is send to APNs.
118+
/// It is **important** that you do not encode anything with the key `aps`
119+
///
120+
/// - Parameters:
121+
/// - expiration: The date when the notification is no longer valid and can be discarded.
122+
/// - priority: The priority of the notification.
123+
/// - topic: The topic for the notification. In general, the topic is your app’s bundle ID/app ID suffixed with `.push-type.liveactivity`.
124+
/// - apnsID: A canonical UUID that identifies the notification.
125+
/// - contentState: Updated content-state of live activity
126+
/// - timestamp: Timestamp when sending notification
127+
/// - event: event type e.g. update
128+
public init(
129+
expiration: APNSNotificationExpiration,
130+
priority: APNSPriority,
131+
topic: String,
132+
apnsID: UUID? = nil,
133+
contentState: ContentState,
134+
event: APNSLiveActivityNotificationEvent,
135+
timestamp: Int
136+
) {
137+
self.aps = APNSLiveActivityNotificationAPSStorage(
138+
timestamp: timestamp,
139+
event: event.rawValue,
140+
contentState: contentState
141+
)
142+
self.apnsID = apnsID
143+
self.expiration = expiration
144+
self.priority = priority
145+
self.topic = topic
146+
}
147+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the APNSwift open source project
4+
//
5+
// Copyright (c) 2022 the APNSwift project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of APNSwift project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
struct APNSLiveActivityNotificationAPSStorage<ContentState: Encodable>: Encodable {
16+
enum CodingKeys: String, CodingKey {
17+
case timestamp = "timestamp"
18+
case event = "event"
19+
case contentState = "content-state"
20+
21+
}
22+
23+
var timestamp: Int
24+
var event: String
25+
var contentState: ContentState
26+
27+
init(
28+
timestamp: Int,
29+
event: String,
30+
contentState: ContentState
31+
) {
32+
self.timestamp = timestamp
33+
self.event = event
34+
self.contentState = contentState
35+
}
36+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the APNSwift open source project
4+
//
5+
// Copyright (c) 2022 the APNSwift project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of APNSwift project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
public struct APNSLiveActivityNotificationEvent: Hashable {
16+
17+
/// The underlying raw value that is send to APNs.
18+
@usableFromInline
19+
internal let rawValue: String
20+
21+
/// Specifies that live activity should be updated
22+
public static let update = Self(rawValue: "update")
23+
24+
/// Specifies that live activity should be ended
25+
public static let end = Self(rawValue: "end")
26+
}

0 commit comments

Comments
 (0)