diff --git a/Package.swift b/Package.swift
index 1255b1c..16be457 100644
--- a/Package.swift
+++ b/Package.swift
@@ -17,7 +17,7 @@ let package = Package(
.library(name: "NetworkServer", targets: ["NetworkServer"]),
],
dependencies: [
- .package(url: "https://github.com/luizmb/FP.git", from: "1.7.0"),
+ .package(url: "https://github.com/luizmb/FP.git", from: "1.8.1"),
.package(url: "https://github.com/apple/swift-nio.git", from: "2.65.0"),
.package(url: "https://github.com/swiftlang/swift-docc-plugin", from: "1.4.0"),
],
diff --git a/Sources/HTMLTemplating/TemplateEngine.swift b/Sources/HTMLTemplating/TemplateEngine.swift
index 87d86a8..0a696a1 100644
--- a/Sources/HTMLTemplating/TemplateEngine.swift
+++ b/Sources/HTMLTemplating/TemplateEngine.swift
@@ -62,7 +62,7 @@ public struct HTMLEnvironment {
public typealias Context = [String: TemplateValue]
-public indirect enum TemplateValue {
+public indirect enum TemplateValue: Sendable {
case string(String)
case list([Context])
case bool(Bool)
diff --git a/Sources/NetworkClient/RequestPublisher+Applicative.swift b/Sources/NetworkClient/RequestPublisher+Applicative.swift
index 9f2ba3d..b25f59b 100644
--- a/Sources/NetworkClient/RequestPublisher+Applicative.swift
+++ b/Sources/NetworkClient/RequestPublisher+Applicative.swift
@@ -6,7 +6,7 @@ import Foundation
public extension RequestPublisher {
/// Lifts a pure value into `RequestPublisher`, ignoring the `URLRequest`.
- static func pure(_ value: A) -> RequestPublisher {
+ static func pure(_ value: A) -> RequestPublisher where A: Sendable {
RequestPublisher { _ in
Just(value).setFailureType(to: HTTPError.self).eraseToAnyPublisher()
}
@@ -14,7 +14,7 @@ public extension RequestPublisher {
/// Applies a request-dependent function to a request-dependent value.
/// Both are run against the same `URLRequest` and zipped.
- static func apply(_ f: RequestPublisher<(A) -> B>, _ r: RequestPublisher) -> RequestPublisher {
+ static func apply(_ f: RequestPublisher<@Sendable (A) -> B>, _ r: RequestPublisher) -> RequestPublisher {
RequestPublisher { request in
f.run(request)
.zip(r.run(request))
diff --git a/Sources/NetworkClient/RequestPublisher+ApplicativeOperators.swift b/Sources/NetworkClient/RequestPublisher+ApplicativeOperators.swift
index a0aa8d3..0d2f489 100644
--- a/Sources/NetworkClient/RequestPublisher+ApplicativeOperators.swift
+++ b/Sources/NetworkClient/RequestPublisher+ApplicativeOperators.swift
@@ -6,7 +6,7 @@ import FP
// MARK: - RequestPublisher Applicative Operators
// (<*>) :: RequestPublisher<(a -> b)> -> RequestPublisher -> RequestPublisher
-public func <*> (_ f: RequestPublisher<(A) -> B>, _ r: RequestPublisher) -> RequestPublisher {
+public func <*> (_ f: RequestPublisher<@Sendable (A) -> B>, _ r: RequestPublisher) -> RequestPublisher {
RequestPublisher.apply(f, r)
}
diff --git a/Sources/NetworkClient/RequestPublisher+Functor.swift b/Sources/NetworkClient/RequestPublisher+Functor.swift
index 60c81e7..e96063b 100644
--- a/Sources/NetworkClient/RequestPublisher+Functor.swift
+++ b/Sources/NetworkClient/RequestPublisher+Functor.swift
@@ -6,22 +6,22 @@ import Foundation
public extension RequestPublisher {
/// Transforms the output value; the same `URLRequest` is threaded through unchanged.
- func map(_ f: @escaping (A) -> B) -> RequestPublisher {
+ func map(_ f: @escaping @Sendable (A) -> B) -> RequestPublisher {
RequestPublisher { request in run(request).map(f).eraseToAnyPublisher() }
}
/// Curried fmap for point-free composition.
- static func fmap(_ f: @escaping (A) -> B) -> (RequestPublisher) -> RequestPublisher {
+ static func fmap(_ f: @escaping @Sendable (A) -> B) -> @Sendable (RequestPublisher) -> RequestPublisher {
{ $0.map(f) }
}
/// Replaces the output with a constant value.
- func replace(with value: B) -> RequestPublisher {
- map { _ in value }
+ func replace(with value: B) -> RequestPublisher {
+ map { @Sendable _ in value }
}
/// Maps the failure side of the underlying publisher.
- func mapError(_ f: @escaping (HTTPError) -> HTTPError) -> RequestPublisher {
+ func mapError(_ f: @escaping @Sendable (HTTPError) -> HTTPError) -> RequestPublisher {
RequestPublisher { request in run(request).mapError(f).eraseToAnyPublisher() }
}
}
diff --git a/Sources/NetworkClient/RequestPublisher+FunctorOperators.swift b/Sources/NetworkClient/RequestPublisher+FunctorOperators.swift
index 05e36e8..8a4e8aa 100644
--- a/Sources/NetworkClient/RequestPublisher+FunctorOperators.swift
+++ b/Sources/NetworkClient/RequestPublisher+FunctorOperators.swift
@@ -6,23 +6,23 @@ import FP
// MARK: - RequestPublisher Functor Operators
// (<$>) :: (a -> b) -> RequestPublisher -> RequestPublisher
-public func <£> (_ f: @escaping (A) -> B, _ r: RequestPublisher) -> RequestPublisher {
+public func <£> (_ f: @escaping @Sendable (A) -> B, _ r: RequestPublisher) -> RequestPublisher {
r.map(f)
}
// (<&>) :: RequestPublisher -> (a -> b) -> RequestPublisher
-public func <&> (_ r: RequestPublisher, _ f: @escaping (A) -> B) -> RequestPublisher {
+public func <&> (_ r: RequestPublisher, _ f: @escaping @Sendable (A) -> B) -> RequestPublisher {
r.map(f)
}
// ($>) :: RequestPublisher -> b -> RequestPublisher
// swiftlint:disable:next identifier_name
-public func £> (_ r: RequestPublisher, _ value: B) -> RequestPublisher {
+public func £> (_ r: RequestPublisher, _ value: B) -> RequestPublisher {
r.replace(with: value)
}
// (<$) :: b -> RequestPublisher -> RequestPublisher
-public func <£ (_ value: B, _ r: RequestPublisher) -> RequestPublisher {
+public func <£ (_ value: B, _ r: RequestPublisher) -> RequestPublisher {
r £> value
}
#endif
diff --git a/Sources/NetworkClient/RequestPublisher+Monad.swift b/Sources/NetworkClient/RequestPublisher+Monad.swift
index 02016e1..b1aac40 100644
--- a/Sources/NetworkClient/RequestPublisher+Monad.swift
+++ b/Sources/NetworkClient/RequestPublisher+Monad.swift
@@ -7,7 +7,7 @@ import Foundation
public extension RequestPublisher {
/// Chains two `RequestPublisher`s, threading the same `URLRequest` through both
/// (Reader + Publisher monad stack).
- func flatMap(_ f: @escaping (A) -> RequestPublisher) -> RequestPublisher {
+ func flatMap(_ f: @escaping @Sendable (A) -> RequestPublisher) -> RequestPublisher {
RequestPublisher { request in
self.run(request)
.flatMap { a in f(a).run(request) }
@@ -16,7 +16,7 @@ public extension RequestPublisher {
}
/// Curried bind for point-free composition.
- static func bind(_ f: @escaping (A) -> RequestPublisher) -> (RequestPublisher) -> RequestPublisher {
+ static func bind(_ f: @escaping @Sendable (A) -> RequestPublisher) -> @Sendable (RequestPublisher) -> RequestPublisher {
{ $0.flatMap(f) }
}
@@ -32,23 +32,23 @@ public extension RequestPublisher {
/// Kleisli composition (left-to-right): `(X -> m A) >=> (A -> m B) = X -> m B`.
static func kleisli(
- _ f: @escaping (X) -> RequestPublisher,
- _ g: @escaping (A) -> RequestPublisher
- ) -> (X) -> RequestPublisher {
+ _ f: @escaping @Sendable (X) -> RequestPublisher,
+ _ g: @escaping @Sendable (A) -> RequestPublisher
+ ) -> @Sendable (X) -> RequestPublisher {
{ x in f(x).flatMap(g) }
}
/// Kleisli composition (right-to-left).
static func kleisliBack(
- _ g: @escaping (A) -> RequestPublisher,
- _ f: @escaping (X) -> RequestPublisher
- ) -> (X) -> RequestPublisher {
+ _ g: @escaping @Sendable (A) -> RequestPublisher,
+ _ f: @escaping @Sendable (X) -> RequestPublisher
+ ) -> @Sendable (X) -> RequestPublisher {
RequestPublisher.kleisli(f, g)
}
/// Recovers from failure by running an alternative `RequestPublisher`
/// against the same `URLRequest`.
- func flatMapError(_ f: @escaping (HTTPError) -> RequestPublisher) -> RequestPublisher {
+ func flatMapError(_ f: @escaping @Sendable (HTTPError) -> RequestPublisher) -> RequestPublisher {
RequestPublisher { request in
self.run(request)
.catch { error in f(error).run(request) }
diff --git a/Sources/NetworkClient/RequestPublisher+MonadOperators.swift b/Sources/NetworkClient/RequestPublisher+MonadOperators.swift
index 4291cd1..f121331 100644
--- a/Sources/NetworkClient/RequestPublisher+MonadOperators.swift
+++ b/Sources/NetworkClient/RequestPublisher+MonadOperators.swift
@@ -6,28 +6,28 @@ import FP
// MARK: - RequestPublisher Monad Operators
// (>>-) :: RequestPublisher -> (a -> RequestPublisher) -> RequestPublisher
-public func >>- (_ r: RequestPublisher, _ f: @escaping (A) -> RequestPublisher) -> RequestPublisher {
+public func >>- (_ r: RequestPublisher, _ f: @escaping @Sendable (A) -> RequestPublisher) -> RequestPublisher {
r.flatMap(f)
}
// (-<<) :: (a -> RequestPublisher) -> RequestPublisher -> RequestPublisher
-public func -<< (_ f: @escaping (A) -> RequestPublisher, _ r: RequestPublisher) -> RequestPublisher {
+public func -<< (_ f: @escaping @Sendable (A) -> RequestPublisher, _ r: RequestPublisher) -> RequestPublisher {
r.flatMap(f)
}
// (>=>) :: (x -> RequestPublisher) -> (a -> RequestPublisher) -> x -> RequestPublisher
public func >=> (
- _ f: @escaping (X) -> RequestPublisher,
- _ g: @escaping (A) -> RequestPublisher
-) -> (X) -> RequestPublisher {
+ _ f: @escaping @Sendable (X) -> RequestPublisher,
+ _ g: @escaping @Sendable (A) -> RequestPublisher
+) -> @Sendable (X) -> RequestPublisher {
RequestPublisher.kleisli(f, g)
}
// (<=<) :: (a -> RequestPublisher) -> (x -> RequestPublisher) -> x -> RequestPublisher
public func <=< (
- _ g: @escaping (A) -> RequestPublisher,
- _ f: @escaping (X) -> RequestPublisher
-) -> (X) -> RequestPublisher {
+ _ g: @escaping @Sendable (A) -> RequestPublisher,
+ _ f: @escaping @Sendable (X) -> RequestPublisher
+) -> @Sendable (X) -> RequestPublisher {
RequestPublisher.kleisliBack(g, f)
}
#endif
diff --git a/Sources/NetworkClient/RequestPublisher.swift b/Sources/NetworkClient/RequestPublisher.swift
index 8aba61e..b50b5e4 100644
--- a/Sources/NetworkClient/RequestPublisher.swift
+++ b/Sources/NetworkClient/RequestPublisher.swift
@@ -8,9 +8,9 @@ import FP
/// Combines the Reader monad (threading the same `URLRequest` through a chain)
/// with the Publisher monad (async response streaming with typed errors).
public struct RequestPublisher: FunctionWrapper {
- public let run: (URLRequest) -> AnyPublisher
+ public let run: @Sendable (URLRequest) -> AnyPublisher
- public init(_ fn: @escaping (URLRequest) -> AnyPublisher) {
+ public init(_ fn: @escaping @Sendable (URLRequest) -> AnyPublisher) {
run = fn
}
diff --git a/Tests/NetworkClientTests/NetworkClientTests.swift b/Tests/NetworkClientTests/NetworkClientTests.swift
index d4397f8..a644dee 100644
--- a/Tests/NetworkClientTests/NetworkClientTests.swift
+++ b/Tests/NetworkClientTests/NetworkClientTests.swift
@@ -44,7 +44,7 @@ private func firstResult(of publisher: AnyPublisher) -> Resul
// MARK: - RequestPublisher builders
-private func just(_ value: A) -> RequestPublisher {
+private func just(_ value: A) -> RequestPublisher {
RequestPublisher { _ in Just(value).setFailureType(to: HTTPError.self).eraseToAnyPublisher() }
}
@@ -284,17 +284,17 @@ struct RequestPublisherFunctorTests {
@Suite("RequestPublisher — Applicative")
struct RequestPublisherApplicativeTests {
@Test func apply_combinesFunctionAndValue() {
- let f = RequestPublisher<(Int) -> String>.pure(String.init)
+ let f = RequestPublisher<@Sendable (Int) -> String>.pure(String.init)
#expect(run(RequestPublisher.apply(f, just(42)))?.successValue == "42")
}
@Test func apply_propagatesLeftFailure() {
- let f = fail(.badStatus(500, Data())) as RequestPublisher<(Int) -> Int>
+ let f = fail(.badStatus(500, Data())) as RequestPublisher<@Sendable (Int) -> Int>
#expect(run(RequestPublisher.apply(f, just(1)))?.isFailure == true)
}
@Test func apply_propagatesRightFailure() {
- let f = just { (n: Int) in n + 1 }
+ let f = just({ @Sendable (n: Int) in n + 1 } as @Sendable (Int) -> Int)
let a = fail(.badStatus(500, Data())) as RequestPublisher
#expect(run(RequestPublisher.apply(f, a))?.isFailure == true)
}
@@ -326,14 +326,14 @@ struct RequestPublisherMonadTests {
}
@Test func kleisli_composes() {
- let f: (String) -> RequestPublisher = { s in just(s.count) }
- let g: (Int) -> RequestPublisher = { n in just("\(n)") }
+ let f: @Sendable (String) -> RequestPublisher = { s in just(s.count) }
+ let g: @Sendable (Int) -> RequestPublisher = { n in just("\(n)") }
#expect(run(RequestPublisher.kleisli(f, g)("hello"))?.successValue == "5")
}
@Test func kleisliBack_composes() {
- let f: (String) -> RequestPublisher = { s in just(s.count) }
- let g: (Int) -> RequestPublisher = { n in just("\(n)") }
+ let f: @Sendable (String) -> RequestPublisher = { s in just(s.count) }
+ let g: @Sendable (Int) -> RequestPublisher = { n in just("\(n)") }
#expect(run(RequestPublisher.kleisliBack(g, f)("hello"))?.successValue == "5")
}
@@ -356,7 +356,7 @@ struct RequestPublisherOperatorTests {
@Test func replaceLeftOp() { #expect(run("r" <£ just(5))?.successValue == "r") }
@Test func applyOp() {
- let f = just { (n: Int) in n + 1 }
+ let f = just({ @Sendable (n: Int) in n + 1 } as @Sendable (Int) -> Int)
#expect(run(f <*> just(41))?.successValue == 42)
}
@Test func seqRightOp() { #expect(run(just(1) *> just("k"))?.successValue == "k") }
@@ -366,13 +366,13 @@ struct RequestPublisherOperatorTests {
@Test func flippedBindOp() { #expect(run({ n in just(n * n) } -<< just(3))?.successValue == 9) }
@Test func kleisliOp() {
- let f: (Int) -> RequestPublisher = { n in just(n + 1) }
- let g: (Int) -> RequestPublisher = { n in just("\(n)") }
+ let f: @Sendable (Int) -> RequestPublisher = { n in just(n + 1) }
+ let g: @Sendable (Int) -> RequestPublisher = { n in just("\(n)") }
#expect(run((f >=> g)(41))?.successValue == "42")
}
@Test func kleisliBackOp() {
- let f: (Int) -> RequestPublisher = { n in just(n + 1) }
- let g: (Int) -> RequestPublisher = { n in just("\(n)") }
+ let f: @Sendable (Int) -> RequestPublisher = { n in just(n + 1) }
+ let g: @Sendable (Int) -> RequestPublisher = { n in just("\(n)") }
#expect(run((g <=< f)(41))?.successValue == "42")
}
}