From 1d89107a1993d75137169a5f7d22a7703e814c59 Mon Sep 17 00:00:00 2001 From: Gregory Higley Date: Sat, 8 Jan 2022 11:53:46 -0500 Subject: [PATCH 01/18] Documentation --- Sources/HTTPFluent/URLClient.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/HTTPFluent/URLClient.swift b/Sources/HTTPFluent/URLClient.swift index 2f2bf8c..32739cf 100644 --- a/Sources/HTTPFluent/URLClient.swift +++ b/Sources/HTTPFluent/URLClient.swift @@ -27,12 +27,12 @@ import Foundation let token = "xyz123" let attendee = Attendee(token: token) let client = HTTPClient(url: "https://api.patron.com/api") - client + await client .path("attendee", token) // api/attendee/xyz123 .query(7, forName: "x") // ?x=7 .authorization(bearer: jwt) .post(json: attendee) - .dataTaskPublisher(decoding: AttendeeResponse.self) + .receive(json: AttendeeResponse.self) ``` */ public struct URLClient { From d0bcafd4195432ef8019c6a0125c6d5c792e8b69 Mon Sep 17 00:00:00 2001 From: Gregory Higley Date: Sat, 8 Jan 2022 12:18:28 -0500 Subject: [PATCH 02/18] Comments and licensing --- Sources/HTTPFluent/Combine+Shim.swift | 4 +++- Sources/HTTPFluent/ConstantValue.swift | 2 ++ Sources/HTTPFluent/Decoders.swift | 4 +++- Sources/HTTPFluent/FormData.swift | 3 ++- Sources/HTTPFluent/HTTPHeaderField.swift | 3 ++- Sources/HTTPFluent/HTTPMethod.swift | 3 ++- Sources/HTTPFluent/MimeType.swift | 3 ++- Sources/HTTPFluent/Publisher.swift | 2 ++ Sources/HTTPFluent/URLClient.swift | 3 ++- Sources/HTTPFluent/URLClientProtocol+Async.swift | 3 ++- Sources/HTTPFluent/URLClientProtocol+Combine.swift | 4 +++- Sources/HTTPFluent/URLClientProtocol+Receive.swift | 4 +++- Sources/HTTPFluent/URLClientProtocol.swift | 3 ++- Sources/HTTPFluent/URLError.swift | 3 ++- Sources/HTTPFluent/URLRequestBuilder.swift | 3 ++- .../HTTPFluent/URLRequestBuilderProtocol+HTTPBody.swift | 3 ++- Sources/HTTPFluent/URLRequestBuilderProtocol+URL.swift | 3 ++- .../URLRequestBuilderProtocol+URLRequest.swift | 3 ++- Sources/HTTPFluent/URLRequestBuilderProtocol.swift | 3 ++- Sources/HTTPFluent/URLResult.swift | 2 ++ Tests/HTTPFluentTests/Const.swift | 4 +++- Tests/HTTPFluentTests/HTTPBinResponse.swift | 4 +++- Tests/HTTPFluentTests/HTTPFluentAsyncTests.swift | 4 +++- Tests/HTTPFluentTests/HTTPFluentCombineTests.swift | 9 +++++++++ Tests/HTTPFluentTests/HTTPFluentReceiveTests.swift | 4 +++- Tests/HTTPFluentTests/Slide.swift | 8 +++++--- 26 files changed, 70 insertions(+), 24 deletions(-) diff --git a/Sources/HTTPFluent/Combine+Shim.swift b/Sources/HTTPFluent/Combine+Shim.swift index 550ce0e..2409c6e 100644 --- a/Sources/HTTPFluent/Combine+Shim.swift +++ b/Sources/HTTPFluent/Combine+Shim.swift @@ -2,7 +2,9 @@ // Combine+Shim.swift // HTTPFluent // -// Created by Gregory Higley on 8/12/20. +// Created by Gregory Higley on 2020-08-12. +// Copyright © 2020 Prosumma. +// This code is licensed under the MIT license (see LICENSE for details). // import Foundation diff --git a/Sources/HTTPFluent/ConstantValue.swift b/Sources/HTTPFluent/ConstantValue.swift index a078402..7a5a3e9 100644 --- a/Sources/HTTPFluent/ConstantValue.swift +++ b/Sources/HTTPFluent/ConstantValue.swift @@ -3,6 +3,8 @@ // HttpFluent // // Created by Gregory Higley on 2020-07-18. +// Copyright © 2020 Prosumma. +// This code is licensed under the MIT license (see LICENSE for details). // import Foundation diff --git a/Sources/HTTPFluent/Decoders.swift b/Sources/HTTPFluent/Decoders.swift index b497f49..b87858c 100644 --- a/Sources/HTTPFluent/Decoders.swift +++ b/Sources/HTTPFluent/Decoders.swift @@ -2,7 +2,9 @@ // Decoder.swift // HTTPFluent // -// Created by Gregory Higley on 8/10/20. +// Created by Gregory Higley on 2020-08-12. +// Copyright © 2020 Prosumma. +// This code is licensed under the MIT license (see LICENSE for details). // import Combine diff --git a/Sources/HTTPFluent/FormData.swift b/Sources/HTTPFluent/FormData.swift index 29e380d..e693dc4 100644 --- a/Sources/HTTPFluent/FormData.swift +++ b/Sources/HTTPFluent/FormData.swift @@ -3,7 +3,8 @@ // HTTPFluent // // Created by Gregory Higley on 2020-05-09. -// Copyright © 2020 Prosumma. All rights reserved. +// Copyright © 2020 Prosumma. +// This code is licensed under the MIT license (see LICENSE for details). // import Foundation diff --git a/Sources/HTTPFluent/HTTPHeaderField.swift b/Sources/HTTPFluent/HTTPHeaderField.swift index cb3745b..00365ef 100644 --- a/Sources/HTTPFluent/HTTPHeaderField.swift +++ b/Sources/HTTPFluent/HTTPHeaderField.swift @@ -3,7 +3,8 @@ // HTTPFluent // // Created by Gregory Higley on 2020-05-08. -// Copyright © 2020 Prosumma. All rights reserved. +// Copyright © 2020 Prosumma. +// This code is licensed under the MIT license (see LICENSE for details). // import Foundation diff --git a/Sources/HTTPFluent/HTTPMethod.swift b/Sources/HTTPFluent/HTTPMethod.swift index 7b41eaa..2968e28 100644 --- a/Sources/HTTPFluent/HTTPMethod.swift +++ b/Sources/HTTPFluent/HTTPMethod.swift @@ -3,7 +3,8 @@ // HTTPFluent // // Created by Gregory Higley on 2020-05-08. -// Copyright © 2020 Prosumma. All rights reserved. +// Copyright © 2020 Prosumma. +// This code is licensed under the MIT license (see LICENSE for details). // import Foundation diff --git a/Sources/HTTPFluent/MimeType.swift b/Sources/HTTPFluent/MimeType.swift index 6ff6787..35755b4 100644 --- a/Sources/HTTPFluent/MimeType.swift +++ b/Sources/HTTPFluent/MimeType.swift @@ -3,7 +3,8 @@ // HTTPFluent // // Created by Gregory Higley on 2020-05-08. -// Copyright © 2020 Prosumma. All rights reserved. +// Copyright © 2020 Prosumma. +// This code is licensed under the MIT license (see LICENSE for details). // import Foundation diff --git a/Sources/HTTPFluent/Publisher.swift b/Sources/HTTPFluent/Publisher.swift index f890f95..8975fe7 100644 --- a/Sources/HTTPFluent/Publisher.swift +++ b/Sources/HTTPFluent/Publisher.swift @@ -3,6 +3,8 @@ // HTTPFluent // // Created by Gregory Higley on 2020-07-13. +// Copyright © 2020 Prosumma. +// This code is licensed under the MIT license (see LICENSE for details). // #if canImport(Combine) diff --git a/Sources/HTTPFluent/URLClient.swift b/Sources/HTTPFluent/URLClient.swift index 32739cf..9ffdbfa 100644 --- a/Sources/HTTPFluent/URLClient.swift +++ b/Sources/HTTPFluent/URLClient.swift @@ -3,7 +3,8 @@ // HTTPFluent // // Created by Gregory Higley on 2020-05-08. -// Copyright © 2020 Prosumma. All rights reserved. +// Copyright © 2020 Prosumma. +// This code is licensed under the MIT license (see LICENSE for details). // #if canImport(Combine) diff --git a/Sources/HTTPFluent/URLClientProtocol+Async.swift b/Sources/HTTPFluent/URLClientProtocol+Async.swift index 88ed029..d45df71 100644 --- a/Sources/HTTPFluent/URLClientProtocol+Async.swift +++ b/Sources/HTTPFluent/URLClientProtocol+Async.swift @@ -3,7 +3,8 @@ // HTTPFluent // // Created by Gregory Higley on 2021-12-25. -// Copyright © 2020 Prosumma. All rights reserved. +// Copyright © 2020 Prosumma. +// This code is licensed under the MIT license (see LICENSE for details). // #if canImport(Combine) diff --git a/Sources/HTTPFluent/URLClientProtocol+Combine.swift b/Sources/HTTPFluent/URLClientProtocol+Combine.swift index d36a857..a68b98c 100644 --- a/Sources/HTTPFluent/URLClientProtocol+Combine.swift +++ b/Sources/HTTPFluent/URLClientProtocol+Combine.swift @@ -2,7 +2,9 @@ // URLClientProtocol+Combine.swift // HTTPFluent // -// Created by Gregory Higley on 8/12/20. +// Created by Gregory Higley on 2020-08-12. +// Copyright © 2020 Prosumma. +// This code is licensed under the MIT license (see LICENSE for details). // #if canImport(Combine) diff --git a/Sources/HTTPFluent/URLClientProtocol+Receive.swift b/Sources/HTTPFluent/URLClientProtocol+Receive.swift index 198ab37..f528930 100644 --- a/Sources/HTTPFluent/URLClientProtocol+Receive.swift +++ b/Sources/HTTPFluent/URLClientProtocol+Receive.swift @@ -2,7 +2,9 @@ // URLClientProtocol+Receive.swift // HTTPFluent // -// Created by Gregory Higley on 8/12/20. +// Created by Gregory Higley on 2020-08-12. +// Copyright © 2020 Prosumma. +// This code is licensed under the MIT license (see LICENSE for details). // #if canImport(Combine) diff --git a/Sources/HTTPFluent/URLClientProtocol.swift b/Sources/HTTPFluent/URLClientProtocol.swift index 3f19c1a..2f55d11 100644 --- a/Sources/HTTPFluent/URLClientProtocol.swift +++ b/Sources/HTTPFluent/URLClientProtocol.swift @@ -3,7 +3,8 @@ // HTTPFluent // // Created by Gregory Higley on 2020-05-08. -// Copyright © 2020 Prosumma. All rights reserved. +// Copyright © 2020 Prosumma. +// This code is licensed under the MIT license (see LICENSE for details). // #if canImport(Combine) diff --git a/Sources/HTTPFluent/URLError.swift b/Sources/HTTPFluent/URLError.swift index bd3716f..09c3200 100644 --- a/Sources/HTTPFluent/URLError.swift +++ b/Sources/HTTPFluent/URLError.swift @@ -3,7 +3,8 @@ // HTTPFluent // // Created by Gregory Higley on 2020-02-24. -// Copyright © 2020 Prosumma. All rights reserved. +// Copyright © 2020 Prosumma. +// This code is licensed under the MIT license (see LICENSE for details). // import Foundation diff --git a/Sources/HTTPFluent/URLRequestBuilder.swift b/Sources/HTTPFluent/URLRequestBuilder.swift index fde4cf6..fde21ed 100644 --- a/Sources/HTTPFluent/URLRequestBuilder.swift +++ b/Sources/HTTPFluent/URLRequestBuilder.swift @@ -3,7 +3,8 @@ // HTTPFluent // // Created by Gregory Higley on 2020-05-08. -// Copyright © 2020 Prosumma. All rights reserved. +// Copyright © 2020 Prosumma. +// This code is licensed under the MIT license (see LICENSE for details). // import Foundation diff --git a/Sources/HTTPFluent/URLRequestBuilderProtocol+HTTPBody.swift b/Sources/HTTPFluent/URLRequestBuilderProtocol+HTTPBody.swift index c5f116c..063843c 100644 --- a/Sources/HTTPFluent/URLRequestBuilderProtocol+HTTPBody.swift +++ b/Sources/HTTPFluent/URLRequestBuilderProtocol+HTTPBody.swift @@ -3,7 +3,8 @@ // HTTPFluent // // Created by Gregory Higley on 2020-05-09. -// Copyright © 2020 Prosumma. All rights reserved. +// Copyright © 2020 Prosumma. +// This code is licensed under the MIT license (see LICENSE for details). // import Combine diff --git a/Sources/HTTPFluent/URLRequestBuilderProtocol+URL.swift b/Sources/HTTPFluent/URLRequestBuilderProtocol+URL.swift index 3be800c..5e6ecc7 100644 --- a/Sources/HTTPFluent/URLRequestBuilderProtocol+URL.swift +++ b/Sources/HTTPFluent/URLRequestBuilderProtocol+URL.swift @@ -3,7 +3,8 @@ // HTTPFluent // // Created by Gregory Higley on 2020-05-08. -// Copyright © 2020 Prosumma. All rights reserved. +// Copyright © 2020 Prosumma. +// This code is licensed under the MIT license (see LICENSE for details). // import Foundation diff --git a/Sources/HTTPFluent/URLRequestBuilderProtocol+URLRequest.swift b/Sources/HTTPFluent/URLRequestBuilderProtocol+URLRequest.swift index 0df3453..0d7246c 100644 --- a/Sources/HTTPFluent/URLRequestBuilderProtocol+URLRequest.swift +++ b/Sources/HTTPFluent/URLRequestBuilderProtocol+URLRequest.swift @@ -3,7 +3,8 @@ // HTTPFluent // // Created by Gregory Higley on 2020-05-08. -// Copyright © 2020 Prosumma. All rights reserved. +// Copyright © 2020 Prosumma. +// This code is licensed under the MIT license (see LICENSE for details). // import Foundation diff --git a/Sources/HTTPFluent/URLRequestBuilderProtocol.swift b/Sources/HTTPFluent/URLRequestBuilderProtocol.swift index 0b37bb6..fa6b50f 100644 --- a/Sources/HTTPFluent/URLRequestBuilderProtocol.swift +++ b/Sources/HTTPFluent/URLRequestBuilderProtocol.swift @@ -3,7 +3,8 @@ // HTTPFluent // // Created by Gregory Higley on 2020-05-08. -// Copyright © 2020 Prosumma. All rights reserved. +// Copyright © 2020 Prosumma. +// This code is licensed under the MIT license (see LICENSE for details). // import Foundation diff --git a/Sources/HTTPFluent/URLResult.swift b/Sources/HTTPFluent/URLResult.swift index 6de8c01..e83e0fc 100644 --- a/Sources/HTTPFluent/URLResult.swift +++ b/Sources/HTTPFluent/URLResult.swift @@ -3,6 +3,8 @@ // HTTPFluent // // Created by Gregory Higley on 2020-08-02. +// Copyright © 2020 Prosumma. +// This code is licensed under the MIT license (see LICENSE for details). // import Foundation diff --git a/Tests/HTTPFluentTests/Const.swift b/Tests/HTTPFluentTests/Const.swift index d9bbbe6..e9b92e6 100644 --- a/Tests/HTTPFluentTests/Const.swift +++ b/Tests/HTTPFluentTests/Const.swift @@ -2,7 +2,9 @@ // Const.swift // HTTPFluent // -// Created by Gregory Higley on 4/1/20. +// Created by Gregory Higley on 2020-04-01. +// Copyright © 2020 Prosumma. +// This code is licensed under the MIT license (see LICENSE for details). // import HTTPFluent diff --git a/Tests/HTTPFluentTests/HTTPBinResponse.swift b/Tests/HTTPFluentTests/HTTPBinResponse.swift index 74f4c71..0113109 100644 --- a/Tests/HTTPFluentTests/HTTPBinResponse.swift +++ b/Tests/HTTPFluentTests/HTTPBinResponse.swift @@ -2,7 +2,9 @@ // HTTPBinResponse.swift // HTTPFluent // -// Created by Gregory Higley on 4/2/20. +// Created by Gregory Higley on 2020-04-02. +// Copyright © 2020 Prosumma. +// This code is licensed under the MIT license (see LICENSE for details). // import Foundation diff --git a/Tests/HTTPFluentTests/HTTPFluentAsyncTests.swift b/Tests/HTTPFluentTests/HTTPFluentAsyncTests.swift index 492d157..22bcf88 100644 --- a/Tests/HTTPFluentTests/HTTPFluentAsyncTests.swift +++ b/Tests/HTTPFluentTests/HTTPFluentAsyncTests.swift @@ -2,7 +2,9 @@ // HTTPFluentAsyncTests.swift // // -// Created by Gregory Higley on 12/25/21. +// Created by Gregory Higley on 2021-12-25. +// Copyright © 2020 Prosumma. +// This code is licensed under the MIT license (see LICENSE for details). // #if canImport(Combine) diff --git a/Tests/HTTPFluentTests/HTTPFluentCombineTests.swift b/Tests/HTTPFluentTests/HTTPFluentCombineTests.swift index ca1cb58..a4c7c51 100644 --- a/Tests/HTTPFluentTests/HTTPFluentCombineTests.swift +++ b/Tests/HTTPFluentTests/HTTPFluentCombineTests.swift @@ -1,3 +1,12 @@ +// +// HTTPFluentCombineTests.swift +// HTTPFluent +// +// Created by Gregory Higley on 2020-04-01. +// Copyright © 2020 Prosumma. +// This code is licensed under the MIT license (see LICENSE for details). +// + #if canImport(Combine) import Combine diff --git a/Tests/HTTPFluentTests/HTTPFluentReceiveTests.swift b/Tests/HTTPFluentTests/HTTPFluentReceiveTests.swift index 440bfe2..3e5e137 100644 --- a/Tests/HTTPFluentTests/HTTPFluentReceiveTests.swift +++ b/Tests/HTTPFluentTests/HTTPFluentReceiveTests.swift @@ -2,7 +2,9 @@ // HTTPFluentReceiveTests.swift // HTTPFluentTests // -// Created by Gregory Higley on 8/12/20. +// Created by Gregory Higley on 2020-08-12. +// Copyright © 2020 Prosumma. +// This code is licensed under the MIT license (see LICENSE for details). // import XCTest diff --git a/Tests/HTTPFluentTests/Slide.swift b/Tests/HTTPFluentTests/Slide.swift index b234c92..74ad792 100644 --- a/Tests/HTTPFluentTests/Slide.swift +++ b/Tests/HTTPFluentTests/Slide.swift @@ -1,8 +1,10 @@ // -// File.swift -// +// Slide.swift +// HTTPFluent // -// Created by Gregory Higley on 4/1/20. +// Created by Gregory Higley on 2020-04-01. +// Copyright © 2020 Prosumma. +// This code is licensed under the MIT license (see LICENSE for details). // struct Slide: Codable { From 08ad0b3159e0a932850ffc5dc02570c616b16c29 Mon Sep 17 00:00:00 2001 From: Gregory Higley Date: Sat, 8 Jan 2022 12:32:34 -0500 Subject: [PATCH 03/18] publisher -> receivePublisher --- README.md | 24 +++++++++++++++++++ Sources/HTTPFluent/URLClient.swift | 2 +- .../URLClientProtocol+Combine.swift | 12 +++++----- Sources/HTTPFluent/URLClientProtocol.swift | 2 +- .../HTTPFluentCombineTests.swift | 6 ++--- 5 files changed, 35 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 50754c7..20b9d2f 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,27 @@ # HTTPFluent HTTPFluent provides a fluent interface over HTTP, primarily designed to work with APIs. HTTPFluent supports three styles: callback, `async` (with Swift >= 5.5) and Combine. + +HTTPFluent is extremely intuitive to use, so a few examples will suffice: + +```swift +let id = 2349713 +let jwt = "xyz123" + +let request = URLClient(url: "httpsZ://myapi.com") + .path("user", id) + .authorization(bearer: jwt) + .post(json: User(name: "Don Quixote")) + +// Callback style +request.receive(json: User.self) { result in + do { + let user = try result.get() + } catch { + // Oops, no user + } +} + +// Combine style +request.receivePublisher(json: User.self) +``` \ No newline at end of file diff --git a/Sources/HTTPFluent/URLClient.swift b/Sources/HTTPFluent/URLClient.swift index 9ffdbfa..788503c 100644 --- a/Sources/HTTPFluent/URLClient.swift +++ b/Sources/HTTPFluent/URLClient.swift @@ -74,7 +74,7 @@ extension URLClient: URLClientProtocol { } @available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) - public var publisher: AnyPublisher { + public var receivePublisher: AnyPublisher { request.publisher.flatMap { req in self.session .dataTaskPublisher(for: req) diff --git a/Sources/HTTPFluent/URLClientProtocol+Combine.swift b/Sources/HTTPFluent/URLClientProtocol+Combine.swift index a68b98c..7d8537f 100644 --- a/Sources/HTTPFluent/URLClientProtocol+Combine.swift +++ b/Sources/HTTPFluent/URLClientProtocol+Combine.swift @@ -31,10 +31,10 @@ public extension URLClientProtocol { `Decoders.string` is a function which decodes from `Data` to `String`, assuming the underlying `Data` is UTF-8. */ - func publisher( + func receivePublisher( decode: @escaping Decoders.Decode ) -> AnyPublisher { - publisher.tryMap(decode).mapErrorIfNeeded(URLError.decoding).eraseToAnyPublisher() + receivePublisher.tryMap(decode).mapErrorIfNeeded(URLError.decoding).eraseToAnyPublisher() } /** @@ -44,19 +44,19 @@ public extension URLClientProtocol { If `decoder` is `JSONDecoder`, the `Accept: application/json` is sent automatically with the request. */ - func publisher( + func receivePublisher( decoding type: Response.Type = Response.self, decoder: Decoder ) -> AnyPublisher where Response: Decodable, Decoder: TopLevelDecoder, Decoder.Input == Data { let fluent = decoder is JSONDecoder ? accept(.json) : self - return fluent.publisher(decode: Decoders.decode(type, with: decoder)) + return fluent.receivePublisher(decode: Decoders.decode(type, with: decoder)) } /// Decodes the `Data` in the published stream using a default `JSONDecoder`. - func publisher( + func receivePublisher( json type: Response.Type = Response.self ) -> AnyPublisher { - accept(.json).publisher(decode: Decoders.json(type)) + accept(.json).receivePublisher(decode: Decoders.json(type)) } } diff --git a/Sources/HTTPFluent/URLClientProtocol.swift b/Sources/HTTPFluent/URLClientProtocol.swift index 2f55d11..559578b 100644 --- a/Sources/HTTPFluent/URLClientProtocol.swift +++ b/Sources/HTTPFluent/URLClientProtocol.swift @@ -22,7 +22,7 @@ public protocol URLClientProtocol: URLRequestBuilderProtocol { Publishes the `Data` result of executing the underlying `URLRequest`. */ @available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) - var publisher: AnyPublisher { get } + var receivePublisher: AnyPublisher { get } #if swift(>=5.5) @available(macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 8.0, *) diff --git a/Tests/HTTPFluentTests/HTTPFluentCombineTests.swift b/Tests/HTTPFluentTests/HTTPFluentCombineTests.swift index a4c7c51..a2f7f54 100644 --- a/Tests/HTTPFluentTests/HTTPFluentCombineTests.swift +++ b/Tests/HTTPFluentTests/HTTPFluentCombineTests.swift @@ -40,7 +40,7 @@ final class HttpFluentCombineTests: XCTestCase { } let cancellable = URLClient.bin .path("json") - .publisher(json: Slideshows.self) + .receivePublisher(json: Slideshows.self) .sink( receiveCompletion: fulfill(e), receiveValue: print(slideshows:) @@ -56,7 +56,7 @@ final class HttpFluentCombineTests: XCTestCase { .path("post") .post(json: slide) .accept(.json) - .publisher(decode: Decoders.string) + .receivePublisher(decode: Decoders.string) .sink(receiveCompletion: fulfill(e)) { s in print(s) } @@ -71,7 +71,7 @@ final class HttpFluentCombineTests: XCTestCase { .path("status", statusCode) .accept(.json) .method(.put) - .publisher(decode: Decoders.string) + .receivePublisher(decode: Decoders.string) .sink( receiveCompletion: fulfill(e, expectError: true), receiveValue: { _ in XCTFail("Expected HTTP Status \(statusCode).") } From e73e80e9feca2e56e802023e7911d6ab73c28001 Mon Sep 17 00:00:00 2001 From: Gregory Higley Date: Sat, 8 Jan 2022 12:49:43 -0500 Subject: [PATCH 04/18] Updated the README --- README.md | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 20b9d2f..67231e1 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ HTTPFluent is extremely intuitive to use, so a few examples will suffice: let id = 2349713 let jwt = "xyz123" -let request = URLClient(url: "httpsZ://myapi.com") +let request = URLClient(url: "https://myapi.com") .path("user", id) .authorization(bearer: jwt) .post(json: User(name: "Don Quixote")) @@ -22,6 +22,39 @@ request.receive(json: User.self) { result in } } +// Async style +let user = try await request.receive(json: User.self) + // Combine style request.receivePublisher(json: User.self) + .sink { completion in + // Handle completion + } receiveValue: { user in + // Do something with user + } + .store(in: &cancellables) +``` + +HTTPFluent can also be used to generate a `URLRequest` without invoking it. + +```swift +let urlRequest = URLClient(url: "https://myapi.com") + .path("user", id) + .authorization(bearer: jwt) + .put(data: data) // Here we put raw data instead of JSON. + .request +``` + +HTTPFluent uses immutable state. Each step in the chain to build the `URLRequest` copies a `URLRequestBuilder` struct. All operations are thus additive, encouraging reuse. + +```swift +// Set up the shared information about the request. +let fluent = URLClient(url: "https://myapi.com") + .authorization(bearer: jwt) + .path("user") + +// This adds the value of id as a path element, so the result is +// the path /user/123 or whatever the value of id is. +let postWithId = fluent.path(id).post(json: User.self) +let user = try await postWithId.receive(json: User.self) ``` \ No newline at end of file From 0df8f530bb0ac89b57084dc958c985c70a4045a3 Mon Sep 17 00:00:00 2001 From: Gregory Higley Date: Sat, 8 Jan 2022 12:58:35 -0500 Subject: [PATCH 05/18] Added SPM to README --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 67231e1..9328402 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,12 @@ HTTPFluent provides a fluent interface over HTTP, primarily designed to work with APIs. HTTPFluent supports three styles: callback, `async` (with Swift >= 5.5) and Combine. +## Integration + +HTTPFluent is available only via Swift Package Manager. + +## Usage + HTTPFluent is extremely intuitive to use, so a few examples will suffice: ```swift From 1e4acaa2b5b79c091f5aeb3e6346d26680b029de Mon Sep 17 00:00:00 2001 From: Gregory Higley Date: Sun, 12 Feb 2023 15:36:39 -0500 Subject: [PATCH 06/18] Linux compatibility --- .gitignore | 1 + Sources/HTTPFluent/Decoders.swift | 2 ++ Sources/HTTPFluent/URLRequestBuilderProtocol+HTTPBody.swift | 2 ++ 3 files changed, 5 insertions(+) diff --git a/.gitignore b/.gitignore index 5fbae6e..ae9498c 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ *.xcworkspace/ xcuserdata/ /.swiftpm +/.vscode diff --git a/Sources/HTTPFluent/Decoders.swift b/Sources/HTTPFluent/Decoders.swift index b87858c..11ec083 100644 --- a/Sources/HTTPFluent/Decoders.swift +++ b/Sources/HTTPFluent/Decoders.swift @@ -7,7 +7,9 @@ // This code is licensed under the MIT license (see LICENSE for details). // +#if canImport(Combine) import Combine +#endif import Foundation public struct Decoders { diff --git a/Sources/HTTPFluent/URLRequestBuilderProtocol+HTTPBody.swift b/Sources/HTTPFluent/URLRequestBuilderProtocol+HTTPBody.swift index 063843c..6f1d74d 100644 --- a/Sources/HTTPFluent/URLRequestBuilderProtocol+HTTPBody.swift +++ b/Sources/HTTPFluent/URLRequestBuilderProtocol+HTTPBody.swift @@ -7,7 +7,9 @@ // This code is licensed under the MIT license (see LICENSE for details). // +#if canImport(Combine) import Combine +#endif import Foundation public extension URLRequestBuilderProtocol { From c4203797dc2f61329dc4758278358717227d8ce3 Mon Sep 17 00:00:00 2001 From: Gregory Higley Date: Thu, 23 Mar 2023 23:40:46 -0400 Subject: [PATCH 07/18] Linux support --- Sources/HTTPFluent/URLClient.swift | 5 +++++ Sources/HTTPFluent/URLClientProtocol+Combine.swift | 4 ++-- Sources/HTTPFluent/URLClientProtocol.swift | 2 ++ Sources/HTTPFluent/URLError.swift | 1 + Sources/HTTPFluent/URLRequestBuilder.swift | 1 + Sources/HTTPFluent/URLRequestBuilderProtocol+HTTPBody.swift | 1 + .../HTTPFluent/URLRequestBuilderProtocol+URLRequest.swift | 1 + Sources/HTTPFluent/URLRequestBuilderProtocol.swift | 1 + 8 files changed, 14 insertions(+), 2 deletions(-) diff --git a/Sources/HTTPFluent/URLClient.swift b/Sources/HTTPFluent/URLClient.swift index 788503c..cebca73 100644 --- a/Sources/HTTPFluent/URLClient.swift +++ b/Sources/HTTPFluent/URLClient.swift @@ -12,6 +12,7 @@ import Combine #endif import Foundation +import FoundationNetworking /** Perform HTTP operations on a base URL. @@ -73,6 +74,8 @@ extension URLClient: URLClientProtocol { builder.request } + #if canImport(Combine) + @available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) public var receivePublisher: AnyPublisher { request.publisher.flatMap { req in @@ -82,6 +85,8 @@ extension URLClient: URLClientProtocol { .mapToURLError }.eraseToAnyPublisher() } + + #endif public func receive(on queue: DispatchQueue = DispatchQueue.global(), callback: @escaping (URLResult) -> Void) { switch request { diff --git a/Sources/HTTPFluent/URLClientProtocol+Combine.swift b/Sources/HTTPFluent/URLClientProtocol+Combine.swift index 7d8537f..59cdd8b 100644 --- a/Sources/HTTPFluent/URLClientProtocol+Combine.swift +++ b/Sources/HTTPFluent/URLClientProtocol+Combine.swift @@ -9,8 +9,6 @@ #if canImport(Combine) import Combine -#endif - import Foundation @available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) @@ -60,3 +58,5 @@ public extension URLClientProtocol { } } + +#endif \ No newline at end of file diff --git a/Sources/HTTPFluent/URLClientProtocol.swift b/Sources/HTTPFluent/URLClientProtocol.swift index 559578b..efdc1cd 100644 --- a/Sources/HTTPFluent/URLClientProtocol.swift +++ b/Sources/HTTPFluent/URLClientProtocol.swift @@ -18,11 +18,13 @@ import Foundation `URLRequestBuilderProtocol`. */ public protocol URLClientProtocol: URLRequestBuilderProtocol { +#if canImport(Combine) /** Publishes the `Data` result of executing the underlying `URLRequest`. */ @available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) var receivePublisher: AnyPublisher { get } +#endif #if swift(>=5.5) @available(macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 8.0, *) diff --git a/Sources/HTTPFluent/URLError.swift b/Sources/HTTPFluent/URLError.swift index 09c3200..f499bad 100644 --- a/Sources/HTTPFluent/URLError.swift +++ b/Sources/HTTPFluent/URLError.swift @@ -8,6 +8,7 @@ // import Foundation +import FoundationNetworking /// An error that may occur as a result of constructing or publishing a `URLRequest`. public enum URLError: Error { diff --git a/Sources/HTTPFluent/URLRequestBuilder.swift b/Sources/HTTPFluent/URLRequestBuilder.swift index fde21ed..9b1b841 100644 --- a/Sources/HTTPFluent/URLRequestBuilder.swift +++ b/Sources/HTTPFluent/URLRequestBuilder.swift @@ -8,6 +8,7 @@ // import Foundation +import FoundationNetworking public struct URLRequestBuilder { public typealias Apply = (inout Self) -> Void diff --git a/Sources/HTTPFluent/URLRequestBuilderProtocol+HTTPBody.swift b/Sources/HTTPFluent/URLRequestBuilderProtocol+HTTPBody.swift index 6f1d74d..d4c1760 100644 --- a/Sources/HTTPFluent/URLRequestBuilderProtocol+HTTPBody.swift +++ b/Sources/HTTPFluent/URLRequestBuilderProtocol+HTTPBody.swift @@ -11,6 +11,7 @@ import Combine #endif import Foundation +import FoundationNetworking public extension URLRequestBuilderProtocol { /** diff --git a/Sources/HTTPFluent/URLRequestBuilderProtocol+URLRequest.swift b/Sources/HTTPFluent/URLRequestBuilderProtocol+URLRequest.swift index 0d7246c..d2c354d 100644 --- a/Sources/HTTPFluent/URLRequestBuilderProtocol+URLRequest.swift +++ b/Sources/HTTPFluent/URLRequestBuilderProtocol+URLRequest.swift @@ -7,6 +7,7 @@ // This code is licensed under the MIT license (see LICENSE for details). // +import CoreFoundation import Foundation public extension URLRequestBuilderProtocol { diff --git a/Sources/HTTPFluent/URLRequestBuilderProtocol.swift b/Sources/HTTPFluent/URLRequestBuilderProtocol.swift index fa6b50f..ad2d39c 100644 --- a/Sources/HTTPFluent/URLRequestBuilderProtocol.swift +++ b/Sources/HTTPFluent/URLRequestBuilderProtocol.swift @@ -8,6 +8,7 @@ // import Foundation +import FoundationNetworking /// A conforming type may be used to build a `URLRequest`. public protocol URLRequestBuilderProtocol { From f1a3135c98041c6d593cfef437d5ecf0295a887d Mon Sep 17 00:00:00 2001 From: Gregory Higley Date: Fri, 24 Mar 2023 00:05:56 -0400 Subject: [PATCH 08/18] No Combine on Linux --- Sources/HTTPFluent/Combine+Shim.swift | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Sources/HTTPFluent/Combine+Shim.swift b/Sources/HTTPFluent/Combine+Shim.swift index 2409c6e..474498d 100644 --- a/Sources/HTTPFluent/Combine+Shim.swift +++ b/Sources/HTTPFluent/Combine+Shim.swift @@ -19,4 +19,7 @@ public protocol TopLevelEncoder { associatedtype Output func encode(_ value: T) throws -> Output } + +extension JSONDecoder: TopLevelDecoder {} +extension JSONEncoder: TopLevelEncoder {} #endif From 0998021010f395ceff8daf637e0e4d06c21a92ed Mon Sep 17 00:00:00 2001 From: Gregory Higley Date: Fri, 24 Mar 2023 00:14:09 -0400 Subject: [PATCH 09/18] Linux --- Sources/HTTPFluent/URLRequestBuilderProtocol+URLRequest.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Sources/HTTPFluent/URLRequestBuilderProtocol+URLRequest.swift b/Sources/HTTPFluent/URLRequestBuilderProtocol+URLRequest.swift index d2c354d..84aecd9 100644 --- a/Sources/HTTPFluent/URLRequestBuilderProtocol+URLRequest.swift +++ b/Sources/HTTPFluent/URLRequestBuilderProtocol+URLRequest.swift @@ -35,7 +35,8 @@ public extension URLRequestBuilderProtocol { var contentType: String = type.rawValue if let charset = charset { let enc = CFStringConvertNSStringEncodingToEncoding(charset.rawValue) - let iana = CFStringConvertEncodingToIANACharSetName(enc) as NSString as String + let cfiana: CFString = CFStringConvertEncodingToIANACharSetName(enc) + let iana = String(describing: cfiana) contentType += ";charset=\(iana)" } return header(contentType, forField: .contentType) From 0fa4c2e343754c05123a304ec54119cd019f2d7e Mon Sep 17 00:00:00 2001 From: Gregory Higley Date: Fri, 24 Mar 2023 00:28:46 -0400 Subject: [PATCH 10/18] Linux --- Sources/HTTPFluent/URLClient.swift | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/Sources/HTTPFluent/URLClient.swift b/Sources/HTTPFluent/URLClient.swift index cebca73..63fd099 100644 --- a/Sources/HTTPFluent/URLClient.swift +++ b/Sources/HTTPFluent/URLClient.swift @@ -119,9 +119,19 @@ extension URLClient: URLClientProtocol { case .failure(let error): throw error case .success(let request): - let (data, response): (Data, URLResponse) + let (data, response): (Data?, URLResponse?) do { - (data, response) = try await session.data(for: request) + #if os(Linux) + (data, response) = try await withCheckedThrowingContinuation { continuation in + let task = session.dataTask(with: request) { (data, response, error) in + if let error = error { continuation.resume(throwing: error) } + continuation.resume(returning: (data, response)) + } + task.resume() + } + #else + (data, response) = try await session.data(for: request) + #endif } catch { throw URLError.error(error) } From 5b8c8f06e4718f4acee7415ca25f2d1b5f7db4f9 Mon Sep 17 00:00:00 2001 From: Gregory Higley Date: Fri, 24 Mar 2023 00:33:42 -0400 Subject: [PATCH 11/18] FoundationNetworking --- Sources/HTTPFluent/URLClient.swift | 3 +++ Sources/HTTPFluent/URLError.swift | 3 +++ Sources/HTTPFluent/URLRequestBuilder.swift | 3 +++ Sources/HTTPFluent/URLRequestBuilderProtocol+HTTPBody.swift | 3 +++ Sources/HTTPFluent/URLRequestBuilderProtocol.swift | 3 +++ 5 files changed, 15 insertions(+) diff --git a/Sources/HTTPFluent/URLClient.swift b/Sources/HTTPFluent/URLClient.swift index 63fd099..d3f5d1d 100644 --- a/Sources/HTTPFluent/URLClient.swift +++ b/Sources/HTTPFluent/URLClient.swift @@ -12,7 +12,10 @@ import Combine #endif import Foundation + +#if canImport(FoundationNetworking) import FoundationNetworking +#endif /** Perform HTTP operations on a base URL. diff --git a/Sources/HTTPFluent/URLError.swift b/Sources/HTTPFluent/URLError.swift index f499bad..3119a46 100644 --- a/Sources/HTTPFluent/URLError.swift +++ b/Sources/HTTPFluent/URLError.swift @@ -8,7 +8,10 @@ // import Foundation + +#if canImport(FoundationNetworking) import FoundationNetworking +#endif /// An error that may occur as a result of constructing or publishing a `URLRequest`. public enum URLError: Error { diff --git a/Sources/HTTPFluent/URLRequestBuilder.swift b/Sources/HTTPFluent/URLRequestBuilder.swift index 9b1b841..fa2dd94 100644 --- a/Sources/HTTPFluent/URLRequestBuilder.swift +++ b/Sources/HTTPFluent/URLRequestBuilder.swift @@ -8,7 +8,10 @@ // import Foundation + +#if canImport(FoundationNetworking) import FoundationNetworking +#endif public struct URLRequestBuilder { public typealias Apply = (inout Self) -> Void diff --git a/Sources/HTTPFluent/URLRequestBuilderProtocol+HTTPBody.swift b/Sources/HTTPFluent/URLRequestBuilderProtocol+HTTPBody.swift index d4c1760..64dd54a 100644 --- a/Sources/HTTPFluent/URLRequestBuilderProtocol+HTTPBody.swift +++ b/Sources/HTTPFluent/URLRequestBuilderProtocol+HTTPBody.swift @@ -11,7 +11,10 @@ import Combine #endif import Foundation + +#if canImport(FoundationNetworking) import FoundationNetworking +#endif public extension URLRequestBuilderProtocol { /** diff --git a/Sources/HTTPFluent/URLRequestBuilderProtocol.swift b/Sources/HTTPFluent/URLRequestBuilderProtocol.swift index ad2d39c..6bf9f55 100644 --- a/Sources/HTTPFluent/URLRequestBuilderProtocol.swift +++ b/Sources/HTTPFluent/URLRequestBuilderProtocol.swift @@ -8,7 +8,10 @@ // import Foundation + +#if canImport(FoundationNetworking) import FoundationNetworking +#endif /// A conforming type may be used to build a `URLRequest`. public protocol URLRequestBuilderProtocol { From 2f755dba3ebf350f2f3acab79b4a780d8ae59003 Mon Sep 17 00:00:00 2001 From: Gregory Higley Date: Fri, 24 Mar 2023 00:40:35 -0400 Subject: [PATCH 12/18] Clarified README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 9328402..5c713be 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # HTTPFluent -HTTPFluent provides a fluent interface over HTTP, primarily designed to work with APIs. HTTPFluent supports three styles: callback, `async` (with Swift >= 5.5) and Combine. +HTTPFluent provides a fluent interface over HTTP, primarily designed to work with APIs. HTTPFluent supports three styles: callback, `async` (with Swift >= 5.5) and Combine (on Apple platforms). ## Integration @@ -63,4 +63,4 @@ let fluent = URLClient(url: "https://myapi.com") // the path /user/123 or whatever the value of id is. let postWithId = fluent.path(id).post(json: User.self) let user = try await postWithId.receive(json: User.self) -``` \ No newline at end of file +``` From 73141a4356b8bf18da1bd80edd30ab01fb7daa17 Mon Sep 17 00:00:00 2001 From: Gregory Higley Date: Fri, 24 Mar 2023 01:11:52 -0400 Subject: [PATCH 13/18] Some cleanup + a header test --- Sources/HTTPFluent/Decoders.swift | 3 ++- Sources/HTTPFluent/URLClient.swift | 4 ++-- Sources/HTTPFluent/URLClientProtocol+Async.swift | 4 ++-- Sources/HTTPFluent/URLClientProtocol+Receive.swift | 4 ++-- Sources/HTTPFluent/URLClientProtocol.swift | 4 ++-- .../URLRequestBuilderProtocol+HTTPBody.swift | 3 ++- Tests/HTTPFluentTests/HTTPFluentAsyncTests.swift | 5 +++++ Tests/HTTPFluentTests/HTTPHeaderResponse.swift | 14 ++++++++++++++ 8 files changed, 31 insertions(+), 10 deletions(-) create mode 100644 Tests/HTTPFluentTests/HTTPHeaderResponse.swift diff --git a/Sources/HTTPFluent/Decoders.swift b/Sources/HTTPFluent/Decoders.swift index 11ec083..3838c1f 100644 --- a/Sources/HTTPFluent/Decoders.swift +++ b/Sources/HTTPFluent/Decoders.swift @@ -7,10 +7,11 @@ // This code is licensed under the MIT license (see LICENSE for details). // +import Foundation + #if canImport(Combine) import Combine #endif -import Foundation public struct Decoders { public typealias Decode = (Data) throws -> T diff --git a/Sources/HTTPFluent/URLClient.swift b/Sources/HTTPFluent/URLClient.swift index d3f5d1d..7ae9e5f 100644 --- a/Sources/HTTPFluent/URLClient.swift +++ b/Sources/HTTPFluent/URLClient.swift @@ -7,12 +7,12 @@ // This code is licensed under the MIT license (see LICENSE for details). // +import Foundation + #if canImport(Combine) import Combine #endif -import Foundation - #if canImport(FoundationNetworking) import FoundationNetworking #endif diff --git a/Sources/HTTPFluent/URLClientProtocol+Async.swift b/Sources/HTTPFluent/URLClientProtocol+Async.swift index d45df71..0804844 100644 --- a/Sources/HTTPFluent/URLClientProtocol+Async.swift +++ b/Sources/HTTPFluent/URLClientProtocol+Async.swift @@ -7,12 +7,12 @@ // This code is licensed under the MIT license (see LICENSE for details). // +import Foundation + #if canImport(Combine) import Combine #endif -import Foundation - #if swift(>=5.5) @available(macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 8.0, *) diff --git a/Sources/HTTPFluent/URLClientProtocol+Receive.swift b/Sources/HTTPFluent/URLClientProtocol+Receive.swift index f528930..9251da5 100644 --- a/Sources/HTTPFluent/URLClientProtocol+Receive.swift +++ b/Sources/HTTPFluent/URLClientProtocol+Receive.swift @@ -7,12 +7,12 @@ // This code is licensed under the MIT license (see LICENSE for details). // +import Foundation + #if canImport(Combine) import Combine #endif -import Foundation - //swiftlint:disable function_default_parameter_at_end public extension URLClientProtocol { diff --git a/Sources/HTTPFluent/URLClientProtocol.swift b/Sources/HTTPFluent/URLClientProtocol.swift index efdc1cd..2beb38a 100644 --- a/Sources/HTTPFluent/URLClientProtocol.swift +++ b/Sources/HTTPFluent/URLClientProtocol.swift @@ -7,12 +7,12 @@ // This code is licensed under the MIT license (see LICENSE for details). // +import Foundation + #if canImport(Combine) import Combine #endif -import Foundation - /** Adds the ability to publish a `URLRequest` built using `URLRequestBuilderProtocol`. diff --git a/Sources/HTTPFluent/URLRequestBuilderProtocol+HTTPBody.swift b/Sources/HTTPFluent/URLRequestBuilderProtocol+HTTPBody.swift index 64dd54a..42636ca 100644 --- a/Sources/HTTPFluent/URLRequestBuilderProtocol+HTTPBody.swift +++ b/Sources/HTTPFluent/URLRequestBuilderProtocol+HTTPBody.swift @@ -7,10 +7,11 @@ // This code is licensed under the MIT license (see LICENSE for details). // +import Foundation + #if canImport(Combine) import Combine #endif -import Foundation #if canImport(FoundationNetworking) import FoundationNetworking diff --git a/Tests/HTTPFluentTests/HTTPFluentAsyncTests.swift b/Tests/HTTPFluentTests/HTTPFluentAsyncTests.swift index 22bcf88..c5624ef 100644 --- a/Tests/HTTPFluentTests/HTTPFluentAsyncTests.swift +++ b/Tests/HTTPFluentTests/HTTPFluentAsyncTests.swift @@ -22,6 +22,11 @@ class HTTPFluentAsyncTests: XCTestCase { let slideshows = try await URLClient.bin.path("json").receive(json: Slideshows.self) XCTAssertEqual(slideshows.slideshow.title, "Sample Slide Show") } + + func testHeaders() async throws { + let response = try await URLClient.bin.path("headers").content(type: .json, charset: .utf8).receive(json: HTTPHeaderResponse.self) + XCTAssertEqual(response.headers["Content-Type"], "application/json;charset=utf-8") + } } #endif diff --git a/Tests/HTTPFluentTests/HTTPHeaderResponse.swift b/Tests/HTTPFluentTests/HTTPHeaderResponse.swift new file mode 100644 index 0000000..48eab1f --- /dev/null +++ b/Tests/HTTPFluentTests/HTTPHeaderResponse.swift @@ -0,0 +1,14 @@ +// +// HTTPHeaderResponse.swift +// HTTPFluent +// +// Created by Gregory Higley on 2023-03-24. +// Copyright © 2023 Prosumma. +// This code is licensed under the MIT license (see LICENSE for details). +// + +import Foundation + +struct HTTPHeaderResponse: Decodable { + let headers: [String: String] +} From 6aae08b2c252bc7d83f59b55ebb0f86231fac039 Mon Sep 17 00:00:00 2001 From: Gregory Higley Date: Sat, 15 Apr 2023 02:51:33 -0400 Subject: [PATCH 14/18] Added convenience method for string bodies --- .../URLRequestBuilderProtocol+HTTPBody.swift | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/Sources/HTTPFluent/URLRequestBuilderProtocol+HTTPBody.swift b/Sources/HTTPFluent/URLRequestBuilderProtocol+HTTPBody.swift index 42636ca..b4b8410 100644 --- a/Sources/HTTPFluent/URLRequestBuilderProtocol+HTTPBody.swift +++ b/Sources/HTTPFluent/URLRequestBuilderProtocol+HTTPBody.swift @@ -33,6 +33,11 @@ public extension URLRequestBuilderProtocol { body { data } } + /// Send a `String` without setting an HTTP method. + func send(string: String, encoding: String.Encoding = .utf8) -> Self { + send(data: string.data(using: .utf8)!) + } + /// Send `FormData` without setting an HTTP method. func send(form: FormData) -> Self { content(type: form.contentType).body { form.encoded() } @@ -56,6 +61,11 @@ public extension URLRequestBuilderProtocol { send(data: data).method(.post) } + /// Post a `String` + func post(string: String, encoding: String.Encoding = .utf8) -> Self { + send(string: string, encoding: encoding).method(.post) + } + /// Post `FormData` func post(form: FormData) -> Self { send(form: form).method(.post) @@ -79,6 +89,11 @@ public extension URLRequestBuilderProtocol { send(data: data).method(.patch) } + /// Patch a `String` + func patch(string: String, encoding: String.Encoding = .utf8) -> Self { + send(string: string, encoding: encoding).method(.patch) + } + /// Patch `FormData` func patch(form: FormData) -> Self { send(form: form).method(.patch) @@ -102,6 +117,11 @@ public extension URLRequestBuilderProtocol { send(data: data).method(.put) } + /// Put a `String` + func put(string: String, encoding: String.Encoding = .utf8) -> Self { + send(string: string, encoding: encoding).method(.put) + } + /// Put `FormData` func put(form: FormData) -> Self { send(form: form).method(.put) From fa08bae958a2e36270ba279ff89ba2c15c9b76a2 Mon Sep 17 00:00:00 2001 From: Gregory Higley Date: Sat, 16 Aug 2025 14:06:03 -0400 Subject: [PATCH 15/18] Swift 6 --- Package.swift | 10 ++--- Sources/HTTPFluent/Combine+Shim.swift | 25 ----------- Sources/HTTPFluent/ConstantValue.swift | 4 +- Sources/HTTPFluent/Decoders.swift | 6 +-- Sources/HTTPFluent/FormData.swift | 6 +-- Sources/HTTPFluent/Publisher.swift | 2 - Sources/HTTPFluent/URLClient.swift | 45 ++++++++++--------- .../HTTPFluent/URLClientProtocol+Async.swift | 12 +---- .../URLClientProtocol+Combine.swift | 6 +-- .../URLClientProtocol+Receive.swift | 15 +++---- Sources/HTTPFluent/URLClientProtocol.swift | 18 +++----- Sources/HTTPFluent/URLRequestBuilder.swift | 6 +-- .../URLRequestBuilderProtocol+HTTPBody.swift | 23 +++++----- Sources/HTTPFluent/URLResult.swift | 2 +- 14 files changed, 66 insertions(+), 114 deletions(-) delete mode 100644 Sources/HTTPFluent/Combine+Shim.swift diff --git a/Package.swift b/Package.swift index c164678..567572c 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version:5.2 +// swift-tools-version:6.0 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription @@ -6,10 +6,10 @@ import PackageDescription let package = Package( name: "HTTPFluent", platforms: [ - .macOS(.v10_15), - .iOS(.v13), - .tvOS(.v13), - .watchOS(.v6) + .macOS(.v13), + .iOS(.v16), + .tvOS(.v16), + .watchOS(.v9) ], products: [ // Products define the executables and libraries produced by a package, and make them visible to other packages. diff --git a/Sources/HTTPFluent/Combine+Shim.swift b/Sources/HTTPFluent/Combine+Shim.swift deleted file mode 100644 index 474498d..0000000 --- a/Sources/HTTPFluent/Combine+Shim.swift +++ /dev/null @@ -1,25 +0,0 @@ -// -// Combine+Shim.swift -// HTTPFluent -// -// Created by Gregory Higley on 2020-08-12. -// Copyright © 2020 Prosumma. -// This code is licensed under the MIT license (see LICENSE for details). -// - -import Foundation - -#if !canImport(Combine) -public protocol TopLevelDecoder { - associatedtype Input - func decode(_ type: T.Type, from: Input) throws -> T -} - -public protocol TopLevelEncoder { - associatedtype Output - func encode(_ value: T) throws -> Output -} - -extension JSONDecoder: TopLevelDecoder {} -extension JSONEncoder: TopLevelEncoder {} -#endif diff --git a/Sources/HTTPFluent/ConstantValue.swift b/Sources/HTTPFluent/ConstantValue.swift index 7a5a3e9..bfb40b5 100644 --- a/Sources/HTTPFluent/ConstantValue.swift +++ b/Sources/HTTPFluent/ConstantValue.swift @@ -9,7 +9,9 @@ import Foundation -public protocol ConstantValue: RawRepresentable & Hashable & ExpressibleByStringLiteral & CustomStringConvertible where RawValue == String { +public protocol ConstantValue: + RawRepresentable, Hashable, ExpressibleByStringLiteral, CustomStringConvertible, Sendable where RawValue == String +{ init(constantValue value: String) } diff --git a/Sources/HTTPFluent/Decoders.swift b/Sources/HTTPFluent/Decoders.swift index 3838c1f..1f22edd 100644 --- a/Sources/HTTPFluent/Decoders.swift +++ b/Sources/HTTPFluent/Decoders.swift @@ -13,8 +13,8 @@ import Foundation import Combine #endif -public struct Decoders { - public typealias Decode = (Data) throws -> T +public struct Decoders: Sendable { + public typealias Decode = @Sendable (Data) throws -> T public static func string(encoding: String.Encoding) -> Decode { return { data in @@ -27,7 +27,7 @@ public struct Decoders { public static let string: Decode = Self.string(encoding: .utf8) - public static func decode(_ type: T.Type, with decoder: Decoder) -> Decode where Decoder.Input == Data { + public static func decode(_ type: T.Type, with decoder: Decoder) -> Decode where Decoder.Input == Data { return { data in do { return try decoder.decode(type, from: data) diff --git a/Sources/HTTPFluent/FormData.swift b/Sources/HTTPFluent/FormData.swift index e693dc4..904d665 100644 --- a/Sources/HTTPFluent/FormData.swift +++ b/Sources/HTTPFluent/FormData.swift @@ -25,8 +25,8 @@ import Foundation let data = form.encoded() ``` */ -public struct FormData { - public enum Encoding { +public struct FormData: @unchecked Sendable { + public enum Encoding: Sendable { case urlEncoded case formEncoded } @@ -36,7 +36,7 @@ public struct FormData { `FormData` instance is encoded as "application/x-www-form-urlencoded", instances of this type are skipped without warning. */ - public struct File { + public struct File: Sendable { public let filename: String public let content: Data public let headers: [String: String] diff --git a/Sources/HTTPFluent/Publisher.swift b/Sources/HTTPFluent/Publisher.swift index 8975fe7..18b9462 100644 --- a/Sources/HTTPFluent/Publisher.swift +++ b/Sources/HTTPFluent/Publisher.swift @@ -7,7 +7,6 @@ // This code is licensed under the MIT license (see LICENSE for details). // -#if canImport(Combine) import Combine @available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) @@ -28,4 +27,3 @@ public extension Publisher { return mapErrorIfNeeded(URLError.error) } } -#endif diff --git a/Sources/HTTPFluent/URLClient.swift b/Sources/HTTPFluent/URLClient.swift index 7ae9e5f..b454d5a 100644 --- a/Sources/HTTPFluent/URLClient.swift +++ b/Sources/HTTPFluent/URLClient.swift @@ -7,11 +7,8 @@ // This code is licensed under the MIT license (see LICENSE for details). // -import Foundation - -#if canImport(Combine) import Combine -#endif +import Foundation #if canImport(FoundationNetworking) import FoundationNetworking @@ -40,24 +37,36 @@ import FoundationNetworking .receive(json: AttendeeResponse.self) ``` */ -public struct URLClient { - public typealias ResponseHandler = (Data?, URLResponse?) throws -> Data +public struct URLClient: Sendable { + public typealias ResponseHandler = @Sendable (Data?, URLResponse?) throws -> Data let session: URLSession var builder: URLRequestBuilder let responseHandler: ResponseHandler - public init(builder: URLRequestBuilder, session: URLSession = URLSession(configuration: .ephemeral), responseHandler: @escaping ResponseHandler = URLClient.defaultResponseHandler) { + public init( + builder: URLRequestBuilder, + session: URLSession = URLSession(configuration: .ephemeral), + responseHandler: @escaping ResponseHandler = URLClient.defaultResponseHandler + ) { self.session = session self.builder = builder self.responseHandler = responseHandler } - public init(url: URL, session: URLSession = URLSession(configuration: .ephemeral), responseHandler: @escaping ResponseHandler = URLClient.defaultResponseHandler) { + public init( + url: URL, + session: URLSession = URLSession(configuration: .ephemeral), + responseHandler: @escaping ResponseHandler = URLClient.defaultResponseHandler + ) { self.init(builder: URLRequestBuilder(url: url), session: session, responseHandler: responseHandler) } - public init(url: String, session: URLSession = URLSession(configuration: .ephemeral), responseHandler: @escaping ResponseHandler = URLClient.defaultResponseHandler) { + public init( + url: String, + session: URLSession = URLSession(configuration: .ephemeral), + responseHandler: @escaping ResponseHandler = URLClient.defaultResponseHandler + ) { self.init(builder: URLRequestBuilder(url: url), session: session, responseHandler: responseHandler) } @@ -77,9 +86,6 @@ extension URLClient: URLClientProtocol { builder.request } - #if canImport(Combine) - - @available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) public var receivePublisher: AnyPublisher { request.publisher.flatMap { req in self.session @@ -88,17 +94,18 @@ extension URLClient: URLClientProtocol { .mapToURLError }.eraseToAnyPublisher() } - - #endif - public func receive(on queue: DispatchQueue = DispatchQueue.global(), callback: @escaping (URLResult) -> Void) { + public func receive(on queue: DispatchQueue = DispatchQueue.global(), callback: @escaping @Sendable (URLResult) -> Void) { switch request { case .failure(let error): queue.async { callback(.failure(error)) } case .success(let request): let task = session.dataTask(with: request) { (data, response, error) in var result: URLResult = .failure(.unknown) - defer { queue.async { callback(result) } } + defer { + let result = result + queue.async { callback(result) } + } if let error = error { return result = .failure(.error(error)) } @@ -114,9 +121,6 @@ extension URLClient: URLClientProtocol { } } - #if swift(>=5.5) - - @available(macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 8.0, *) public func receive() async throws -> Data { switch request { case .failure(let error): @@ -142,9 +146,6 @@ extension URLClient: URLClientProtocol { } } - #endif - - public func build(_ apply: (inout URLRequestBuilder) -> Void) -> URLClient { var client = self apply(&client.builder) diff --git a/Sources/HTTPFluent/URLClientProtocol+Async.swift b/Sources/HTTPFluent/URLClientProtocol+Async.swift index 0804844..dd44ebe 100644 --- a/Sources/HTTPFluent/URLClientProtocol+Async.swift +++ b/Sources/HTTPFluent/URLClientProtocol+Async.swift @@ -7,21 +7,15 @@ // This code is licensed under the MIT license (see LICENSE for details). // -import Foundation - -#if canImport(Combine) import Combine -#endif - -#if swift(>=5.5) +import Foundation -@available(macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 8.0, *) public extension URLClientProtocol { func receive(decode: @escaping Decoders.Decode) async throws -> Response { try decode(await receive()) } - func receive( + func receive( decoding type: Response.Type = Response.self, decoder: Decoder ) async throws -> Response where Response: Decodable, Decoder: TopLevelDecoder, Decoder.Input == Data { @@ -34,5 +28,3 @@ public extension URLClientProtocol { try await receive(decode: Decoders.json(type)) } } - -#endif diff --git a/Sources/HTTPFluent/URLClientProtocol+Combine.swift b/Sources/HTTPFluent/URLClientProtocol+Combine.swift index 59cdd8b..62782a5 100644 --- a/Sources/HTTPFluent/URLClientProtocol+Combine.swift +++ b/Sources/HTTPFluent/URLClientProtocol+Combine.swift @@ -7,11 +7,9 @@ // This code is licensed under the MIT license (see LICENSE for details). // -#if canImport(Combine) import Combine import Foundation -@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) public extension URLClientProtocol { /** Decodes the `Data` in the published stream using `decode`. @@ -42,7 +40,7 @@ public extension URLClientProtocol { If `decoder` is `JSONDecoder`, the `Accept: application/json` is sent automatically with the request. */ - func receivePublisher( + func receivePublisher( decoding type: Response.Type = Response.self, decoder: Decoder ) -> AnyPublisher where Response: Decodable, Decoder: TopLevelDecoder, Decoder.Input == Data { @@ -58,5 +56,3 @@ public extension URLClientProtocol { } } - -#endif \ No newline at end of file diff --git a/Sources/HTTPFluent/URLClientProtocol+Receive.swift b/Sources/HTTPFluent/URLClientProtocol+Receive.swift index 9251da5..1daea23 100644 --- a/Sources/HTTPFluent/URLClientProtocol+Receive.swift +++ b/Sources/HTTPFluent/URLClientProtocol+Receive.swift @@ -7,11 +7,8 @@ // This code is licensed under the MIT license (see LICENSE for details). // -import Foundation - -#if canImport(Combine) import Combine -#endif +import Foundation //swiftlint:disable function_default_parameter_at_end public extension URLClientProtocol { @@ -24,7 +21,7 @@ public extension URLClientProtocol { */ func receive( on queue: DispatchQueue = DispatchQueue.global(), - callback: @escaping (URLResult) -> Void) + callback: @escaping @Sendable (URLResult) -> Void) { receive(on: queue, callback: callback) } @@ -38,7 +35,7 @@ public extension URLClientProtocol { func receive( on queue: DispatchQueue = DispatchQueue.global(), decode: @escaping Decoders.Decode, - callback: @escaping (URLResult) -> Void + callback: @escaping @Sendable (URLResult) -> Void ) { receive(on: queue) { result in do { @@ -51,11 +48,11 @@ public extension URLClientProtocol { } } - func receive( + func receive( decoding type: Response.Type = Response.self, decoder: Decoder, on queue: DispatchQueue = DispatchQueue.global(), - callback: @escaping (URLResult) -> Void + callback: @escaping @Sendable (URLResult) -> Void ) where Response: Decodable, Decoder: TopLevelDecoder, Decoder.Input == Data { let fluent = decoder is JSONDecoder ? accept(.json) : self return fluent.receive(on: queue, decode: Decoders.decode(type, with: decoder), callback: callback) @@ -64,7 +61,7 @@ public extension URLClientProtocol { func receive( json type: Response.Type = Response.self, on queue: DispatchQueue = DispatchQueue.global(), - callback: @escaping (URLResult) -> Void + callback: @escaping @Sendable (URLResult) -> Void ) where Response: Decodable { receive(on: queue, decode: Decoders.json(type), callback: callback) } diff --git a/Sources/HTTPFluent/URLClientProtocol.swift b/Sources/HTTPFluent/URLClientProtocol.swift index 2beb38a..292a60a 100644 --- a/Sources/HTTPFluent/URLClientProtocol.swift +++ b/Sources/HTTPFluent/URLClientProtocol.swift @@ -7,33 +7,27 @@ // This code is licensed under the MIT license (see LICENSE for details). // -import Foundation - -#if canImport(Combine) import Combine -#endif +import Foundation /** Adds the ability to publish a `URLRequest` built using `URLRequestBuilderProtocol`. */ public protocol URLClientProtocol: URLRequestBuilderProtocol { -#if canImport(Combine) /** Publishes the `Data` result of executing the underlying `URLRequest`. */ - @available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) var receivePublisher: AnyPublisher { get } -#endif - -#if swift(>=5.5) - @available(macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 8.0, *) + + /** + Gets the result of the `URLRequest` asynchronously as `Data`. + */ func receive() async throws -> Data -#endif /** Executes the underlying `URLRequest` and calls `callback` on the given `queue` when it completes. */ - func receive(on queue: DispatchQueue, callback: @escaping (URLResult) -> Void) + func receive(on queue: DispatchQueue, callback: @escaping @Sendable (URLResult) -> Void) } diff --git a/Sources/HTTPFluent/URLRequestBuilder.swift b/Sources/HTTPFluent/URLRequestBuilder.swift index fa2dd94..54c1b85 100644 --- a/Sources/HTTPFluent/URLRequestBuilder.swift +++ b/Sources/HTTPFluent/URLRequestBuilder.swift @@ -13,14 +13,14 @@ import Foundation import FoundationNetworking #endif -public struct URLRequestBuilder { +public struct URLRequestBuilder: Sendable { public typealias Apply = (inout Self) -> Void fileprivate var error: URLError? fileprivate var components: URLComponents fileprivate var method: String = "GET" fileprivate var headers: [String: String] = [:] - fileprivate var body: (() throws -> Data)? + fileprivate var body: (@Sendable () throws -> Data)? private init(components: URLComponents) { self.components = components @@ -109,7 +109,7 @@ extension URLRequestBuilder { } } - static func buildBody(_ body: @escaping () throws -> Data) -> URLRequestBuilder.Apply { + static func buildBody(_ body: @escaping @Sendable () throws -> Data) -> URLRequestBuilder.Apply { return { builder in builder.body = body } diff --git a/Sources/HTTPFluent/URLRequestBuilderProtocol+HTTPBody.swift b/Sources/HTTPFluent/URLRequestBuilderProtocol+HTTPBody.swift index b4b8410..871a00b 100644 --- a/Sources/HTTPFluent/URLRequestBuilderProtocol+HTTPBody.swift +++ b/Sources/HTTPFluent/URLRequestBuilderProtocol+HTTPBody.swift @@ -7,11 +7,8 @@ // This code is licensed under the MIT license (see LICENSE for details). // -import Foundation - -#if canImport(Combine) import Combine -#endif +import Foundation #if canImport(FoundationNetworking) import FoundationNetworking @@ -24,7 +21,7 @@ public extension URLRequestBuilderProtocol { Don't use this. Use higher-level functions like `post(json:)`, which ultimately call this one. */ - func body(_ body: @escaping () throws -> Data) -> Self { + func body(_ body: @escaping @Sendable () throws -> Data) -> Self { build(URLRequestBuilder.buildBody(body)) } @@ -44,7 +41,7 @@ public extension URLRequestBuilderProtocol { } /// Encode a type in the HTTP body without setting an HTTP method. - func send( + func send( _ request: Request, encoder: Encoder ) -> Self where Encoder.Output == Data { @@ -52,7 +49,7 @@ public extension URLRequestBuilderProtocol { } /// Send JSON in the HTTP body without setting an HTTP method. - func send(json request: Request) -> Self { + func send(json request: Request) -> Self { send(request, encoder: JSONEncoder()).content(type: .json) } @@ -72,7 +69,7 @@ public extension URLRequestBuilderProtocol { } /// Post `Request` encoded with `Encoder` - func post( + func post( _ request: Request, encoder: Encoder ) -> Self where Encoder.Output == Data { @@ -80,7 +77,7 @@ public extension URLRequestBuilderProtocol { } /// Post `Request` encoded as JSON - func post(json request: Request) -> Self { + func post(json request: Request) -> Self { send(json: request).method(.post) } @@ -100,7 +97,7 @@ public extension URLRequestBuilderProtocol { } /// Patch `Request` encoded with `Encoder` - func patch( + func patch( _ request: Request, encoder: Encoder ) -> Self where Encoder.Output == Data { @@ -108,7 +105,7 @@ public extension URLRequestBuilderProtocol { } /// Patch `Request` encoded as JSON - func patch(json request: Request) -> Self { + func patch(json request: Request) -> Self { send(json: request).method(.patch) } @@ -128,7 +125,7 @@ public extension URLRequestBuilderProtocol { } /// Put `Request` encoded by `Encoder` - func put( + func put( _ request: Request, encoder: Encoder ) -> Self where Encoder.Output == Data { @@ -136,7 +133,7 @@ public extension URLRequestBuilderProtocol { } /// Put `Request` encoded as JSON - func put(json request: Request) -> Self { + func put(json request: Request) -> Self { send(json: request).method(.put) } } diff --git a/Sources/HTTPFluent/URLResult.swift b/Sources/HTTPFluent/URLResult.swift index e83e0fc..565199d 100644 --- a/Sources/HTTPFluent/URLResult.swift +++ b/Sources/HTTPFluent/URLResult.swift @@ -9,4 +9,4 @@ import Foundation -public typealias URLResult = Result +public typealias URLResult = Result From 39120dd4f45f94e615d1eeb02397de002f742e5d Mon Sep 17 00:00:00 2001 From: Gregory Higley Date: Mon, 15 Sep 2025 23:45:01 -0400 Subject: [PATCH 16/18] Remove unnecessary doc comment --- Sources/HTTPFluent/URLClient.swift | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/Sources/HTTPFluent/URLClient.swift b/Sources/HTTPFluent/URLClient.swift index b454d5a..4fc6440 100644 --- a/Sources/HTTPFluent/URLClient.swift +++ b/Sources/HTTPFluent/URLClient.swift @@ -14,29 +14,6 @@ import Foundation import FoundationNetworking #endif -/** - Perform HTTP operations on a base URL. - - Most uses of HTTP at Patron and other organizations - perform one or more operations against some API hosted - at a specific base URL. `HTTPClient` makes this very - easy and natural using a fluent interface. - - This is best illustrated with an example: - - ```swift - let jwt: String = "...imagine a JWT..." - let token = "xyz123" - let attendee = Attendee(token: token) - let client = HTTPClient(url: "https://api.patron.com/api") - await client - .path("attendee", token) // api/attendee/xyz123 - .query(7, forName: "x") // ?x=7 - .authorization(bearer: jwt) - .post(json: attendee) - .receive(json: AttendeeResponse.self) - ``` - */ public struct URLClient: Sendable { public typealias ResponseHandler = @Sendable (Data?, URLResponse?) throws -> Data From 80e254184b47b710f53ab9e66629c97ed7852def Mon Sep 17 00:00:00 2001 From: Gregory Higley Date: Sat, 4 Oct 2025 21:32:40 -0400 Subject: [PATCH 17/18] Sendable --- Sources/HTTPFluent/Decoders.swift | 4 ++-- Sources/HTTPFluent/URLClientProtocol+Async.swift | 6 +++--- Sources/HTTPFluent/URLClientProtocol+Combine.swift | 5 ++--- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/Sources/HTTPFluent/Decoders.swift b/Sources/HTTPFluent/Decoders.swift index 1f22edd..a24600c 100644 --- a/Sources/HTTPFluent/Decoders.swift +++ b/Sources/HTTPFluent/Decoders.swift @@ -27,7 +27,7 @@ public struct Decoders: Sendable { public static let string: Decode = Self.string(encoding: .utf8) - public static func decode(_ type: T.Type, with decoder: Decoder) -> Decode where Decoder.Input == Data { + public static func decode(_ type: T.Type, with decoder: Decoder) -> Decode where Decoder.Input == Data { return { data in do { return try decoder.decode(type, from: data) @@ -37,7 +37,7 @@ public struct Decoders: Sendable { } } - public static func json(_ type: T.Type) -> Decode { + public static func json(_ type: T.Type) -> Decode { return decode(type, with: JSONDecoder()) } } diff --git a/Sources/HTTPFluent/URLClientProtocol+Async.swift b/Sources/HTTPFluent/URLClientProtocol+Async.swift index dd44ebe..4d32fe7 100644 --- a/Sources/HTTPFluent/URLClientProtocol+Async.swift +++ b/Sources/HTTPFluent/URLClientProtocol+Async.swift @@ -11,18 +11,18 @@ import Combine import Foundation public extension URLClientProtocol { - func receive(decode: @escaping Decoders.Decode) async throws -> Response { + func receive(decode: @escaping Decoders.Decode) async throws -> Response { try decode(await receive()) } - func receive( + func receive( decoding type: Response.Type = Response.self, decoder: Decoder ) async throws -> Response where Response: Decodable, Decoder: TopLevelDecoder, Decoder.Input == Data { try await receive(decode: Decoders.decode(type, with: decoder)) } - func receive( + func receive( json type: Response.Type ) async throws -> Response { try await receive(decode: Decoders.json(type)) diff --git a/Sources/HTTPFluent/URLClientProtocol+Combine.swift b/Sources/HTTPFluent/URLClientProtocol+Combine.swift index 62782a5..6438597 100644 --- a/Sources/HTTPFluent/URLClientProtocol+Combine.swift +++ b/Sources/HTTPFluent/URLClientProtocol+Combine.swift @@ -40,7 +40,7 @@ public extension URLClientProtocol { If `decoder` is `JSONDecoder`, the `Accept: application/json` is sent automatically with the request. */ - func receivePublisher( + func receivePublisher( decoding type: Response.Type = Response.self, decoder: Decoder ) -> AnyPublisher where Response: Decodable, Decoder: TopLevelDecoder, Decoder.Input == Data { @@ -49,10 +49,9 @@ public extension URLClientProtocol { } /// Decodes the `Data` in the published stream using a default `JSONDecoder`. - func receivePublisher( + func receivePublisher( json type: Response.Type = Response.self ) -> AnyPublisher { accept(.json).receivePublisher(decode: Decoders.json(type)) } - } From 3c763ebba305d236ea1c49bc5776df283c8c4f44 Mon Sep 17 00:00:00 2001 From: Gregory Higley Date: Sat, 4 Oct 2025 22:29:02 -0400 Subject: [PATCH 18/18] Tests --- .../HTTPFluentAsyncTests.swift | 20 +++++--------- .../HTTPFluentCombineTests.swift | 6 ----- .../HTTPFluentReceiveTests.swift | 27 +++++++------------ 3 files changed, 16 insertions(+), 37 deletions(-) diff --git a/Tests/HTTPFluentTests/HTTPFluentAsyncTests.swift b/Tests/HTTPFluentTests/HTTPFluentAsyncTests.swift index c5624ef..a606e49 100644 --- a/Tests/HTTPFluentTests/HTTPFluentAsyncTests.swift +++ b/Tests/HTTPFluentTests/HTTPFluentAsyncTests.swift @@ -7,26 +7,18 @@ // This code is licensed under the MIT license (see LICENSE for details). // -#if canImport(Combine) import Combine -#endif - -import XCTest import HTTPFluent +import Testing -#if swift(>=5.5) - -@available(macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 8.0, *) -class HTTPFluentAsyncTests: XCTestCase { - func testGetJSON() async throws { +struct HTTPFluentAsyncTests { + @Test func testGetJSON() async throws { let slideshows = try await URLClient.bin.path("json").receive(json: Slideshows.self) - XCTAssertEqual(slideshows.slideshow.title, "Sample Slide Show") + #expect(slideshows.slideshow.title == "Sample Slide Show") } - func testHeaders() async throws { + @Test func testHeaders() async throws { let response = try await URLClient.bin.path("headers").content(type: .json, charset: .utf8).receive(json: HTTPHeaderResponse.self) - XCTAssertEqual(response.headers["Content-Type"], "application/json;charset=utf-8") + #expect(response.headers["Content-Type"] == "application/json;charset=utf-8") } } - -#endif diff --git a/Tests/HTTPFluentTests/HTTPFluentCombineTests.swift b/Tests/HTTPFluentTests/HTTPFluentCombineTests.swift index a2f7f54..31cce67 100644 --- a/Tests/HTTPFluentTests/HTTPFluentCombineTests.swift +++ b/Tests/HTTPFluentTests/HTTPFluentCombineTests.swift @@ -7,13 +7,10 @@ // This code is licensed under the MIT license (see LICENSE for details). // -#if canImport(Combine) - import Combine import XCTest import HTTPFluent -@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) func fulfill(_ e: XCTestExpectation, expectError: Bool = false) -> (Subscribers.Completion) -> Void { return { c in if expectError { @@ -30,7 +27,6 @@ func fulfill(_ e: XCTestExpectation, expectError: Bool = false) -> (Su } } -@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) final class HttpFluentCombineTests: XCTestCase { func testGetJSON() { @@ -80,5 +76,3 @@ final class HttpFluentCombineTests: XCTestCase { cancellable.cancel() } } - -#endif diff --git a/Tests/HTTPFluentTests/HTTPFluentReceiveTests.swift b/Tests/HTTPFluentTests/HTTPFluentReceiveTests.swift index 3e5e137..03c5859 100644 --- a/Tests/HTTPFluentTests/HTTPFluentReceiveTests.swift +++ b/Tests/HTTPFluentTests/HTTPFluentReceiveTests.swift @@ -7,24 +7,17 @@ // This code is licensed under the MIT license (see LICENSE for details). // -import XCTest import HTTPFluent +import Testing -class HTTPFluentReceiveTests: XCTestCase { - - func testGetJSON() { - let e = expectation(description: "http") - URLClient.bin - .path("json") - .receive(json: Slideshows.self) { result in - do { - try print(result.get()) - } catch { - XCTFail("\(error)") +struct HTTPFluentReceiveTests { + @Test func testGetJSON() async throws { + _ = try await withCheckedThrowingContinuation { cont in + URLClient.bin + .path("json") + .receive(json: Slideshows.self) { result in + cont.resume(with: result) } - e.fulfill() - } - wait(for: [e], timeout: 10) + } } - -} +} \ No newline at end of file