From 209c62f2f2b1eb9af6dcfb4f3d6983aad01af9dc Mon Sep 17 00:00:00 2001 From: Kyle Browning Date: Tue, 20 Aug 2024 11:28:29 -0700 Subject: [PATCH 1/6] updating for strict concurrency --- Package.swift | 23 ++++++++++++++++++++--- Sources/APNS/APNSClient.swift | 7 +------ Sources/APNSCore/APNSClient.swift | 3 +++ Sources/APNSExample/Program.swift | 29 ++++++++++++++++++++--------- 4 files changed, 44 insertions(+), 18 deletions(-) diff --git a/Package.swift b/Package.swift index 27acfe6e..bb0f66a7 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version:5.7 +// swift-tools-version:5.9 import PackageDescription let package = Package( @@ -31,17 +31,25 @@ let package = Package( dependencies: [ .target(name: "APNSCore"), .target(name: "APNS"), - ]), + ] + ), .testTarget( name: "APNSTests", dependencies: [ .target(name: "APNSCore"), .target(name: "APNS"), - ]), + ], + swiftSettings: [ + .enableExperimentalFeature("StrictConcurrency") + ] + ), .target( name: "APNSCore", dependencies: [ .product(name: "Crypto", package: "swift-crypto"), + ], + swiftSettings: [ + .enableExperimentalFeature("StrictConcurrency") ] ), .target( @@ -50,6 +58,9 @@ let package = Package( .product(name: "Crypto", package: "swift-crypto"), .product(name: "AsyncHTTPClient", package: "async-http-client"), .target(name: "APNSCore"), + ], + swiftSettings: [ + .enableExperimentalFeature("StrictConcurrency") ] ), .target( @@ -62,12 +73,18 @@ let package = Package( .product(name: "NIOSSL", package: "swift-nio-ssl"), .product(name: "NIOHTTP1", package: "swift-nio"), .product(name: "NIOHTTP2", package: "swift-nio-http2"), + ], + swiftSettings: [ + .enableExperimentalFeature("StrictConcurrency") ] ), .target( name: "APNSURLSession", dependencies: [ .target(name: "APNSCore"), + ], + swiftSettings: [ + .enableExperimentalFeature("StrictConcurrency") ] ), ] diff --git a/Sources/APNS/APNSClient.swift b/Sources/APNS/APNSClient.swift index 2e648ede..7c318f96 100644 --- a/Sources/APNS/APNSClient.swift +++ b/Sources/APNS/APNSClient.swift @@ -125,14 +125,9 @@ public final class APNSClient Void) { + public func shutdown(queue: DispatchQueue = .global(), callback: @Sendable @escaping (Error?) -> Void) { self.httpClient.shutdown(callback) } - - /// Shuts down the client and `EventLoopGroup` if it was created by the client. - public func syncShutdown() throws { - try self.httpClient.syncShutdown() - } } extension APNSClient: Sendable where Decoder: Sendable, Encoder: Sendable {} diff --git a/Sources/APNSCore/APNSClient.swift b/Sources/APNSCore/APNSClient.swift index 8c1b5a2e..4efe6916 100644 --- a/Sources/APNSCore/APNSClient.swift +++ b/Sources/APNSCore/APNSClient.swift @@ -12,6 +12,9 @@ // //===----------------------------------------------------------------------===// +import Dispatch + public protocol APNSClientProtocol { func send(_ request: APNSRequest) async throws -> APNSResponse + func shutdown(queue: DispatchQueue, callback: @Sendable @escaping (Error?) -> Void) } diff --git a/Sources/APNSExample/Program.swift b/Sources/APNSExample/Program.swift index 6c1c335c..b4406ff2 100644 --- a/Sources/APNSExample/Program.swift +++ b/Sources/APNSExample/Program.swift @@ -14,6 +14,7 @@ import APNSCore import APNS +import OSLog import Foundation @available(macOS 11.0, *) @@ -30,6 +31,7 @@ struct Main { static let teamIdentifier = "" static func main() async throws { + let logger = Logger(subsystem: "apns", category: "apns-main") let client = APNSClient( configuration: .init( authenticationMethod: .jwt( @@ -43,15 +45,24 @@ struct Main { responseDecoder: JSONDecoder(), requestEncoder: JSONEncoder() ) - - try await Self.sendSimpleAlert(with: client) - try await Self.sendLocalizedAlert(with: client) - try await Self.sendThreadedAlert(with: client) - try await Self.sendCustomCategoryAlert(with: client) - try await Self.sendMutableContentAlert(with: client) - try await Self.sendBackground(with: client) - try await Self.sendVoIP(with: client) - try await Self.sendFileProvider(with: client) + do { + try await Self.sendSimpleAlert(with: client) + try await Self.sendLocalizedAlert(with: client) + try await Self.sendThreadedAlert(with: client) + try await Self.sendCustomCategoryAlert(with: client) + try await Self.sendMutableContentAlert(with: client) + try await Self.sendBackground(with: client) + try await Self.sendVoIP(with: client) + try await Self.sendFileProvider(with: client) + } catch { + logger.warning("error sending push:\(error)") + } + + client.shutdown { error in + if let error = error { + logger.warning("error shutting down client: \(error.localizedDescription)") + } + } } } From 25e1447e7b1660074cf9cf1b9911ef015e97efa4 Mon Sep 17 00:00:00 2001 From: Kyle Browning Date: Tue, 20 Aug 2024 12:01:55 -0700 Subject: [PATCH 2/6] Updates --- .github/workflows/swift.yml | 6 +++--- Package.swift | 3 ++- Sources/APNS/APNSClient.swift | 16 +++------------- Sources/APNSCore/APNSClient.swift | 2 +- Sources/APNSExample/Program.swift | 14 ++++++-------- 5 files changed, 15 insertions(+), 26 deletions(-) diff --git a/.github/workflows/swift.yml b/.github/workflows/swift.yml index cb4c4f62..2bd8b5e9 100644 --- a/.github/workflows/swift.yml +++ b/.github/workflows/swift.yml @@ -4,21 +4,21 @@ on: jobs: focal: container: - image: swift:5.7-focal + image: swift:6.0-focal runs-on: ubuntu-latest steps: - uses: actions/checkout@v1 - run: swift test --enable-test-discovery thread: container: - image: swift:5.7-focal + image: swift:6.0-focal runs-on: ubuntu-latest steps: - uses: actions/checkout@v1 - run: swift test --enable-test-discovery --sanitize=thread address: container: - image: swift:5.7-focal + image: swift:6.0-focal runs-on: ubuntu-latest steps: - uses: actions/checkout@v1 diff --git a/Package.swift b/Package.swift index bb0f66a7..c3956c59 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version:5.9 +// swift-tools-version:6.0 import PackageDescription let package = Package( @@ -31,6 +31,7 @@ let package = Package( dependencies: [ .target(name: "APNSCore"), .target(name: "APNS"), + .product(name: "Logging", package: "swift-log"), ] ), .testTarget( diff --git a/Sources/APNS/APNSClient.swift b/Sources/APNS/APNSClient.swift index 7c318f96..a0e1dfd3 100644 --- a/Sources/APNS/APNSClient.swift +++ b/Sources/APNS/APNSClient.swift @@ -114,19 +114,9 @@ public final class APNSClient Void) { - self.httpClient.shutdown(callback) + /// Shuts down the client and event loop gracefully. + public func shutdown() async throws { + try await self.httpClient.shutdown() } } diff --git a/Sources/APNSCore/APNSClient.swift b/Sources/APNSCore/APNSClient.swift index 4efe6916..0cf89f26 100644 --- a/Sources/APNSCore/APNSClient.swift +++ b/Sources/APNSCore/APNSClient.swift @@ -16,5 +16,5 @@ import Dispatch public protocol APNSClientProtocol { func send(_ request: APNSRequest) async throws -> APNSResponse - func shutdown(queue: DispatchQueue, callback: @Sendable @escaping (Error?) -> Void) + func shutdown() async throws } diff --git a/Sources/APNSExample/Program.swift b/Sources/APNSExample/Program.swift index b4406ff2..e3192799 100644 --- a/Sources/APNSExample/Program.swift +++ b/Sources/APNSExample/Program.swift @@ -14,7 +14,7 @@ import APNSCore import APNS -import OSLog +import Logging import Foundation @available(macOS 11.0, *) @@ -30,8 +30,10 @@ struct Main { static let keyIdentifier = "" static let teamIdentifier = "" + private static let logger = Logger(label: "APNSwiftExample") + static func main() async throws { - let logger = Logger(subsystem: "apns", category: "apns-main") + let client = APNSClient( configuration: .init( authenticationMethod: .jwt( @@ -55,14 +57,10 @@ struct Main { try await Self.sendVoIP(with: client) try await Self.sendFileProvider(with: client) } catch { - logger.warning("error sending push:\(error)") + logger.warning("error sending push: \(error)") } - client.shutdown { error in - if let error = error { - logger.warning("error shutting down client: \(error.localizedDescription)") - } - } + try? await client.shutdown() } } From 7e51a0d3e0272f1d8c5e4c5765cd2ef2bcba1ceb Mon Sep 17 00:00:00 2001 From: Kyle Browning Date: Tue, 20 Aug 2024 12:06:42 -0700 Subject: [PATCH 3/6] update workflows --- .github/workflows/swift.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/swift.yml b/.github/workflows/swift.yml index 2bd8b5e9..364c19a4 100644 --- a/.github/workflows/swift.yml +++ b/.github/workflows/swift.yml @@ -4,21 +4,21 @@ on: jobs: focal: container: - image: swift:6.0-focal + image: swiftlang/swift:nightly-6.0-focal runs-on: ubuntu-latest steps: - uses: actions/checkout@v1 - run: swift test --enable-test-discovery thread: container: - image: swift:6.0-focal + image: swiftlang/swift:nightly-6.0-focal runs-on: ubuntu-latest steps: - uses: actions/checkout@v1 - run: swift test --enable-test-discovery --sanitize=thread address: container: - image: swift:6.0-focal + image: swiftlang/swift:nightly-6.0-focal runs-on: ubuntu-latest steps: - uses: actions/checkout@v1 From a0be13d06b75a4eb0289d37b62bc50ab9f80219a Mon Sep 17 00:00:00 2001 From: Kyle Browning Date: Tue, 20 Aug 2024 14:39:17 -0700 Subject: [PATCH 4/6] pr feedback --- .github/workflows/swift.yml | 6 +++--- Package.swift | 18 ++---------------- Sources/APNS/APNSClient.swift | 1 - Sources/APNSCore/APNSClient.swift | 2 -- Sources/APNSExample/Program.swift | 12 ++++++++++++ 5 files changed, 17 insertions(+), 22 deletions(-) diff --git a/.github/workflows/swift.yml b/.github/workflows/swift.yml index 364c19a4..cf02ac7f 100644 --- a/.github/workflows/swift.yml +++ b/.github/workflows/swift.yml @@ -8,18 +8,18 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v1 - - run: swift test --enable-test-discovery + - run: swift test thread: container: image: swiftlang/swift:nightly-6.0-focal runs-on: ubuntu-latest steps: - uses: actions/checkout@v1 - - run: swift test --enable-test-discovery --sanitize=thread + - run: swift test --sanitize=thread address: container: image: swiftlang/swift:nightly-6.0-focal runs-on: ubuntu-latest steps: - uses: actions/checkout@v1 - - run: ASAN_OPTIONS=detect_leaks=0 swift test --enable-test-discovery --sanitize=address + - run: ASAN_OPTIONS=detect_leaks=0 swift test --sanitize=address diff --git a/Package.swift b/Package.swift index c3956c59..c09ab920 100644 --- a/Package.swift +++ b/Package.swift @@ -39,18 +39,12 @@ let package = Package( dependencies: [ .target(name: "APNSCore"), .target(name: "APNS"), - ], - swiftSettings: [ - .enableExperimentalFeature("StrictConcurrency") ] ), .target( name: "APNSCore", dependencies: [ .product(name: "Crypto", package: "swift-crypto"), - ], - swiftSettings: [ - .enableExperimentalFeature("StrictConcurrency") ] ), .target( @@ -59,9 +53,6 @@ let package = Package( .product(name: "Crypto", package: "swift-crypto"), .product(name: "AsyncHTTPClient", package: "async-http-client"), .target(name: "APNSCore"), - ], - swiftSettings: [ - .enableExperimentalFeature("StrictConcurrency") ] ), .target( @@ -74,19 +65,14 @@ let package = Package( .product(name: "NIOSSL", package: "swift-nio-ssl"), .product(name: "NIOHTTP1", package: "swift-nio"), .product(name: "NIOHTTP2", package: "swift-nio-http2"), - ], - swiftSettings: [ - .enableExperimentalFeature("StrictConcurrency") ] ), .target( name: "APNSURLSession", dependencies: [ .target(name: "APNSCore"), - ], - swiftSettings: [ - .enableExperimentalFeature("StrictConcurrency") ] ), - ] + ], + swiftLanguageVersions: [.v6] ) diff --git a/Sources/APNS/APNSClient.swift b/Sources/APNS/APNSClient.swift index 4842cdba..3f075c53 100644 --- a/Sources/APNS/APNSClient.swift +++ b/Sources/APNS/APNSClient.swift @@ -14,7 +14,6 @@ import APNSCore import AsyncHTTPClient -import Dispatch import struct Foundation.Date import struct Foundation.UUID import NIOConcurrencyHelpers diff --git a/Sources/APNSCore/APNSClient.swift b/Sources/APNSCore/APNSClient.swift index 0cf89f26..ee11f37f 100644 --- a/Sources/APNSCore/APNSClient.swift +++ b/Sources/APNSCore/APNSClient.swift @@ -12,8 +12,6 @@ // //===----------------------------------------------------------------------===// -import Dispatch - public protocol APNSClientProtocol { func send(_ request: APNSRequest) async throws -> APNSResponse func shutdown() async throws diff --git a/Sources/APNSExample/Program.swift b/Sources/APNSExample/Program.swift index 1c4e7537..fe4f4c71 100644 --- a/Sources/APNSExample/Program.swift +++ b/Sources/APNSExample/Program.swift @@ -17,6 +17,8 @@ import APNS import Logging import Foundation +let logger = Logger(label: "APNSwiftExample") + @available(macOS 11.0, *) @main struct Main { @@ -48,6 +50,7 @@ struct Main { responseDecoder: JSONDecoder(), requestEncoder: JSONEncoder() ) + do { try await Self.sendSimpleAlert(with: client) try await Self.sendLocalizedAlert(with: client) @@ -86,6 +89,7 @@ extension Main { ), deviceToken: self.deviceToken ) + logger.info("successfully sent simple alert notification") } static func sendLocalizedAlert(with client: some APNSClientProtocol) async throws { @@ -104,6 +108,7 @@ extension Main { ), deviceToken: self.deviceToken ) + logger.info("successfully sent alert localized notification") } static func sendThreadedAlert(with client: some APNSClientProtocol) async throws { @@ -123,6 +128,7 @@ extension Main { ), deviceToken: self.deviceToken ) + logger.info("successfully sent threaded alert") } static func sendCustomCategoryAlert(with client: some APNSClientProtocol) async throws { @@ -142,6 +148,7 @@ extension Main { ), deviceToken: self.deviceToken ) + logger.info("successfully sent custom category alert") } static func sendMutableContentAlert(with client: some APNSClientProtocol) async throws { @@ -161,6 +168,7 @@ extension Main { ), deviceToken: self.deviceToken ) + logger.info("successfully sent mutable content alert") } } @@ -177,6 +185,7 @@ extension Main { ), deviceToken: self.deviceToken ) + logger.info("successfully sent background notification") } } @@ -194,6 +203,7 @@ extension Main { ), deviceToken: self.pushKitDeviceToken ) + logger.info("successfully sent VoIP notification") } } @@ -210,6 +220,7 @@ extension Main { ), deviceToken: self.fileProviderDeviceToken ) + logger.info("successfully sent FileProvider notification") } } @@ -228,5 +239,6 @@ extension Main { ), deviceToken: self.ephemeralPushToken ) + logger.info("successfully sent Push to Talk notification") } } From 576a50c8e0d8442aacb4ffcc20300f5b46b3b7c0 Mon Sep 17 00:00:00 2001 From: Kyle Browning Date: Tue, 20 Aug 2024 15:02:14 -0700 Subject: [PATCH 5/6] fix tests --- Sources/APNSURLSession/APNSUrlSessionClient.swift | 5 +++++ Tests/APNSTests/APNSClientTests.swift | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/Sources/APNSURLSession/APNSUrlSessionClient.swift b/Sources/APNSURLSession/APNSUrlSessionClient.swift index ea0c0eac..c70c6edd 100644 --- a/Sources/APNSURLSession/APNSUrlSessionClient.swift +++ b/Sources/APNSURLSession/APNSUrlSessionClient.swift @@ -7,6 +7,7 @@ enum APNSUrlSessionClientError: Error { } public struct APNSURLSessionClient: APNSClientProtocol { + private let configuration: APNSURLSessionClientConfiguration let encoder = JSONEncoder() @@ -61,6 +62,10 @@ public struct APNSURLSessionClient: APNSClientProtocol { return APNSResponse(apnsID: apnsID, apnsUniqueID: apnsUniqueID) } } + + public func shutdown() async throws { + // no op + } } #endif diff --git a/Tests/APNSTests/APNSClientTests.swift b/Tests/APNSTests/APNSClientTests.swift index e639179d..d04679ca 100644 --- a/Tests/APNSTests/APNSClientTests.swift +++ b/Tests/APNSTests/APNSClientTests.swift @@ -18,9 +18,9 @@ import Crypto import XCTest final class APNSClientTests: XCTestCase { - func testShutdown() throws { + func testShutdown() async throws { let client = self.makeClient() - try client.syncShutdown() + try await client.shutdown() } // MARK: - Helper methods From d24cb2e54ef1922f810e6364b114880d5abb6f8c Mon Sep 17 00:00:00 2001 From: Kyle Browning Date: Wed, 21 Aug 2024 07:13:02 -0700 Subject: [PATCH 6/6] update logger --- Sources/APNS/APNSClient.swift | 2 +- Sources/APNSExample/Program.swift | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/Sources/APNS/APNSClient.swift b/Sources/APNS/APNSClient.swift index 3f075c53..e277e334 100644 --- a/Sources/APNS/APNSClient.swift +++ b/Sources/APNS/APNSClient.swift @@ -113,7 +113,7 @@ public final class APNSClient