11import Vapor
22import APNS
3+ #if canImport(Darwin)
34import Foundation
5+ #else
6+ // JSONEncoder / JSONDecoder is not Sendable in scf, but is in Darwin...
7+ // Import as `@preconcurrency` to fix warnings.
8+ @preconcurrency import Foundation
9+ #endif
410import NIO
511import NIOConcurrencyHelpers
612
713public typealias APNSGenericClient = APNSClient < JSONDecoder , JSONEncoder >
814
9- public class APNSContainers {
10- public struct ID : Hashable , Codable {
15+ public final class APNSContainers : Sendable {
16+ public struct ID : Sendable , Hashable , Codable {
1117 public let string : String
1218 public init ( string: String ) {
1319 self . string = string
1420 }
1521 }
1622
17- public final class Container {
23+ public final class Container : Sendable {
1824 public let configuration : APNSClientConfiguration
1925 public let client : APNSGenericClient
2026
21- internal init ( configuration: APNSClientConfiguration , client: APNSGenericClient ) {
27+ internal init ( configuration: APNSClientConfiguration , client: APNSGenericClient ) {
2228 self . configuration = configuration
2329 self . client = client
2430 }
2531 }
2632
27- private var containers : [ ID : Container ]
28- private var defaultID : ID ?
29- private var lock : NIOLock
33+ private let storage : NIOLockedValueBox < ( containers: [ ID : Container ] , defaultID: ID ? ) >
3034
3135 init ( ) {
32- self . containers = [ : ]
33- self . lock = . init( )
36+ storage = . init( ( containers: [ : ] , defaultID: nil ) )
3437 }
3538
3639 public func syncShutdown( ) {
37- self . lock. lock ( )
38- defer { self . lock. unlock ( ) }
39- do {
40- try containers. forEach { key, container in
41- try container. client. syncShutdown ( )
40+ storage. withLockedValue {
41+ do {
42+ try $0. containers. values. forEach { container in
43+ try container. client. syncShutdown ( )
44+ }
45+ } catch {
46+ fatalError ( " Could not shutdown APNS Containers " )
4247 }
43- } catch {
44- fatalError ( " Could not shutdown APNS Containers " )
4548 }
4649 }
4750}
@@ -57,42 +60,40 @@ extension APNSContainers {
5760 as id: ID ,
5861 isDefault: Bool ? = nil
5962 ) {
60- self . lock. lock ( )
61- defer { self . lock. unlock ( ) }
62-
63- self . containers [ id] = Container (
64- configuration: config,
65- client: APNSGenericClient (
63+ storage. withLockedValue {
64+ $0. containers [ id] = Container (
6665 configuration: config,
67- eventLoopGroupProvider: eventLoopGroupProvider,
68- responseDecoder: responseDecoder,
69- requestEncoder: requestEncoder,
70- byteBufferAllocator: byteBufferAllocator
66+ client: APNSGenericClient (
67+ configuration: config,
68+ eventLoopGroupProvider: eventLoopGroupProvider,
69+ responseDecoder: responseDecoder,
70+ requestEncoder: requestEncoder,
71+ byteBufferAllocator: byteBufferAllocator
72+ )
7173 )
72- )
7374
74- if isDefault == true || ( self . defaultID == nil && isDefault != false ) {
75- self . defaultID = id
75+ if isDefault == true || ( $0. defaultID == nil && isDefault != false ) {
76+ $0. defaultID = id
77+ }
7678 }
7779 }
7880
7981 public func `default`( to id: ID ) {
80- self . lock . lock ( )
81- defer { self . lock . unlock ( ) }
82- self . defaultID = id
82+ storage . withLockedValue {
83+ $0 . defaultID = id
84+ }
8385 }
8486
8587 public func container( for id: ID ? = nil ) -> APNSContainers . Container ? {
86- self . lock. lock ( )
87- defer { self . lock. unlock ( ) }
88- guard let id = id ?? self . defaultID else {
89- return nil
88+ storage. withLockedValue {
89+ guard let id = id ?? $0. defaultID else {
90+ return nil
91+ }
92+ return $0. containers [ id]
9093 }
91- return self . containers [ id]
9294 }
9395
9496 public var container : APNSContainers . Container ? {
9597 container ( )
9698 }
9799}
98-
0 commit comments