Skip to content

Commit

Permalink
Merge branch 'main' into cleanup-for-6
Browse files Browse the repository at this point in the history
  • Loading branch information
kylebrowning authored Aug 20, 2024
2 parents 25e1447 + fe9a00c commit 7b0e51a
Show file tree
Hide file tree
Showing 6 changed files with 237 additions and 4 deletions.
2 changes: 1 addition & 1 deletion Sources/APNS/APNSClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ extension APNSClient {
var headers = self.defaultRequestHeaders

// Push type
headers.add(name: "apns-push-type", value: request.pushType.configuration.rawValue)
headers.add(name: "apns-push-type", value: request.pushType.description)

// APNS ID
if let apnsID = request.apnsID {
Expand Down
20 changes: 17 additions & 3 deletions Sources/APNSCore/APNSPushType.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@
//===----------------------------------------------------------------------===//

/// A struct which represents the different supported APNs push types.
public struct APNSPushType: Hashable, Sendable {
public enum Configuration: String, Hashable, Sendable {
public struct APNSPushType: Hashable, Sendable, CustomStringConvertible {

internal enum Configuration: String, Hashable, Sendable {
case alert
case background
case location
Expand All @@ -23,10 +24,15 @@ public struct APNSPushType: Hashable, Sendable {
case fileprovider
case mdm
case liveactivity
case pushtotalk
}

public var description: String {
configuration.rawValue
}

/// The underlying raw value that is send to APNs.
public var configuration: Configuration
internal var configuration: Configuration

/// Use the alert push type for notifications that trigger a user interaction—for example, an alert, badge, or sound.
///
Expand Down Expand Up @@ -94,4 +100,12 @@ public struct APNSPushType: Hashable, Sendable {
/// Use the live activity push type to update your live activity.
///
public static let liveactivity = Self(configuration: .liveactivity)

/// Use the pushtotalk push type for notifications that provide information about an incoming Push to Talk (Ptt).
///
/// Push to Talk services aren’t available to compatible iPad and iPhone apps running in visionOS.
///
/// - Important: If you set this push type, the topic must use your app’s bundle ID with `.voip-ptt` appended to the end.
///
public static let pushtotalk = Self(configuration: .pushtotalk)
}
45 changes: 45 additions & 0 deletions Sources/APNSCore/PushToTalk/APNSClient+PushToTalk.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the APNSwift open source project
//
// Copyright (c) 2022 the APNSwift project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of APNSwift project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//


extension APNSClientProtocol {
/// Sends a Push To Talk (PTT) notification to APNs.
///
/// - Parameters:
/// - notification: The notification to send.
///
/// - deviceToken: The hexadecimal bytes that identify the user’s device. Your app receives the bytes for this device token
/// when registering for remote notifications.
///
///
/// - logger: The logger to use for sending this notification.
@discardableResult
@inlinable
public func sendPushToTalkNotification<Payload: Encodable & Sendable>(
_ notification: APNSPushToTalkNotification<Payload>,
deviceToken: String
) async throws -> APNSResponse {
let request = APNSRequest(
message: notification,
deviceToken: deviceToken,
pushType: .pushtotalk,
expiration: notification.expiration,
priority: notification.priority,
apnsID: notification.apnsID,
topic: notification.topic,
collapseID: nil
)
return try await send(request)
}
}
124 changes: 124 additions & 0 deletions Sources/APNSCore/PushToTalk/APNSPushToTalkNotification.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the APNSwift open source project
//
// Copyright (c) 2022 the APNSwift project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of APNSwift project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//

import struct Foundation.UUID

/// A Push to Talk (PTT) notification.
public struct APNSPushToTalkNotification<Payload: Encodable & Sendable>: APNSMessage {
/// A canonical UUID that identifies the notification. If there is an error sending the notification,
/// APNs uses this value to identify the notification to your server. The canonical form is 32 lowercase hexadecimal digits,
/// displayed in five groups separated by hyphens in the form 8-4-4-4-12. An example UUID is as follows:
/// `123e4567-e89b-12d3-a456-42665544000`.
///
/// If you omit this, a new UUID is created by APNs and returned in the response.
public var apnsID: UUID?

/// The topic for the notification. In general, the topic is your app’s bundle ID/app ID suffixed with `.voip-ptt`.
public var topic: String

/// The date when the notification is no longer valid and can be discarded. If this value is not `none`,
/// APNs stores the notification and tries to deliver it at least once,
/// repeating the attempt as needed if it is unable to deliver the notification the first time.
/// If the value is `immediately`, APNs treats the notification as if it expires immediately
/// and does not store the notification or attempt to redeliver it.
public var expiration: APNSNotificationExpiration

/// The priority of the notification.
public var priority: APNSPriority

/// Your custom payload.
public var payload: Payload

/// Initializes a new ``APNSPushToTalkNotification``.
///
/// - Important: Your dynamic payload will get encoded to the root of the JSON payload that is send to APNs.
/// It is **important** that you do not encode anything with the key `aps`
///
/// - Parameters:
/// - expiration: The date when the notification is no longer valid and can be discarded. Defaults to `.immediately`
/// - priority: The priority of the notification. Defaults to `.immediately`
/// - appID: Your app’s bundle ID/app ID. This will be suffixed with `.voip-ptt`.
/// - payload: Payload contain speaker data like name of active speaker or indication the session has ended.
/// - apnsID: A canonical UUID that identifies the notification.
@inlinable
public init(
expiration: APNSNotificationExpiration = .immediately,
priority: APNSPriority = .immediately,
appID: String,
payload: Payload,
apnsID: UUID? = nil
) {
self.init(
expiration: expiration,
priority: priority,
topic: appID + ".voip-ptt",
payload: payload,
apnsID: apnsID
)
}

/// Initializes a new ``APNSPushToTalkNotification``.
///
/// - Important: Your dynamic payload will get encoded to the root of the JSON payload that is send to APNs.
/// It is **important** that you do not encode anything with the key `aps`
///
/// - Parameters:
/// - expiration: The date when the notification is no longer valid and can be discarded. Defaults to `.immediately`
/// - priority: The priority of the notification. Defaults to `.immediately`
/// - topic: The topic for the notification. In general, the topic is your app’s bundle ID/app ID suffixed with `.voip-ptt`.
/// - payload: Payload contain speaker data like name of active speaker or indication the session has ended.
/// - apnsID: A canonical UUID that identifies the notification.
@inlinable
public init(
expiration: APNSNotificationExpiration = .immediately,
priority: APNSPriority = .immediately,
topic: String,
payload: Payload,
apnsID: UUID? = nil
) {
self.expiration = expiration
self.priority = priority
self.topic = topic
self.payload = payload
self.apnsID = apnsID
}
}

extension APNSPushToTalkNotification where Payload == EmptyPayload {
/// Initializes a new ``APNSPushToTalkNotification`` with an EmptyPayload.
///
/// - Important: Your dynamic payload will get encoded to the root of the JSON payload that is send to APNs.
/// It is **important** that you do not encode anything with the key `aps`
///
/// - Parameters:
/// - expiration: The date when the notification is no longer valid and can be discarded. Defaults to `.immediately`
/// - priority: The priority of the notification. Defaults to `.immediately`
/// - appID: Your app’s bundle ID/app ID. This will be suffixed with `.voip-ptt`.
/// - payload: Empty Payload.
/// - apnsID: A canonical UUID that identifies the notification.
public init(
expiration: APNSNotificationExpiration = .immediately,
priority: APNSPriority = .immediately,
appID: String,
apnsID: UUID? = nil
) {
self.init(
expiration: expiration,
priority: priority,
topic: appID + ".voip-ptt",
payload: EmptyPayload(),
apnsID: apnsID
)
}
}
20 changes: 20 additions & 0 deletions Sources/APNSExample/Program.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ struct Main {
/// To use this example app please provide proper values for variable below.
static let deviceToken = ""
static let pushKitDeviceToken = ""
static let ephemeralPushToken = "" // PTT
static let fileProviderDeviceToken = ""
static let appBundleID = ""
static let privateKey = """
Expand Down Expand Up @@ -56,6 +57,7 @@ struct Main {
try await Self.sendBackground(with: client)
try await Self.sendVoIP(with: client)
try await Self.sendFileProvider(with: client)
try await Self.sendPushToTalk(with: client)
} catch {
logger.warning("error sending push: \(error)")
}
Expand Down Expand Up @@ -210,3 +212,21 @@ extension Main {
)
}
}


// MARK: Push to Talk (PTT)

@available(macOS 11.0, *)
extension Main {
static func sendPushToTalk(with client: some APNSClientProtocol) async throws {
try await client.sendPushToTalkNotification(
.init(
expiration: .immediately,
priority: .immediately,
appID: self.appBundleID,
payload: EmptyPayload()
),
deviceToken: self.ephemeralPushToken
)
}
}
30 changes: 30 additions & 0 deletions Tests/APNSTests/PushToTalk/APNSPushToTalkNotificationTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the APNSwift open source project
//
// Copyright (c) 2022 the APNSwift project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of APNSwift project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//

import APNSCore
import XCTest

final class APNSPushToTalkNotificationTests: XCTestCase {
func testAppID() {
struct Payload: Encodable {
let foo = "bar"
}
let voipNotification = APNSPushToTalkNotification(
appID: "com.example.app",
payload: Payload()
)

XCTAssertEqual(voipNotification.topic, "com.example.app.voip-ptt")
}
}

0 comments on commit 7b0e51a

Please sign in to comment.