Skip to content

Commit 98ba2f2

Browse files
authored
Add Sendable conformances (#57)
1 parent 00fddb7 commit 98ba2f2

File tree

5 files changed

+70
-47
lines changed

5 files changed

+70
-47
lines changed

.github/workflows/test.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ on:
88

99
jobs:
1010
unit-tests:
11-
uses: vapor/ci/.github/workflows/run-unit-tests.yml@reusable-workflows
11+
uses: vapor/ci/.github/workflows/run-unit-tests.yml@main
1212
with:
1313
with_coverage: true
1414
with_tsan: false

[email protected]

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// swift-tools-version:5.9
2+
import PackageDescription
3+
4+
let package = Package(
5+
name: "vapor-apns",
6+
platforms: [
7+
.macOS(.v13),
8+
.iOS(.v16)
9+
],
10+
products: [
11+
.library(name: "VaporAPNS", targets: ["VaporAPNS"]),
12+
],
13+
dependencies: [
14+
.package(url: "https://github.com/swift-server-community/APNSwift.git", from: "5.0.0"),
15+
.package(url: "https://github.com/vapor/vapor.git", from: "4.77.2"),
16+
],
17+
targets: [
18+
.target(name: "VaporAPNS", dependencies: [
19+
.product(name: "APNS", package: "apnswift"),
20+
.product(name: "Vapor", package: "vapor"),
21+
],
22+
swiftSettings: [.enableExperimentalFeature("StrictConcurrency=complete")]),
23+
.testTarget(name: "VaporAPNSTests", dependencies: [
24+
.target(name: "VaporAPNS"),
25+
.product(name: "XCTVapor", package: "vapor"),
26+
],
27+
swiftSettings: [.enableExperimentalFeature("StrictConcurrency=complete")]),
28+
]
29+
)

Sources/VaporAPNS/APNSContainers.swift

+39-38
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,50 @@
11
import Vapor
22
import APNS
3+
#if canImport(Darwin)
34
import 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
410
import NIO
511
import NIOConcurrencyHelpers
612

713
public 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-

Sources/VaporAPNS/Application+APNS.swift

+1-7
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,12 @@
11
import APNS
2-
import NIOConcurrencyHelpers
32
import Vapor
43

54
extension Application {
65
public var apns: APNS {
76
return .init(application: self)
87
}
98

10-
public struct APNS {
11-
12-
// Synchronize access across threads.
13-
private var lock: NIOLock
14-
9+
public struct APNS: Sendable {
1510
struct ContainersKey: StorageKey, LockKey {
1611
typealias Value = APNSContainers
1712
}
@@ -49,7 +44,6 @@ extension Application {
4944

5045
public init(application: Application) {
5146
self.application = application
52-
self.lock = .init()
5347
}
5448
}
5549
}

Sources/VaporAPNS/Request+APNS.swift

-1
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,3 @@ extension Request {
66
.init(application: self.application)
77
}
88
}
9-

0 commit comments

Comments
 (0)