Skip to content

Commit af59858

Browse files
authored
Add semantic safe API for FileProvider notifications (#149)
# Motivation We want to provide new APIs that are semantically safe when sending FileProvider notifications to APNs. # Modification The PR adds new types for the FileProvider notification and a convenience method to send it notifications with the APNSClient. # Result We can now send FileProvider notifications.
1 parent 93b9fe7 commit af59858

File tree

4 files changed

+185
-0
lines changed

4 files changed

+185
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
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 Logging
16+
import NIOCore
17+
18+
extension APNSClient {
19+
/// Sends a file provider notification to APNs.
20+
///
21+
/// - Parameters:
22+
/// - notification: The notification to send.
23+
///
24+
/// - deviceToken: The hexadecimal bytes that identify the user’s device. Your app receives the bytes for this device token
25+
/// when registering for remote notifications.
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 sendFileProviderNotification<Payload: Encodable>(
33+
_ notification: APNSFileProviderNotification<Payload>,
34+
deviceToken: String,
35+
deadline: NIODeadline,
36+
logger: Logger = _noOpLogger
37+
) async throws -> APNSResponse {
38+
try await self.send(
39+
payload: notification.payload,
40+
deviceToken: deviceToken,
41+
pushType: .fileprovider,
42+
expiration: notification.expiration,
43+
// This always needs to be consideringDevicePower otherwise APNs returns an error
44+
priority: .consideringDevicePower,
45+
topic: notification.topic,
46+
deadline: deadline,
47+
logger: logger
48+
)
49+
}
50+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
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 file provider notification.
18+
public struct APNSFileProviderNotification<Payload: Encodable> {
19+
/// A canonical UUID that identifies the notification. If there is an error sending the notification,
20+
/// APNs uses this value to identify the notification to your server. The canonical form is 32 lowercase hexadecimal digits,
21+
/// displayed in five groups separated by hyphens in the form 8-4-4-4-12. An example UUID is as follows:
22+
/// `123e4567-e89b-12d3-a456-42665544000`.
23+
///
24+
/// If you omit this, a new UUID is created by APNs and returned in the response.
25+
public var apnsID: UUID?
26+
27+
/// The topic for the notification. In general, the topic is your app’s bundle ID/app ID suffixed with `.voip`.
28+
public var topic: String
29+
30+
/// The date when the notification is no longer valid and can be discarded. If this value is not `none`,
31+
/// APNs stores the notification and tries to deliver it at least once,
32+
/// repeating the attempt as needed if it is unable to deliver the notification the first time.
33+
/// If the value is `immediately`, APNs treats the notification as if it expires immediately
34+
/// and does not store the notification or attempt to redeliver it.
35+
public var expiration: APNSNotificationExpiration
36+
37+
/// Your custom payload.
38+
public var payload: Payload
39+
40+
/// Initializes a new ``APNSFileProviderNotification``.
41+
///
42+
/// - Parameters:
43+
/// - expiration: The date when the notification is no longer valid and can be discarded
44+
/// - appID: Your app’s bundle ID/app ID. This will be suffixed with `.pushkit.fileprovider`.
45+
/// - payload: Your custom payload.
46+
/// - apnsID: A canonical UUID that identifies the notification.
47+
@inlinable
48+
public init(
49+
expiration: APNSNotificationExpiration,
50+
appID: String,
51+
payload: Payload,
52+
apnsID: UUID? = nil
53+
) {
54+
self.init(
55+
expiration: expiration,
56+
topic: appID + ".pushkit.fileprovider",
57+
payload: payload,
58+
apnsID: apnsID
59+
)
60+
}
61+
62+
/// Initializes a new ``APNSFileProviderNotification``.
63+
///
64+
/// - Important: Your dynamic payload will get encoded to the root of the JSON payload that is send to APNs.
65+
/// It is **important** that you do not encode anything with the key `aps`
66+
///
67+
/// - Parameters:
68+
/// - expiration: The date when the notification is no longer valid and can be discarded
69+
/// - topic: The topic for the notification. In general, the topic is your app’s bundle ID/app ID suffixed with `.pushkit.fileprovider`.
70+
/// - payload: Your custom payload.
71+
/// - apnsID: A canonical UUID that identifies the notification.
72+
@inlinable
73+
public init(
74+
expiration: APNSNotificationExpiration,
75+
topic: String,
76+
payload: Payload,
77+
apnsID: UUID? = nil
78+
) {
79+
self.expiration = expiration
80+
self.topic = topic
81+
self.payload = payload
82+
self.apnsID = apnsID
83+
}
84+
}

Sources/APNSwiftExample/Program.swift

+20
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ struct Main {
3030
/// To use this example app please provide proper values for variable below.
3131
static let deviceToken = ""
3232
static let pushKitDeviceToken = ""
33+
static let fileProviderDeviceToken = ""
3334
static let appBundleID = ""
3435
static let privateKey = """
3536
-----BEGIN PRIVATE KEY-----
@@ -68,6 +69,7 @@ struct Main {
6869
try await Self.sendMutableContentAlert(with: client)
6970
try await Self.sendBackground(with: client)
7071
try await Self.sendVoIP(with: client)
72+
try await Self.sendFileProvider(with: client)
7173
} catch {
7274
self.logger.error("Failed sending push", metadata: ["error": "\(error)"])
7375
}
@@ -218,3 +220,21 @@ extension Main {
218220
)
219221
}
220222
}
223+
224+
// MARK: FileProvider
225+
226+
@available(macOS 11.0, *)
227+
extension Main {
228+
static func sendFileProvider(with client: APNSClient<JSONDecoder, JSONEncoder>) async throws {
229+
try await client.sendFileProviderNotification(
230+
.init(
231+
expiration: .immediately,
232+
appID: self.appBundleID,
233+
payload: Payload()
234+
),
235+
deviceToken: self.fileProviderDeviceToken,
236+
deadline: .distantFuture,
237+
logger: self.logger
238+
)
239+
}
240+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
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 APNSwift
16+
import XCTest
17+
18+
final class APNSFileProviderNotificationTests: XCTestCase {
19+
func testAppID() {
20+
struct Payload: Encodable {
21+
let foo = "bar"
22+
}
23+
let fileProviderNotification = APNSFileProviderNotification(
24+
expiration: .immediately,
25+
appID: "com.example.app",
26+
payload: Payload()
27+
)
28+
29+
XCTAssertEqual(fileProviderNotification.topic, "com.example.app.pushkit.fileprovider")
30+
}
31+
}

0 commit comments

Comments
 (0)