Skip to content

Commit

Permalink
Merge pull request #62 from freshOS/encodable_post
Browse files Browse the repository at this point in the history
Enables using Encodable as POST, PUT, PATCH body
  • Loading branch information
s4cha authored Nov 14, 2023
2 parents efbab68 + 47f9ceb commit 190d35e
Show file tree
Hide file tree
Showing 12 changed files with 309 additions and 29 deletions.
2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// swift-tools-version:5.3
// swift-tools-version:5.9
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription
Expand Down
12 changes: 12 additions & 0 deletions Sources/Networking/Calls/NetworkingClient+Data.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ public extension NetworkingClient {
func post(_ route: String, params: Params = Params()) -> AnyPublisher<Data, Error> {
request(.post, route, params: params).publisher()
}

func post<E: Encodable>(_ route: String, body: E) -> AnyPublisher<Data, Error> {
request(.post, route, encodableBody: body).publisher()
}

func put(_ route: String, params: Params = Params()) -> AnyPublisher<Data, Error> {
request(.put, route, params: params).publisher()
Expand All @@ -25,6 +29,10 @@ public extension NetworkingClient {
func patch(_ route: String, params: Params = Params()) -> AnyPublisher<Data, Error> {
request(.patch, route, params: params).publisher()
}

func patch<E: Encodable>(_ route: String, body: E) -> AnyPublisher<Data, Error> {
request(.patch, route, encodableBody: body).publisher()
}

func delete(_ route: String, params: Params = Params()) -> AnyPublisher<Data, Error> {
request(.delete, route, params: params).publisher()
Expand All @@ -41,6 +49,10 @@ public extension NetworkingClient {
try await request(.post, route, params: params).execute()
}

func post<E: Encodable>(_ route: String, body: E) async throws -> Data {
try await request(.post, route, encodableBody: body).execute()
}

func put(_ route: String, params: Params = Params()) async throws -> Data {
try await request(.put, route, params: params).execute()
}
Expand Down
37 changes: 37 additions & 0 deletions Sources/Networking/Calls/NetworkingClient+Decodable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,16 @@ public extension NetworkingClient {
.eraseToAnyPublisher()
}

func post<E: Encodable, T: Decodable>(_ route: String,
body: E,
keypath: String? = nil
) -> AnyPublisher<T, Error> {
return post(route, body: body)
.tryMap { json -> T in try self.toModel(json, keypath: keypath) }
.receive(on: DispatchQueue.main)
.eraseToAnyPublisher()
}

// Array version
func post<T: Decodable>(_ route: String,
params: Params = Params(),
Expand Down Expand Up @@ -79,6 +89,17 @@ public extension NetworkingClient {
.eraseToAnyPublisher()
}


func patch<E: Encodable, T: Decodable>(_ route: String,
body: E,
keypath: String? = nil
) -> AnyPublisher<T, Error> {
return patch(route, body: body)
.tryMap { json -> T in try self.toModel(json, keypath: keypath) }
.receive(on: DispatchQueue.main)
.eraseToAnyPublisher()
}

// Array version
func patch<T: Decodable>(_ route: String,
params: Params = Params(),
Expand Down Expand Up @@ -137,6 +158,14 @@ public extension NetworkingClient {
return try self.toModel(json, keypath: keypath)
}

func post<E: Encodable, T: Decodable>(_ route: String,
body: E,
keypath: String? = nil
) async throws -> T {
let json: Any = try await post(route, body: body)
return try self.toModel(json, keypath: keypath)
}

func post<T: Decodable>(_ route: String,
params: Params = Params(),
keypath: String? = nil) async throws -> T where T: Collection {
Expand Down Expand Up @@ -175,6 +204,14 @@ public extension NetworkingClient {
return try self.toModel(json, keypath: keypath)
}

func patch<E: Encodable, T: Decodable>(_ route: String,
body: E,
keypath: String? = nil
) async throws -> T {
let json: Any = try await patch(route, body: body)
return try self.toModel(json, keypath: keypath)
}

func delete<T: Decodable>(_ route: String,
params: Params = Params(),
keypath: String? = nil) async throws -> T {
Expand Down
20 changes: 20 additions & 0 deletions Sources/Networking/Calls/NetworkingClient+JSON.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ public extension NetworkingClient {
func post(_ route: String, params: Params = Params()) -> AnyPublisher<Any, Error> {
post(route, params: params).toJSON()
}

func post<E: Encodable>(_ route: String, body: E) -> AnyPublisher<Any, Error> {
post(route, body: body).toJSON()
}

func put(_ route: String, params: Params = Params()) -> AnyPublisher<Any, Error> {
put(route, params: params).toJSON()
Expand All @@ -25,6 +29,10 @@ public extension NetworkingClient {
func patch(_ route: String, params: Params = Params()) -> AnyPublisher<Any, Error> {
patch(route, params: params).toJSON()
}

func patch<E: Encodable>(_ route: String, body: E) -> AnyPublisher<Any, Error> {
patch(route, body: body).toJSON()
}

func delete(_ route: String, params: Params = Params()) -> AnyPublisher<Any, Error> {
delete(route, params: params).toJSON()
Expand All @@ -45,6 +53,12 @@ public extension NetworkingClient {
return try JSONSerialization.jsonObject(with: data, options: [])
}

func post<E: Encodable>(_ route: String, body: E) async throws -> Any {
let req = request(.post, route, encodableBody: body)
let data = try await req.execute()
return try JSONSerialization.jsonObject(with: data, options: [])
}

func put(_ route: String, params: Params = Params()) async throws -> Any {
let req = request(.put, route, params: params)
let data = try await req.execute()
Expand All @@ -57,6 +71,12 @@ public extension NetworkingClient {
return try JSONSerialization.jsonObject(with: data, options: [])
}

func patch<E: Encodable>(_ route: String, body: E) async throws -> Any {
let req = request(.patch, route, encodableBody: body)
let data = try await req.execute()
return try JSONSerialization.jsonObject(with: data, options: [])
}

func delete(_ route: String, params: Params = Params()) async throws -> Any {
let req = request(.delete, route, params: params)
let data = try await req.execute()
Expand Down
50 changes: 42 additions & 8 deletions Sources/Networking/Calls/NetworkingClient+Requests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,32 +10,66 @@ import Combine

public extension NetworkingClient {

func getRequest(_ route: String, params: Params = Params()) -> NetworkingRequest {
func getRequest(_ route: String, params: Params = Params()) -> NetworkingRequest<String> {
request(.get, route, params: params)
}

func postRequest(_ route: String, params: Params = Params()) -> NetworkingRequest {
func postRequest(_ route: String, params: Params = Params()) -> NetworkingRequest<String> {
request(.post, route, params: params)
}

func putRequest(_ route: String, params: Params = Params()) -> NetworkingRequest {
func putRequest(_ route: String, params: Params = Params()) -> NetworkingRequest<String> {
request(.put, route, params: params)
}

func patchRequest(_ route: String, params: Params = Params()) -> NetworkingRequest {
func patchRequest(_ route: String, params: Params = Params()) -> NetworkingRequest<String> {
request(.patch, route, params: params)
}

func deleteRequest(_ route: String, params: Params = Params()) -> NetworkingRequest {
func deleteRequest(_ route: String, params: Params = Params()) -> NetworkingRequest<String> {
request(.delete, route, params: params)
}

internal func request(_ httpMethod: HTTPMethod, _ route: String, params: Params = Params()) -> NetworkingRequest {
let req = NetworkingRequest()
internal func request(_ httpMethod: HTTPMethod,
_ route: String,
params: Params = Params()
) -> NetworkingRequest<String> {
let req = NetworkingRequest<String>()
req.httpMethod = httpMethod
req.route = route
req.params = params


let updateRequest = { [weak req, weak self] in
guard let self = self else { return }
req?.baseURL = self.baseURL
req?.logLevel = self.logLevel
req?.headers = self.headers
req?.parameterEncoding = self.parameterEncoding
req?.sessionConfiguration = self.sessionConfiguration
req?.timeout = self.timeout
}
updateRequest()
req.requestRetrier = { [weak self] in
self?.requestRetrier?($0, $1)?
.handleEvents(receiveOutput: { _ in
updateRequest()
})
.eraseToAnyPublisher()
}
return req
}

internal func request<E: Encodable>(_ httpMethod: HTTPMethod,
_ route: String,
params: Params = Params(),
encodableBody: E? = nil
) -> NetworkingRequest<E> {
let req = NetworkingRequest<E>()
req.httpMethod = httpMethod
req.route = route
req.params = Params()
req.encodableBody = encodableBody

let updateRequest = { [weak req, weak self] in
guard let self = self else { return }
req?.baseURL = self.baseURL
Expand Down
17 changes: 17 additions & 0 deletions Sources/Networking/Calls/NetworkingClient+Void.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@ public extension NetworkingClient {
.map { (data: Data) -> Void in () }
.eraseToAnyPublisher()
}

func post<E: Encodable>(_ route: String, body: E) -> AnyPublisher<Void, Error> {
post(route, body: body)
.map { (data: Data) -> Void in () }
.eraseToAnyPublisher()
}

func put(_ route: String, params: Params = Params()) -> AnyPublisher<Void, Error> {
put(route, params: params)
Expand All @@ -33,6 +39,12 @@ public extension NetworkingClient {
.map { (data: Data) -> Void in () }
.eraseToAnyPublisher()
}

func patch<E: Encodable>(_ route: String, body: E) -> AnyPublisher<Void, Error> {
patch(route, body: body)
.map { (data: Data) -> Void in () }
.eraseToAnyPublisher()
}

func delete(_ route: String, params: Params = Params()) -> AnyPublisher<Void, Error> {
delete(route, params: params)
Expand All @@ -53,6 +65,11 @@ public extension NetworkingClient {
_ = try await req.execute()
}

func post<E: Encodable>(_ route: String, body: E) async throws {
let req = request(.post, route, encodableBody: body)
_ = try await req.execute()
}

func put(_ route: String, params: Params = Params()) async throws {
let req = request(.put, route, params: params)
_ = try await req.execute()
Expand Down
48 changes: 29 additions & 19 deletions Sources/Networking/NetworkingRequest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,14 @@ import Combine

public typealias NetworkRequestRetrier = (_ request: URLRequest, _ error: Error) -> AnyPublisher<Void, Error>?

public class NetworkingRequest: NSObject {
public class NetworkingRequest<E: Encodable>: NSObject, URLSessionTaskDelegate {

var parameterEncoding = ParameterEncoding.urlEncoded
var baseURL = ""
var route = ""
var httpMethod = HTTPMethod.get
public var params = Params()
public var encodableBody: E?
var headers = [String: String]()
var multipartData: [MultipartData]?
var logLevel: NetworkingLogLevel {
Expand Down Expand Up @@ -196,12 +197,22 @@ public class NetworkingRequest: NSObject {
}

if httpMethod != .get && multipartData == nil {
switch parameterEncoding {
case .urlEncoded:
request.httpBody = params.asPercentEncodedString().data(using: .utf8)
case .json:
let jsonData = try? JSONSerialization.data(withJSONObject: params)
request.httpBody = jsonData
if let encodableBody {
let jsonEncoder = JSONEncoder()
do {
let data = try jsonEncoder.encode(encodableBody)
request.httpBody = data
} catch {
print(error)
}
} else {
switch parameterEncoding {
case .urlEncoded:
request.httpBody = params.asPercentEncodedString().data(using: .utf8)
case .json:
let jsonData = try? JSONSerialization.data(withJSONObject: params)
request.httpBody = jsonData
}
}
}

Expand All @@ -228,6 +239,17 @@ public class NetworkingRequest: NSObject {
.reduce(Data.init(), +)
+ boundaryEnding
}

public func urlSession(_ session: URLSession,
task: URLSessionTask,
didSendBodyData bytesSent: Int64,
totalBytesSent: Int64,
totalBytesExpectedToSend: Int64) {
let progress = Progress(totalUnitCount: totalBytesExpectedToSend)
progress.completedUnitCount = totalBytesSent
progressPublisher.send(progress)
}

}

// Thansks to https://stackoverflow.com/questions/26364914/http-request-in-swift-with-post-method
Expand All @@ -241,18 +263,6 @@ extension CharacterSet {
}()
}

extension NetworkingRequest: URLSessionTaskDelegate {
public func urlSession(_ session: URLSession,
task: URLSessionTask,
didSendBodyData bytesSent: Int64,
totalBytesSent: Int64,
totalBytesExpectedToSend: Int64) {
let progress = Progress(totalUnitCount: totalBytesExpectedToSend)
progress.completedUnitCount = totalBytesSent
progressPublisher.send(progress)
}
}

public enum ParameterEncoding {
case urlEncoded
case json
Expand Down
Loading

0 comments on commit 190d35e

Please sign in to comment.