import Foundation import APNSwift import NIO import Crypto struct APNSInterface { private struct Payload: Codable {} private var apnsConfiguration: APNSClientConfiguration! private let title: String private let messages: [String] private let topic: String private let apnsEventGroup = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount) private let apnsRequestEncoder = JSONEncoder() private let apnsResponseDecoder = JSONDecoder() init?(_ config: ServerConfiguration) { guard let key = load(keyAtPath: config.privateKeyFilePath) else { return nil } let authentication = APNSClientConfiguration.AuthenticationMethod.jwt( privateKey: key, keyIdentifier: config.keyIdentifier, teamIdentifier: config.teamIdentifier) apnsConfiguration = APNSClientConfiguration( authenticationMethod: authentication, environment: .sandbox) title = config.messageTitle messages = config.messages topic = config.topic } func sendPush(to tokens: Set) async { let client = APNSClient( configuration: apnsConfiguration, eventLoopGroupProvider: .shared(apnsEventGroup), responseDecoder: apnsResponseDecoder, requestEncoder: apnsRequestEncoder) log(info: "Client created") let alert = APNSAlertNotificationContent( title: .raw(title), body: .raw(messages.randomElement()!)) let apnsNotification = APNSAlertNotification( alert: alert, expiration: .none, priority: .immediately, topic: topic, payload: Payload(), sound: .default) do { for token in tokenStorage.tokens { log(info: "Sending push to \(token.prefix(6))...") try await client.sendAlertNotification( apnsNotification, deviceToken: token, deadline: .now() + .seconds(10)) log(info: "Sent push to \(token.prefix(6))...") } } catch let error as APNSError { print(error) } catch { log(error: error.localizedDescription) } do { log("Closing client") try client.syncShutdown() } catch { log(error: error.localizedDescription) } } } private func load(keyAtPath path: String) -> P256.Signing.PrivateKey? { do { guard let key = try P256.Signing.PrivateKey.loadFrom(filePath: path) else { print("Failed to read key \(path)") return nil } return key } catch { print("Failed to load key \(error)") return nil } }