diff --git a/Package.swift b/Package.swift index 51fc057..1b658ae 100644 --- a/Package.swift +++ b/Package.swift @@ -3,9 +3,14 @@ import PackageDescription +let approachableConcurrency: [SwiftSetting] = [ + .enableUpcomingFeature("NonisolatedNonsendingByDefault"), + .enableUpcomingFeature("InferIsolatedConformances") +] + let package = Package( name: "SimpleHTTP", - platforms: [.iOS(.v13), .macOS(.v10_15)], + platforms: [.iOS(.v13), .macOS(.v13)], products: [ .library(name: "SimpleHTTPFoundation", targets: ["SimpleHTTPFoundation"]), .library(name: "SimpleHTTP", targets: ["SimpleHTTP"]) @@ -13,16 +18,21 @@ let package = Package( dependencies: [ ], targets: [ - .target(name: "SimpleHTTPFoundation", dependencies: []), - .target(name: "SimpleHTTP", dependencies: ["SimpleHTTPFoundation"]), - .testTarget(name: "SimpleHTTPFoundationTests", dependencies: ["SimpleHTTPFoundation"]), + .target(name: "SimpleHTTPFoundation", dependencies: [], swiftSettings: approachableConcurrency), + .target(name: "SimpleHTTP", dependencies: ["SimpleHTTPFoundation"], swiftSettings: approachableConcurrency), + .testTarget( + name: "SimpleHTTPFoundationTests", + dependencies: ["SimpleHTTPFoundation"], + swiftSettings: approachableConcurrency + ), .testTarget( name: "SimpleHTTPTests", dependencies: ["SimpleHTTP"], resources: [ .copy("Ressources/Images/swift.png"), .copy("Ressources/Images/swiftUI.png") - ] + ], + swiftSettings: approachableConcurrency ) ] ) diff --git a/Sources/SimpleHTTP/ContentData/ContentDataCodersConfiguration.swift b/Sources/SimpleHTTP/ContentData/ContentDataCodersConfiguration.swift index b077577..d6fdef5 100644 --- a/Sources/SimpleHTTP/ContentData/ContentDataCodersConfiguration.swift +++ b/Sources/SimpleHTTP/ContentData/ContentDataCodersConfiguration.swift @@ -4,7 +4,7 @@ public typealias ContentDataEncodersConfiguration = [HTTPContentType: ContentDat public typealias ContentDataDecodersConfiguration = [HTTPContentType: ContentDataDecoder] /// Defines the list of encoders and decoders to use. -public struct ContentDataCodersConfiguration { +public struct ContentDataCodersConfiguration: Sendable { public var encoders: ContentDataEncodersConfiguration public var decoders: ContentDataDecodersConfiguration public let defaultType: HTTPContentType diff --git a/Sources/SimpleHTTP/Interceptor/Interceptor.swift b/Sources/SimpleHTTP/Interceptor/Interceptor.swift index f64ea37..6dfbf38 100644 --- a/Sources/SimpleHTTP/Interceptor/Interceptor.swift +++ b/Sources/SimpleHTTP/Interceptor/Interceptor.swift @@ -3,7 +3,7 @@ import Foundation public typealias Interceptor = RequestInterceptor & ResponseInterceptor /// a protocol intercepting a session request -public protocol RequestInterceptor { +public protocol RequestInterceptor: Sendable { /// Should be called before making the request to provide modifications to `request` func adaptRequest(_ request: Request) async throws -> Request @@ -14,7 +14,7 @@ public protocol RequestInterceptor { } /// a protocol intercepting a session response -public protocol ResponseInterceptor { +public protocol ResponseInterceptor: Sendable { /// Should be called once the request is done and output was received. Let one last chance to modify the output /// optionally throwing an error instead if needed /// - Parameter request: the request that was sent to the server diff --git a/Sources/SimpleHTTP/Session/Session.swift b/Sources/SimpleHTTP/Session/Session.swift index e6fa124..139a525 100644 --- a/Sources/SimpleHTTP/Session/Session.swift +++ b/Sources/SimpleHTTP/Session/Session.swift @@ -1,9 +1,9 @@ import Foundation /// Primary class of the library used to perform http request using a `Request` object -public class Session { +final public class Session: Sendable { /// a function returning a `RequestData` from a `URLRequest` - public typealias URLRequestTask = (URLRequest) async throws -> URLDataResponse + public typealias URLRequestTask = @Sendable (URLRequest) async throws -> URLDataResponse let baseURL: URL let config: SessionConfiguration diff --git a/Sources/SimpleHTTP/Session/SessionConfiguration.swift b/Sources/SimpleHTTP/Session/SessionConfiguration.swift index 957f3de..27537a0 100644 --- a/Sources/SimpleHTTP/Session/SessionConfiguration.swift +++ b/Sources/SimpleHTTP/Session/SessionConfiguration.swift @@ -1,7 +1,7 @@ import Foundation /// a type defining some parameters for a `Session` -public struct SessionConfiguration { +public struct SessionConfiguration: Sendable { /// data encoders/decoders configuration per content type let data: ContentDataCodersConfiguration /// an interceptor to apply custom behavior on the session requests/responses. diff --git a/Sources/SimpleHTTPFoundation/Foundation/Coder/DataCoder.swift b/Sources/SimpleHTTPFoundation/Foundation/Coder/DataCoder.swift index 83067c4..9468051 100644 --- a/Sources/SimpleHTTPFoundation/Foundation/Coder/DataCoder.swift +++ b/Sources/SimpleHTTPFoundation/Foundation/Coder/DataCoder.swift @@ -1,12 +1,12 @@ import Foundation /// A encoder suited to encode to Data -public protocol DataEncoder { +public protocol DataEncoder: Sendable { func encode(_ value: T) throws -> Data } /// A decoder suited to decode Data -public protocol DataDecoder { +public protocol DataDecoder: Sendable { func decode(_ type: T.Type, from: Data) throws -> T } @@ -23,6 +23,6 @@ public protocol ContentDataDecoder: DataDecoder { } /// A function converting data when a http error occur into a custom error -public typealias DataErrorDecoder = (Data) throws -> Error +public typealias DataErrorDecoder = @Sendable (Data) throws -> Error -public typealias ContentDataErrorDecoder = (Data, HTTPContentType) throws -> Error +public typealias ContentDataErrorDecoder = @Sendable (Data, HTTPContentType) throws -> Error diff --git a/Tests/SimpleHTTPTests/Interceptor/CompositeInterceptorTests.swift b/Tests/SimpleHTTPTests/Interceptor/CompositeInterceptorTests.swift index 0aefa05..27f40af 100644 --- a/Tests/SimpleHTTPTests/Interceptor/CompositeInterceptorTests.swift +++ b/Tests/SimpleHTTPTests/Interceptor/CompositeInterceptorTests.swift @@ -20,7 +20,7 @@ class CompositeInterceptorTests: XCTestCase { } private struct InterceptorStub: Interceptor { - var shouldRequestMock: (Error) throws -> Bool = { _ in false } + var shouldRequestMock: @Sendable (Error) throws -> Bool = { _ in false } func shouldRescueRequest(_ request: Request, error: Error) async throws -> Bool { try shouldRequestMock(error) @@ -38,4 +38,4 @@ private struct InterceptorStub: Interceptor { } -} \ No newline at end of file +} diff --git a/Tests/SimpleHTTPTests/Session/SessionTests.swift b/Tests/SimpleHTTPTests/Session/SessionTests.swift index b32f646..9ab8bcb 100644 --- a/Tests/SimpleHTTPTests/Session/SessionTests.swift +++ b/Tests/SimpleHTTPTests/Session/SessionTests.swift @@ -1,6 +1,7 @@ import XCTest @testable import SimpleHTTP +@MainActor class SessionAsyncTests: XCTestCase { let baseURL = URL(string: "https://sessionTests.io")! let data = ContentDataCodersConfiguration( @@ -41,7 +42,7 @@ class SessionAsyncTests: XCTestCase { } func test_response_rescue_rescueIsSuccess_itRetryRequest() async throws { - var isRescued = false + nonisolated(unsafe) var isRescued = false let interceptor = InterceptorStub() let session = sesssionStub(interceptor: [interceptor]) { URLDataResponse(data: Data(), response: isRescued ? .success : .unauthorized) @@ -91,8 +92,10 @@ class SessionAsyncTests: XCTestCase { } /// helper to create a session for testing - private func sesssionStub(interceptor: CompositeInterceptor = [], response: @escaping () throws -> URLDataResponse) - -> Session { + private func sesssionStub( + interceptor: CompositeInterceptor = [], + response: @Sendable @escaping () throws -> URLDataResponse + ) -> Session { let config = SessionConfiguration(data: data, interceptors: interceptor) return Session(baseURL: baseURL, configuration: config, dataTask: { _ in try response() }) @@ -121,10 +124,10 @@ private extension Request { } } -private class InterceptorStub: Interceptor { - var rescueRequestErrorMock: (Error) throws -> Bool = { _ in false } - var receivedResponseMock: ((Any, Any) -> Void)? - var adaptResponseMock: ((Any, Any) throws -> Any)? +final private class InterceptorStub: Interceptor, @unchecked Sendable { + var rescueRequestErrorMock: @Sendable (Error) throws -> Bool = { _ in false } + var receivedResponseMock: (@Sendable (Any, Any) -> Void)? + var adaptResponseMock: (@Sendable (Any, Any) throws -> Any)? func adaptRequest(_ request: Request) -> Request { request