Skip to content

Commit

Permalink
0.39.0
Browse files Browse the repository at this point in the history
  • Loading branch information
dankinsoid committed Mar 10, 2024
1 parent 4c35352 commit fd7b907
Show file tree
Hide file tree
Showing 77 changed files with 351 additions and 351 deletions.
6 changes: 3 additions & 3 deletions Example/Sources/PetStore/CustomAuth.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ protocol TokenCacheService {
func clearToken()
}

extension NetworkClient {
extension APIClient {

func bearerAuth(_ service: TokenCacheService) -> NetworkClient {
func bearerAuth(_ service: TokenCacheService) -> APIClient {
// It's not required to create a .tokenCacheService config in this case, but it allows to override the token cache service and use it in other services, for instance, in a token refresher.
configs(\.tokenCacheService, service)
.auth(
Expand All @@ -24,7 +24,7 @@ extension NetworkClient {
}
}

extension NetworkClient.Configs {
extension APIClient.Configs {

var tokenCacheService: TokenCacheService {
get {
Expand Down
16 changes: 8 additions & 8 deletions Example/Sources/PetStore/PetStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ public struct PetStore {

// MARK: - BaseURL

var client: NetworkClient
var client: APIClient

public init(baseURL: BaseURL, fileID: String, line: UInt) {
client = NetworkClient(baseURL: baseURL.url)
client = APIClient(baseURL: baseURL.url)
.fileIDLine(fileID: fileID, line: line)
.bodyDecoder(PetStoreDecoder())
.tokenRefresher { client, _ in
Expand All @@ -29,7 +29,7 @@ public extension PetStore {

struct Pet {

var client: NetworkClient
var client: APIClient

public func update(_ pet: PetModel) async throws -> PetModel {
try await client.body(pet).put()
Expand All @@ -53,7 +53,7 @@ public extension PetStore {

public struct PetByID {

var client: NetworkClient
var client: APIClient

public func get() async throws -> PetModel {
try await client()
Expand Down Expand Up @@ -90,7 +90,7 @@ public extension PetStore {

struct Store {

var client: NetworkClient
var client: APIClient

public func inventory() async throws -> [String: Int] {
try await client("inventory").auth(enabled: true).call()
Expand All @@ -106,7 +106,7 @@ public extension PetStore {

public struct Order {

var client: NetworkClient
var client: APIClient

public func find() async throws -> OrderModel {
try await client()
Expand All @@ -129,7 +129,7 @@ public extension PetStore {

struct User {

var client: NetworkClient
var client: APIClient

public func create(_ model: UserModel) async throws -> UserModel {
try await client.body(model).post()
Expand All @@ -155,7 +155,7 @@ public extension PetStore {

public struct UserByUsername {

var client: NetworkClient
var client: APIClient

public func get() async throws -> UserModel {
try await client()
Expand Down
6 changes: 3 additions & 3 deletions Example/Sources/PetStore/TokenRefresher.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ protocol TokenRefresher {
) async throws -> (Data, HTTPURLResponse)
}

extension NetworkClient {
extension APIClient {

func tokenRefresher(_ refresher: @escaping (NetworkClient.Configs) -> TokenRefresher) -> NetworkClient {
func tokenRefresher(_ refresher: @escaping (APIClient.Configs) -> TokenRefresher) -> APIClient {
configs { configs in
let base = configs.httpClient
configs.httpClient = HTTPClient { request, configs in
Expand All @@ -26,7 +26,7 @@ struct APITokenRefresher: TokenRefresher {

let tokenService: TokenCacheService

init(_ configs: NetworkClient.Configs) {
init(_ configs: APIClient.Configs) {
tokenService = configs.tokenCacheService
}

Expand Down
10 changes: 5 additions & 5 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,29 @@
import PackageDescription

var package = Package(
name: "swift-networking",
name: "swift-api-client",
platforms: [
.macOS(.v10_15),
.iOS(.v13),
.watchOS(.v5),
.tvOS(.v13),
],
products: [
.library(name: "SwiftNetworking", targets: ["SwiftNetworking"]),
.library(name: "SwiftAPIClient", targets: ["SwiftAPIClient"]),
],
dependencies: [
.package(url: "https://github.com/apple/swift-log.git", from: "1.5.3"),
],
targets: [
.target(
name: "SwiftNetworking",
name: "SwiftAPIClient",
dependencies: [
.product(name: "Logging", package: "swift-log"),
]
),
.testTarget(
name: "SwiftNetworkingTests",
dependencies: [.target(name: "SwiftNetworking")]
name: "SwiftAPIClientTests",
dependencies: [.target(name: "SwiftAPIClient")]
),
]
)
62 changes: 31 additions & 31 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
`swift-networking` is a comprehensive and modular client networking library for Swift.
`swift-api-client` is a comprehensive and modular Swift library for API design.

## Table of Contents
- [Table of Contents](#table-of-contents)
- [Main Goals of the Library](#main-goals-of-the-library)
- [Usage](#usage)
- [What is `NetworkClient`](#what-is-networkclient)
- [Built-in `NetworkClient` Extensions](#built-in-networkclient-extensions)
- [What is `APIClient`](#what-is-apiclient)
- [Built-in `APIClient` Extensions](#built-in-apiclient-extensions)
- [Request building](#request-building)
- [Request execution](#request-execution)
- [`NetworkClientCaller`](#networkclientcaller)
- [`APIClientCaller`](#apiclientcaller)
- [Serializer](#serializer)
- [Encoding and Decoding](#encoding-and-decoding)
- [ContentSerializer](#contentserializer)
- [Auth](#auth)
- [Mocking](#mocking)
- [Logging](#logging)
- [`NetworkClient.Configs`](#networkclientconfigs)
- [`APIClient.Configs`](#apiclientconfigs)
- [Configs Modifications Order](#configs-modifications-order)
- [Introducing `swift-networking-addons`](#introducing-swift-networking-addons)
- [Introducing `swift-api-client-addons`](#introducing-swift-api-client-addons)
- [Installation](#installation)
- [Author](#author)
- [License](#license)
Expand All @@ -31,19 +31,19 @@
- Facilitation of testing and mocking.

## Usage
The core of the library is the `NetworkClient` struct, serving both as a request builder and executor. It is a generic struct, enabling use for any task associated with `URLRequest`.
The core of the library is the `APIClient` struct, serving both as a request builder and executor. It is a generic struct, enabling use for any task associated with `URLRequest`.

The branching and configuration injection/overriding capabilities of NetworkClient, extending to all its child instances, facilitate the effortless recycling of networking logic and tasks, thereby eliminating the need for copy-pasting.
The branching and configuration injection/overriding capabilities of APIClient, extending to all its child instances, facilitate the effortless recycling of networking logic and tasks, thereby eliminating the need for copy-pasting.

While a full example is available in the [Example folder](/Example/), here is a simple usage example:
```swift
let client = NetworkClient(url: baseURL)
let client = APIClient(url: baseURL)
.auth(.bearer(token))
.bodyDecoder(.json(dateDecodingStrategy: .iso8601))
.bodyEncoder(.json(dateEncodingStrategy: .iso8601))
.errorDecoder(.decodable(APIError.self))

// Create a `NetworkClient` instance for the /users path
// Create a `APIClient` instance for the /users path
let usersClient = client("users")

// GET /users?name=John&limit=1
Expand All @@ -52,7 +52,7 @@ let john: User = try await usersClient
.auth(enabled: false)
.get()

// Create a `NetworkClient` instance for /users/{userID} path
// Create a `APIClient` instance for /users/{userID} path
let johnClient = usersClient(john.id)

// GET /user/{userID}
Expand All @@ -65,27 +65,27 @@ try await johnClient.body(updatedUser).put()
try await johnClient.delete()
```

## What is `NetworkClient`
## What is `APIClient`

`NetworkClient` is a struct combining a closure for creating a URLRequest and a typed dictionary of configurations `NetworkClient.Configs`. There are two primary ways to extend a `NetworkClient`:
`APIClient` is a struct combining a closure for creating a URLRequest and a typed dictionary of configurations `APIClient.Configs`. There are two primary ways to extend a `APIClient`:
- `modifyRequest` modifiers.
- `configs` modifiers.

Executing an operation on the client involves:
- `withRequest` methods.

All built-in extensions utilize these modifiers.
## Built-in `NetworkClient` Extensions
## Built-in `APIClient` Extensions
### Request building
Numerous methods exist for modifying a `URLRequest` such as `query`, `body`, `header`, `headers`, `method`, `path`, `timeout`, `cachePolicy`, `body`, `bodyStream` and more.\
The full list of modifiers is available in [RequestModifiers.swift](/Sources/SwiftNetworking/Modifiers/RequestModifiers.swift), all based on the `modifyRequest` modifier.
The full list of modifiers is available in [RequestModifiers.swift](/Sources/SwiftAPIClient/Modifiers/RequestModifiers.swift), all based on the `modifyRequest` modifier.

Notable non-obvious modifiers include:
- `.callAsFunction(path...)` - as a shorthand for the `.path(path...)` modifier, allowing `client("path")` instead of `client.path("path")`.
- HTTP method shorthands like `.get`, `.post`, `.put`, `.delete`, `.patch`.

### Request execution
The method`call(_ caller: NetworkClientCaller<...>, as serializer: Serializer<...>)` is provided.
The method`call(_ caller: APIClientCaller<...>, as serializer: Serializer<...>)` is provided.
Examples:
```swift
try await client.call(.http, as: .decodable)
Expand All @@ -96,7 +96,7 @@ There are also shorthands for built-in callers and serializers:
- `call()` is equivalent to `call(.http, as: .decodable)` or `call(.http, as: .void)`
- `callAsFunction()` acts as `call()`, simplifying `client.delete()` to `client.delete.call()` or `client()` instead of `client.call()`, etc.

#### `NetworkClientCaller`
#### `APIClientCaller`
Defines request execution with several built-in callers for various request types, including:
- `.http` for HTTP requests using `try await` syntax.
- `.httpPublisher` for HTTP requests with Combine syntax.
Expand All @@ -113,7 +113,7 @@ Custom callers can be created for different types of requests, such as WebSocket
- `.decodable` for decoding a response into a Decodable type.
- `.data` for obtaining a raw Data response.
- `.void` for ignoring the response.
- `.instance` for receiving a response of the same type as `NetworkClientCaller` returns. For HTTP requests, it is `Data`.
- `.instance` for receiving a response of the same type as `APIClientCaller` returns. For HTTP requests, it is `Data`.

The `.decodable` serializer uses the `.bodyDecoder` configuration, which can be customized with the `.bodyDecoder` modifier. The default `bodyDecoder` is `JSONDecoder()`.

Expand Down Expand Up @@ -146,12 +146,12 @@ Built-in tools for mocking requests include:
- `.usingMocksPolicy` configuration defines whether to use mocks, customizable with `.usingMocks(policy:)` modifier.
By default, mocks are ignored in the `live` environment and used as specified for tests and SwiftUI previews.

Additionally, `.mock(_:)` as a `NetworkClientCaller` offers an alternative way to mock requests, like `client.call(.mock(data), as: .decodable)`.
Additionally, `.mock(_:)` as a `APIClientCaller` offers an alternative way to mock requests, like `client.call(.mock(data), as: .decodable)`.

Custom HTTPClient instances can also be created and injected for testing or previews.

### Logging
`swift-networking` employs `swift-log` for logging, with `.logger` and `.logLevel` configurations customizable via `logger` and `.log(level:)` modifiers.
`swift-api-client` employs `swift-log` for logging, with `.logger` and `.logLevel` configurations customizable via `logger` and `.log(level:)` modifiers.
The default log level is `.info`. A built-in `.none` Logger is available to disable all logs.

Log example:
Expand All @@ -166,13 +166,13 @@ Content-Type: application/json
```
Log message format can be customized with the `.loggingComponents(_:)` modifier.

## `NetworkClient.Configs`
## `APIClient.Configs`
A collection of config values is propagated through the modifier chain. These configs are accessible in all core methods: `modifyRequest`, `withRequest`, and `withConfigs`.

To create custom config values, extend the `NetworkClient.Configs` structure with a new property.
To create custom config values, extend the `APIClient.Configs` structure with a new property.
Use subscript with your property key path to get and set the value, and provide a dedicated modifier for clients to use when setting this value:
```swift
extension NetworkClient.Configs {
extension APIClient.Configs {
var myCustomValue: MyConfig {
get {
self[\.myCustomValue] ?? myDefaultConfig
Expand All @@ -183,8 +183,8 @@ extension NetworkClient.Configs {
}
}

extension NetworkClient {
func myCustomValue(_ myCustomValue: MyConfig) -> NetworkClient {
extension APIClient {
func myCustomValue(_ myCustomValue: MyConfig) -> APIClient {
configs(\.myCustomValue, myCustomValue)
}
}
Expand Down Expand Up @@ -215,10 +215,10 @@ let configs = try client
print(configs.intValue) // 3
```

## Introducing `swift-networking-addons`
## Introducing `swift-api-client-addons`

To enhance your experience with `swift-networking`, I'm excited to introduce [`swift-networking-addons`](https://github.com/dankinsoid/swift-networking-addons)
— a complementary library designed to extend the core functionality of `swift-networking` with additional features and utilities.
To enhance your experience with `swift-api-client`, I'm excited to introduce [`swift-api-client-addons`](https://github.com/dankinsoid/swift-api-client-addons)
— a complementary library designed to extend the core functionality of `swift-api-client` with additional features and utilities.

## Installation

Expand All @@ -232,13 +232,13 @@ import PackageDescription
let package = Package(
name: "SomeProject",
dependencies: [
.package(url: "https://github.com/dankinsoid/swift-networking.git", from: "0.38.0")
.package(url: "https://github.com/dankinsoid/swift-api-client.git", from: "0.39.0")
],
targets: [
.target(
name: "SomeProject",
dependencies: [
.product(name: "SwiftNetworking", package: "swift-networking"),
.product(name: "SwiftAPIClient", package: "swift-api-client"),
]
)
]
Expand All @@ -254,7 +254,7 @@ Daniil Voidilov, [email protected]

## License

swift-networking is available under the MIT license. See the LICENSE file for more info.
swift-api-client is available under the MIT license. See the LICENSE file for more info.

## Contributing
We welcome contributions to Swift-Networking! Please read our contributing guidelines to get started.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import Foundation
#endif

/// A network client for handling url requests with configurable request and configuration handling.
public struct NetworkClient {
public struct APIClient {

private var _createRequest: (Configs) throws -> URLRequest
private var modifyConfigs: (inout Configs) -> Void = { _ in }
Expand Down Expand Up @@ -49,17 +49,17 @@ public struct NetworkClient {
/// - Parameters:
/// - keyPath: The key path to the configuration property to be modified.
/// - value: The new value for the specified configuration property.
/// - Returns: An instance of `NetworkClient` with updated configurations.
public func configs<T>(_ keyPath: WritableKeyPath<NetworkClient.Configs, T>, _ value: T) -> NetworkClient {
/// - Returns: An instance of `APIClient` with updated configurations.
public func configs<T>(_ keyPath: WritableKeyPath<APIClient.Configs, T>, _ value: T) -> APIClient {
configs {
$0[keyPath: keyPath] = value
}
}

/// Configures the client with a closure that modifies its configurations.
/// - Parameter configs: A closure that takes `inout Configs` and modifies them.
/// - Returns: An instance of `NetworkClient` with updated configurations.
public func configs(_ configs: @escaping (inout Configs) -> Void) -> NetworkClient {
/// - Returns: An instance of `APIClient` with updated configurations.
public func configs(_ configs: @escaping (inout Configs) -> Void) -> APIClient {
var result = self
result.modifyConfigs = { [modifyConfigs] in
modifyConfigs(&$0)
Expand All @@ -71,10 +71,10 @@ public struct NetworkClient {
/// Modifies the URLRequest using the provided closure.
/// - location: When the request should be modified.
/// - modifier: A closure that takes `inout URLRequest` and modifies the URLRequest.
/// - Returns: An instance of `NetworkClient` with a modified URLRequest.
/// - Returns: An instance of `APIClient` with a modified URLRequest.
public func modifyRequest(
_ modifier: @escaping (inout URLRequest) throws -> Void
) -> NetworkClient {
) -> APIClient {
modifyRequest { req, _ in
try modifier(&req)
}
Expand All @@ -84,10 +84,10 @@ public struct NetworkClient {
/// - Parameter:
/// - location: When the request should be modified.
/// - modifier: A closure that takes `inout URLRequest` and `Configs`, and modifies the URLRequest.
/// - Returns: An instance of `NetworkClient` with a modified URLRequest.
/// - Returns: An instance of `APIClient` with a modified URLRequest.
public func modifyRequest(
_ modifier: @escaping (inout URLRequest, Configs) throws -> Void
) -> NetworkClient {
) -> APIClient {
var result = self
result._createRequest = { [_createRequest] configs in
var request = try _createRequest(configs)
Expand Down
Loading

0 comments on commit fd7b907

Please sign in to comment.