Skip to content

Commit

Permalink
Merge branch 'release/0.3.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
antonio-war committed Aug 12, 2024
2 parents 5218ded + 36ed6ce commit c418f67
Show file tree
Hide file tree
Showing 9 changed files with 183 additions and 25 deletions.
13 changes: 11 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,9 @@ In a context where the app makes numerous requests of different types to the sam
var path: String {
switch self {
case .users:
"/"
"users/"
case .user(let id):
"/\(id)"
"users/\(id)"
}
}
}
Expand All @@ -113,6 +113,15 @@ Making a request to one of the exposed routes will be really easy!
let response = try await client.send(request)
```

### Decoding
In most cases once you make a network call you need to read the contents of the response body, obviously in the iOS environment this is achieved using the power of the Decodable protocol and its Decoder, that's why SwiftyNetworking also provides methods with integrated decoding. They are very useful when the decoding operation must be done in a simple way, without any custom behavior, SwiftyNetworking will relieve you of any responsibility.

```swift
let users = try await networkingClient.send(JsonPlaceholderRouter.users, decoding: [JsonPlaceholderUser].self, using: JSONDecoder())
```

By default the method uses its own instance of JSONDecoder, however, as shown it is possible to inject a custom decoder if a particular decoding configuration is necessary.

---
# Support
Your generous donations help sustain and improve this project. Here's why supporting us is important:
Expand Down
46 changes: 42 additions & 4 deletions Sources/SwiftyNetworking/Clients/SwiftyNetworkingClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,7 @@ public struct SwiftyNetworkingClient {
configuration: URLSessionConfiguration = URLSessionConfiguration.default,
delegate: SwiftyNetworkingDelegate = SwiftyNetworkingDelegate()
) {
self.session = URLSession(
configuration: configuration,
delegate: delegate
)
self.session = URLSession(configuration: configuration, delegate: delegate)
self.delegate = delegate
}

Expand Down Expand Up @@ -66,11 +63,52 @@ public struct SwiftyNetworkingClient {
}
}

public func send<Model: Decodable>(_ request: SwiftyNetworkingRequest, decoding model: Model.Type, using decoder: JSONDecoder = JSONDecoder(), completion: @escaping (Result<Model, Error>) -> Void) {
send(request) { result in
do {
switch result {
case .success(let response):
guard response.status == .success else {
completion(.failure(URLError(.badServerResponse)))
return
}
let model = try decoder.decode(model, from: response.body)
completion(.success(model))
case .failure(let error):
completion(.failure(error))
}
} catch {
completion(.failure(error))
}
}
}

public func send<Model: Decodable>(_ request: SwiftyNetworkingRequest, decoding model: Model.Type, using decoder: JSONDecoder = JSONDecoder()) async throws -> Model {
try await withCheckedThrowingContinuation { continuation in
send(request, decoding: model, using: decoder) { result in
switch result {
case .success(let response):
continuation.resume(returning: response)
case .failure(let error):
continuation.resume(throwing: error)
}
}
}
}

public func send(_ router: SwiftyNetworkingRouter, completion: @escaping (Result<SwiftyNetworkingResponse, Error>) -> Void) {
send(router.rawValue, completion: completion)
}

public func send(_ router: SwiftyNetworkingRouter) async throws -> SwiftyNetworkingResponse {
try await send(router.rawValue)
}

public func send<Model: Decodable>(_ router: SwiftyNetworkingRouter, decoding model: Model.Type, using decoder: JSONDecoder = JSONDecoder(), completion: @escaping (Result<Model, Error>) -> Void) {
send(router.rawValue, decoding: model, using: decoder, completion: completion)
}

public func send<Model: Decodable>(_ router: SwiftyNetworkingRouter, decoding model: Model.Type, using decoder: JSONDecoder = JSONDecoder()) async throws -> Model {
try await send(router.rawValue, decoding: model, using: decoder)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,48 @@ final class SwiftyNetworkingClientTests: XCTestCase {
await XCTAssertThrowsError(try await networkingClient.send(request))
}

func testSendGetRequestWithDecoding() async throws {
let request = SwiftyNetworkingRequest(
endpoint: "https://jsonplaceholder.typicode.com",
path: "/users",
cachePolicy: .reloadIgnoringCacheData
)
let users = try await networkingClient.send(request, decoding: [JsonPlaceholderUser].self)
XCTAssertFalse(users.isEmpty)
}

func testSendGetRequestWithDecodingWhenStatusIsInvalid() async throws {
let request = SwiftyNetworkingRequest(
endpoint: "https://httpbin.org",
path: "status/600",
cachePolicy: .reloadIgnoringCacheData
)
await XCTAssertThrowsError(try await networkingClient.send(request, decoding: [JsonPlaceholderUser].self))
}

func testSendGetRequestWithDecodingWhenModelIsInvalid() async throws {
let request = SwiftyNetworkingRequest(
endpoint: "https://jsonplaceholder.typicode.com",
path: "/users",
cachePolicy: .reloadIgnoringCacheData
)
await XCTAssertThrowsError(try await networkingClient.send(request, decoding: [JsonPlaceholderInvalidUser].self))
}

func testSendGetRequestUsingRouter() async throws {
let response = try await networkingClient.send(JsonPlaceholderRouter.users)
XCTAssertEqual(response.code, 200)
XCTAssertEqual(response.status, .success)
XCTAssertEqual(response.fetchType, .networkLoad)
XCTAssertFalse(response.body.isEmpty)
XCTAssertFalse(response.headers.isEmpty)
}

func testSendGetRequestWithDecodingUsingRouter() async throws {
let users = try await networkingClient.send(JsonPlaceholderRouter.users, decoding: [JsonPlaceholderUser].self)
XCTAssertFalse(users.isEmpty)
}

func testSendPatchRequest() async throws {
let request = SwiftyNetworkingRequest(
endpoint: "https://httpbin.org",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,35 +10,35 @@ import XCTest

final class SwiftyNetworkingDelegateTests: XCTestCase {

var networkingDelegate: SwiftyNetworkingDelegate!
var delegate: SwiftyNetworkingDelegate!
var client: SwiftyNetworkingClient!

override func setUpWithError() throws {
networkingDelegate = SwiftyNetworkingDelegate(cache: NSCache(countLimit: 1))
client = SwiftyNetworkingClient(configuration: .default, delegate: networkingDelegate)
delegate = SwiftyNetworkingDelegate(cache: NSCache(countLimit: 1))
client = SwiftyNetworkingClient(configuration: .default, delegate: delegate)
}

override func tearDownWithError() throws {
networkingDelegate = nil
delegate = nil
client = nil
}

func testCacheWhenMetricsAreNotAvailable() throws {
let url = try XCTUnwrap(URL(string: "http://valid-endpoint/valid-path?id=1"))
let request = URLRequest(url: url)
XCTAssertNil(networkingDelegate.fetchType(for: request))
XCTAssertNil(networkingDelegate.start(for: request))
XCTAssertNil(networkingDelegate.end(for: request))
XCTAssertNil(delegate.fetchType(for: request))
XCTAssertNil(delegate.start(for: request))
XCTAssertNil(delegate.end(for: request))
}

func testCacheWhenMetricsAreAvailable() async throws {
let request = JsonPlaceholderRouter.users
let response = try await client.send(request)
let rawRequest = try request.rawValue.rawValue
XCTAssertEqual(response.status, .success)
XCTAssertNotNil(networkingDelegate.fetchType(for: rawRequest))
XCTAssertNotNil(networkingDelegate.start(for: rawRequest))
XCTAssertNotNil(networkingDelegate.end(for: rawRequest))
XCTAssertNotNil(delegate.fetchType(for: rawRequest))
XCTAssertNotNil(delegate.start(for: rawRequest))
XCTAssertNotNil(delegate.end(for: rawRequest))
}

func testCacheIsCleanedWhenTheLimitIsReached() async throws {
Expand All @@ -48,8 +48,8 @@ final class SwiftyNetworkingDelegateTests: XCTestCase {
let secondRequest = JsonPlaceholderRouter.user(id: 1)
let _ = try await client.send(secondRequest)
XCTAssertEqual(firstResponse.status, .success)
XCTAssertNil(networkingDelegate.fetchType(for: firstRawRequest))
XCTAssertNil(networkingDelegate.start(for: firstRawRequest))
XCTAssertNil(networkingDelegate.end(for: firstRawRequest))
XCTAssertNil(delegate.fetchType(for: firstRawRequest))
XCTAssertNil(delegate.start(for: firstRawRequest))
XCTAssertNil(delegate.end(for: firstRawRequest))
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//
// JsonPlaceholderInvalidUser.swift
//
//
// Created by Antonio Guerra on 06/08/24.
//

import Foundation

struct JsonPlaceholderInvalidUser: Identifiable, Decodable {
var id: String
var name: Int
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,13 @@ enum JsonPlaceholderRouter: SwiftyNetworkingRouter {
var path: String {
switch self {
case .users:
"/"
"users/"
case .user(let id):
"/\(id)"
"users/\(id)"
}
}

var cachePolicy: CachePolicy {
.reloadIgnoringLocalCacheData
}
}
13 changes: 13 additions & 0 deletions Tests/SwiftyNetworkingTests/Helpers/JsonPlaceholderUser.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//
// JsonPlaceholderUser.swift
//
//
// Created by Antonio Guerra on 06/08/24.
//

import Foundation

struct JsonPlaceholderUser: Identifiable, Decodable {
var id: Int
var name: String
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
//
// SwiftyNetworkingResponseTests.swift
//
//
// Created by Antonio Guerra on 06/08/24.
//

@testable import SwiftyNetworking
import XCTest

final class SwiftyNetworkingResponseTests: XCTestCase {

var delegate: SwiftyNetworkingDelegate!
var client: SwiftyNetworkingClient!

override func setUpWithError() throws {
delegate = SwiftyNetworkingDelegate(cache: NSCache(countLimit: 1))
client = SwiftyNetworkingClient(configuration: .default, delegate: delegate)
}

override func tearDownWithError() throws {
delegate = nil
client = nil
}

func testDuration() async throws {
let request = SwiftyNetworkingRequest(
endpoint: "https://httpbin.org",
path: "get",
method: .get,
cachePolicy: .reloadIgnoringCacheData
)
let response = try await client.send(request)
let start = try XCTUnwrap(response.start)
let end = try XCTUnwrap(response.end)
let duration = try XCTUnwrap(response.duration)
XCTAssertEqual(duration, end.timeIntervalSince(start))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,24 +13,24 @@ final class SwiftyNetworkingRouterTests: XCTestCase {
func testRawValueWhenCaseHasNotAssociatedParameters() throws {
let request = JsonPlaceholderRouter.users.rawValue
XCTAssertEqual(request.endpoint, "https://jsonplaceholder.typicode.com")
XCTAssertEqual(request.path, "/")
XCTAssertEqual(request.path, "users/")
XCTAssertEqual(request.method, .get)
XCTAssertEqual(request.headers, [:])
XCTAssertEqual(request.body, nil)
XCTAssertEqual(request.parameters, [:])
XCTAssertEqual(request.cachePolicy, .returnCacheDataElseLoad)
XCTAssertEqual(request.cachePolicy, .reloadIgnoringLocalCacheData)
XCTAssertEqual(request.timeout, 60)
}

func testRawValueWhenCaseHasAssociatedParameters() throws {
let request = JsonPlaceholderRouter.user(id: 1).rawValue
XCTAssertEqual(request.endpoint, "https://jsonplaceholder.typicode.com")
XCTAssertEqual(request.path, "/1")
XCTAssertEqual(request.path, "users/1")
XCTAssertEqual(request.method, .get)
XCTAssertEqual(request.headers, [:])
XCTAssertEqual(request.body, nil)
XCTAssertEqual(request.parameters, [:])
XCTAssertEqual(request.cachePolicy, .returnCacheDataElseLoad)
XCTAssertEqual(request.cachePolicy, .reloadIgnoringLocalCacheData)
XCTAssertEqual(request.timeout, 60)
}
}

0 comments on commit c418f67

Please sign in to comment.