Skip to content

Commit 8089a53

Browse files
committed
Merge remote-tracking branch 'upstream/main'
2 parents 7048038 + 989437d commit 8089a53

File tree

4 files changed

+165
-0
lines changed

4 files changed

+165
-0
lines changed

Sources/APNSCore/APNSPushType.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ public struct APNSPushType: Hashable, Sendable, CustomStringConvertible {
2525
case mdm
2626
case liveactivity
2727
case pushtotalk
28+
case widgets
2829
}
2930

3031
public var description: String {
@@ -108,4 +109,10 @@ public struct APNSPushType: Hashable, Sendable, CustomStringConvertible {
108109
/// - Important: If you set this push type, the topic must use your app’s bundle ID with `.voip-ptt` appended to the end.
109110
///
110111
public static let pushtotalk = Self(configuration: .pushtotalk)
112+
113+
/// Use the widgets push type for notifications that trigger widget updates.
114+
///
115+
/// - Important: if you set this push type, the topic must use your app’s bundle ID with `.push-type.widgets` appended to the end.
116+
///
117+
public static let widgets = Self(configuration: .widgets)
111118
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the APNSwift open source project
4+
//
5+
// Copyright (c) 2025 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+
16+
extension APNSClientProtocol {
17+
/// Sends a widgets update notification to APNs.
18+
///
19+
/// - Parameters:
20+
/// - notification: The notification to send.
21+
///
22+
/// - deviceToken: The hexadecimal bytes that identify the user’s device. Your app receives the bytes for this device token
23+
/// when registering for remote notifications.
24+
///
25+
@discardableResult
26+
@inlinable
27+
public func sendWidgetsNotification(
28+
notification: APNSWidgetsNotification,
29+
deviceToken: String
30+
) async throws -> APNSResponse {
31+
let request = APNSRequest(
32+
message: notification,
33+
deviceToken: deviceToken,
34+
pushType: .widgets,
35+
expiration: nil,
36+
priority: nil,
37+
apnsID: notification.apnsID,
38+
topic: notification.topic,
39+
collapseID: nil
40+
)
41+
return try await send(request)
42+
}
43+
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
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+
/// A widget update notification.
18+
public struct APNSWidgetsNotification: APNSMessage {
19+
@usableFromInline
20+
struct APS: Encodable, Sendable {
21+
enum CodingKeys: String, CodingKey {
22+
case contentAvailable = "content-changed"
23+
}
24+
25+
let contentAvailable: Bool = true
26+
}
27+
28+
@usableFromInline
29+
enum CodingKeys: CodingKey {
30+
case aps
31+
}
32+
33+
/// The fixed content to indicate that this is a background notification.
34+
@usableFromInline
35+
internal let aps = APS()
36+
37+
/// A canonical UUID that identifies the notification. If there is an error sending the notification,
38+
/// APNs uses this value to identify the notification to your server. The canonical form is 32 lowercase hexadecimal digits,
39+
/// displayed in five groups separated by hyphens in the form 8-4-4-4-12. An example UUID is as follows:
40+
/// `123e4567-e89b-12d3-a456-42665544000`.
41+
///
42+
/// If you omit this, a new UUID is created by APNs and returned in the response.
43+
public var apnsID: UUID?
44+
45+
/// The topic for the notification. In general, the topic is your app’s bundle ID/app ID suffixed with `.push-type.widgets`.
46+
public var topic: String
47+
48+
/// Initializes a new ``APNSWidgetsNotification``.
49+
///
50+
/// - Parameters:
51+
/// - appID: Your app’s bundle ID/app ID. This will be suffixed with `.push-type.widgets`.
52+
/// - apnsID: A canonical UUID that identifies the notification.
53+
@inlinable
54+
public init(
55+
appID: String,
56+
apnsID: UUID? = nil
57+
) {
58+
self.init(
59+
topic: appID + ".push-type.widgets",
60+
apnsID: apnsID
61+
)
62+
}
63+
64+
/// Initializes a new ``APNSWidgetsNotification``.
65+
///
66+
/// - Parameters:
67+
/// - topic: The topic for the notification. In general, the topic is your app’s bundle ID/app ID suffixed with `.push-type.widgets`.
68+
/// - apnsID: A canonical UUID that identifies the notification.
69+
@inlinable
70+
public init(
71+
topic: String,
72+
apnsID: UUID? = nil
73+
) {
74+
self.topic = topic
75+
self.apnsID = apnsID
76+
}
77+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
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 APNSCore
16+
import XCTest
17+
18+
final class APNSWidgetsNotificationTests: XCTestCase {
19+
func testAppID() {
20+
let widgetsNotification = APNSWidgetsNotification(appID: "com.example.app")
21+
XCTAssertEqual(widgetsNotification.topic, "com.example.app.push-type.widgets")
22+
}
23+
24+
func testEncode() throws {
25+
let widgetsNotification = APNSWidgetsNotification(appID: "com.example.app")
26+
27+
let encoder = JSONEncoder()
28+
let data = try encoder.encode(widgetsNotification)
29+
30+
let expectedJSONString = """
31+
{"aps":{"content-changed":true}}
32+
"""
33+
let jsonObject1 = try JSONSerialization.jsonObject(with: data) as! NSDictionary
34+
let jsonObject2 = try JSONSerialization.jsonObject(with: expectedJSONString.data(using: .utf8)!) as! NSDictionary
35+
XCTAssertEqual(jsonObject1, jsonObject2)
36+
}
37+
38+
}

0 commit comments

Comments
 (0)