Skip to content

Commit

Permalink
Merge pull request #9 from MFB-Technologies-Inc/cleanup-3.0.0
Browse files Browse the repository at this point in the history
Cleanup 3.0.0
  • Loading branch information
roanutil authored Mar 29, 2023
2 parents 698fc3b + 845ab74 commit a153c85
Show file tree
Hide file tree
Showing 17 changed files with 1,103 additions and 1,040 deletions.
35 changes: 16 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
[![CI](https://github.com/MFB-Technologies-Inc/NetworkService/actions/workflows/ci.yml/badge.svg)](https://github.com/MFB-Technologies-Inc/NetworkService/actions/workflows/ci.yml)
[![codecov](https://codecov.io/gh/MFB-Technologies-Inc/NetworkService/branch/main/graph/badge.svg?token=3LpLfgAhq3)](https://codecov.io/gh/MFB-Technologies-Inc/NetworkService)

Reactive wrapper for URLSession using Combine. At its core, the library consist of the `NetworkServiceClient` protocol along with a minimal implementation `NetworkService`.
Async wrapper and dependency injection layer for URLSession. At its core, the library consist of the `NetworkServiceClient` protocol along with a minimal implementation `NetworkService`.

### TopLevelCodable
A notable convenience the library provides is the `TopLevelCodable` protocol that enables easy encoding and decoding of conforming types. The protocol associates a `TopLevelEncoder` and `TopLevelDecoder` with a given type so that it is used by the library without explicitly passing it as a parameter. Additionally, `TopLevelEncodable` and `TopLevelDecodable` are included.
Expand All @@ -23,42 +23,39 @@ let foo = Foo(bar: 0)
```
#### GET
```swift
let publisher: AnyPublisher<Foo, Failuer> = networkService.get(url)
let cancellable = publisher.assertNoFailure().sink { foo in
print(foo.bar)
}
let result: Result<Foo, NetworkService.Failure> = await networkService.get(url)
let foo = try result.get()
print(foo.bar)
```

#### POST
```swift
let publisher: AnyPublisher<Foo, Failuer> = networkService.post(foo, to: url)
let cancellable = publisher.assertNoFailure().sink { foo in
print(foo.bar)
}
let result: Result<Foo, NetworkService.Failure> = await networkService.post(foo, to: url)
let foo = try result.get()
print(foo.bar)
```

#### PUT
```swift
let publisher: AnyPublisher<Foo, Failuer> = networkService.put(foo, to: url)
let cancellable = publisher.assertNoFailure().sink { foo in
print(foo.bar)
}
let result: Result<Foo, NetworkService.Failure> = await networkService.put(foo, to: url)
let foo = try result.get()
print(foo.bar)
```

#### DELETE
```swift
let publisher: AnyPublisher<Foo, Failuer> = networkService.get(url)
let cancellable = publisher.assertNoFailure().sink { _ in }
let result: Result<Foo, NetworkService.Failure> = await networkService.get(url)
let foo = try result.get()
print(foo.bar)
```

#### Start
```swift
var request = URLRequest(url: url)
request.method = .GET
let publisher: AnyPublisher<Foo, Failuer> = networkService.start(request)
let cancellable = publisher.assertNoFailure().sink { foo in
print(foo.bar)
}
let result = await networkService.start(request)
let foo = try result.get()
print(foo.bar)
```
## NetworkServiceTestHelper

Expand Down
1 change: 0 additions & 1 deletion Sources/NetworkService/NetworkService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,4 @@ public final class NetworkService {

// MARK: NetworkService+NetworkServiceClient

@available(swift 5.5)
extension NetworkService: NetworkServiceClient {}
256 changes: 129 additions & 127 deletions Sources/NetworkService/NetworkServiceClient+Post.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
// This source code is licensed under the MIT license found in the
// LICENSE file in the root directory of this source tree.

import Combine
import Foundation

extension NetworkServiceClient {
Expand All @@ -25,140 +24,143 @@ extension NetworkServiceClient {
let request = URLRequest.build(url: url, body: body, headers: headers, method: .POST)
return await start(request)
}
}

/// - Parameters:
/// - body: The body of the request as `Encodable`
/// - url: The destination for the request
/// - headers: HTTP headers for the request
/// - encoder: `TopLevelEncoder` for encoding the request body
/// - Returns: Type erased publisher with `Data` output and `NetworkService`'s error domain for failure
public func post<RequestBody, Encoder>(
_ body: RequestBody,
to url: URL,
headers: [HTTPHeader],
encoder: Encoder
) async -> Result<Data, Failure>
where RequestBody: Encodable,
Encoder: TopLevelEncoder,
Encoder.Output == Data
{
do {
let body = try encoder.encode(body)
return await post(body, to: url, headers: headers)
} catch {
return .failure(Failure.unknown(error as NSError))
#if canImport(Combine)
import Combine
extension NetworkServiceClient {
/// - Parameters:
/// - body: The body of the request as `Encodable`
/// - url: The destination for the request
/// - headers: HTTP headers for the request
/// - encoder: `TopLevelEncoder` for encoding the request body
/// - Returns: Type erased publisher with `Data` output and `NetworkService`'s error domain for failure
public func post<RequestBody, Encoder>(
_ body: RequestBody,
to url: URL,
headers: [HTTPHeader],
encoder: Encoder
) async -> Result<Data, Failure>
where RequestBody: Encodable,
Encoder: TopLevelEncoder,
Encoder.Output == Data
{
do {
let body = try encoder.encode(body)
return await post(body, to: url, headers: headers)
} catch {
return .failure(Failure.unknown(error as NSError))
}
}
}

/// - Parameters:
/// - body: The body of the request as `TopLevelEnodable`
/// - url: The destination for the request
/// - headers: HTTP headers for the request
/// - Returns: Type erased publisher with `Data` output and `NetworkService`'s error domain for failure
public func post<RequestBody>(
_ body: RequestBody,
to url: URL,
headers: [HTTPHeader]
) async -> Result<Data, Failure>
where RequestBody: TopLevelEncodable
{
do {
let body = try RequestBody.encoder.encode(body)
return await post(body, to: url, headers: headers)
} catch let urlError as URLError {
return .failure(Failure.urlError(urlError))
} catch {
return .failure(Failure.unknown(error as NSError))
/// - Parameters:
/// - body: The body of the request as `TopLevelEnodable`
/// - url: The destination for the request
/// - headers: HTTP headers for the request
/// - Returns: Type erased publisher with `Data` output and `NetworkService`'s error domain for failure
public func post<RequestBody>(
_ body: RequestBody,
to url: URL,
headers: [HTTPHeader]
) async -> Result<Data, Failure>
where RequestBody: TopLevelEncodable
{
do {
let body = try RequestBody.encoder.encode(body)
return await post(body, to: url, headers: headers)
} catch let urlError as URLError {
return .failure(Failure.urlError(urlError))
} catch {
return .failure(Failure.unknown(error as NSError))
}
}
}

/// Send a post request to a `URL`
/// - Parameters:
/// - body: The body of the request as `Data`
/// - url: The destination for the request
/// - headers: HTTP headers for the request
/// - decoder:`TopLevelDecoder` for decoding the response body
/// - Returns: Type erased publisher with decoded output and `NetworkService`'s error domain for failure
public func post<ResponseBody, Decoder>(
_ body: Data,
to url: URL,
headers: [HTTPHeader] = [],
decoder: Decoder
) async -> Result<ResponseBody, Failure>
where ResponseBody: Decodable, Decoder: TopLevelDecoder, Decoder.Input == Data
{
var request = URLRequest(url: url)
request.httpBody = body
request.method = .POST
headers.forEach { request.addValue($0) }
return await start(request, with: decoder)
}
/// Send a post request to a `URL`
/// - Parameters:
/// - body: The body of the request as `Data`
/// - url: The destination for the request
/// - headers: HTTP headers for the request
/// - decoder:`TopLevelDecoder` for decoding the response body
/// - Returns: Type erased publisher with decoded output and `NetworkService`'s error domain for failure
public func post<ResponseBody, Decoder>(
_ body: Data,
to url: URL,
headers: [HTTPHeader] = [],
decoder: Decoder
) async -> Result<ResponseBody, Failure>
where ResponseBody: Decodable, Decoder: TopLevelDecoder, Decoder.Input == Data
{
var request = URLRequest(url: url)
request.httpBody = body
request.method = .POST
headers.forEach { request.addValue($0) }
return await start(request, with: decoder)
}

/// - Parameters:
/// - body: The body of the request as `Data`
/// - url: The destination for the request
/// - headers: HTTP headers for the request
/// - Returns: Type erased publisher with `TopLevelDecodable` output and `NetworkService`'s error domain for failure
public func post<ResponseBody>(
_ body: Data,
to url: URL,
headers: [HTTPHeader] = []
) async -> Result<ResponseBody, Failure>
where ResponseBody: TopLevelDecodable
{
await post(body, to: url, headers: headers, decoder: ResponseBody.decoder)
}
/// - Parameters:
/// - body: The body of the request as `Data`
/// - url: The destination for the request
/// - headers: HTTP headers for the request
/// - Returns: Type erased publisher with `TopLevelDecodable` output and `NetworkService`'s error domain for
/// failure
public func post<ResponseBody>(
_ body: Data,
to url: URL,
headers: [HTTPHeader] = []
) async -> Result<ResponseBody, Failure>
where ResponseBody: TopLevelDecodable
{
await post(body, to: url, headers: headers, decoder: ResponseBody.decoder)
}

/// Send a post request to a `URL`
/// - Parameters:
/// - body: The body of the request as a `Encodable` conforming type
/// - url: The destination for the request
/// - headers: HTTP headers for the request
/// - encoder:`TopLevelEncoder` for encoding the request body
/// - decoder:`TopLevelDecoder` for decoding the response body
/// - Returns: Type erased publisher with decoded output and `NetworkService`'s error domain for failure
public func post<RequestBody, ResponseBody, Encoder, Decoder>(
_ body: RequestBody,
to url: URL,
headers: [HTTPHeader] = [],
encoder: Encoder,
decoder: Decoder
) async -> Result<ResponseBody, Failure>
where RequestBody: Encodable,
ResponseBody: Decodable,
Encoder: TopLevelEncoder,
Encoder.Output == Data,
Decoder: TopLevelDecoder,
Decoder.Input == Data
{
do {
let body = try encoder.encode(body)
return await post(body, to: url, headers: headers, decoder: decoder)
} catch let urlError as URLError {
return .failure(Failure.urlError(urlError))
} catch {
return .failure(Failure.unknown(error as NSError))
/// Send a post request to a `URL`
/// - Parameters:
/// - body: The body of the request as a `Encodable` conforming type
/// - url: The destination for the request
/// - headers: HTTP headers for the request
/// - encoder:`TopLevelEncoder` for encoding the request body
/// - decoder:`TopLevelDecoder` for decoding the response body
/// - Returns: Type erased publisher with decoded output and `NetworkService`'s error domain for failure
public func post<RequestBody, ResponseBody, Encoder, Decoder>(
_ body: RequestBody,
to url: URL,
headers: [HTTPHeader] = [],
encoder: Encoder,
decoder: Decoder
) async -> Result<ResponseBody, Failure>
where RequestBody: Encodable,
ResponseBody: Decodable,
Encoder: TopLevelEncoder,
Encoder.Output == Data,
Decoder: TopLevelDecoder,
Decoder.Input == Data
{
do {
let body = try encoder.encode(body)
return await post(body, to: url, headers: headers, decoder: decoder)
} catch let urlError as URLError {
return .failure(Failure.urlError(urlError))
} catch {
return .failure(Failure.unknown(error as NSError))
}
}
}

/// Send a post request to a `URL`
/// - Parameters:
/// - body: The body of the request as a `TopLevelEnodable` conforming type
/// - url: The destination for the request
/// - headers: HTTP headers for the request
/// - Returns: Type erased publisher with `TopLevelDecodable` output and `NetworkService`'s error domain for failure
public func post<RequestBody, ResponseBody>(
_ body: RequestBody,
to url: URL,
headers: [HTTPHeader] = []
) async -> Result<ResponseBody, Failure>
where RequestBody: TopLevelEncodable,
ResponseBody: TopLevelDecodable
{
await post(body, to: url, headers: headers, encoder: RequestBody.encoder, decoder: ResponseBody.decoder)
/// Send a post request to a `URL`
/// - Parameters:
/// - body: The body of the request as a `TopLevelEnodable` conforming type
/// - url: The destination for the request
/// - headers: HTTP headers for the request
/// - Returns: Type erased publisher with `TopLevelDecodable` output and `NetworkService`'s error domain for
/// failure
public func post<RequestBody, ResponseBody>(
_ body: RequestBody,
to url: URL,
headers: [HTTPHeader] = []
) async -> Result<ResponseBody, Failure>
where RequestBody: TopLevelEncodable,
ResponseBody: TopLevelDecodable
{
await post(body, to: url, headers: headers, encoder: RequestBody.encoder, decoder: ResponseBody.decoder)
}
}
}

#if canImport(Combine)
extension NetworkServiceClient {}
#endif
Loading

0 comments on commit a153c85

Please sign in to comment.