Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enables using Encodable as POST, PUT, PATCH body #62

Merged
merged 13 commits into from
Nov 14, 2023
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
Loading