Skip to content

Commit

Permalink
Merge branch 'release/0.7.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
antonio-war committed Sep 30, 2024
2 parents 923e0c0 + 25fe57f commit c66198a
Show file tree
Hide file tree
Showing 15 changed files with 282 additions and 239 deletions.
4 changes: 4 additions & 0 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,9 @@ let package = Package(
.testTarget(
name: "SwiftyNetworkingTests",
dependencies: ["SwiftyNetworking"]),
],
swiftLanguageVersions: [
.v5,
.version("6")
]
)
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,14 @@ let users = try await networkingClient.send(request, decoding: [JsonPlaceholderU
```

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.
The model you want to decode must conform to the `SwiftyNetworkingModel` protocol, this ensures compatibility with Swift 6 strict concurrency checking. Here is a simple example of model definition:

```swift
struct JsonPlaceholderUser: Identifiable, SwiftyNetworkingModel {
var id: Int
var name: String
}
```

### SwiftUI integration
SwiftyNetworking was born to be a modern framework and for this reason it is oriented towards development with SwiftUI.
Expand Down
24 changes: 19 additions & 5 deletions Sources/SwiftyNetworking/Clients/SwiftyNetworkingClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import Foundation

public struct SwiftyNetworkingClient {
public struct SwiftyNetworkingClient: Sendable {
private let session: URLSession
private let delegate: SwiftyNetworkingDelegate

Expand All @@ -19,7 +19,10 @@ public struct SwiftyNetworkingClient {
self.delegate = delegate
}

public func send(_ request: SwiftyNetworkingRequest, completion: @escaping (Result<SwiftyNetworkingResponse, Error>) -> Void) {
public func send(
_ request: SwiftyNetworkingRequest,
completion: @escaping @Sendable (Result<SwiftyNetworkingResponse, Error>) -> Void
) {
let dataTask = session.dataTask(with: request.rawValue) { data, response, error in
if let error = error {
completion(.failure(error))
Expand All @@ -43,7 +46,9 @@ public struct SwiftyNetworkingClient {
dataTask.resume()
}

public func send(_ request: SwiftyNetworkingRequest) async throws -> SwiftyNetworkingResponse {
public func send(
_ request: SwiftyNetworkingRequest
) async throws -> SwiftyNetworkingResponse {
try await withCheckedThrowingContinuation { continuation in
send(request) { result in
switch result {
Expand All @@ -56,7 +61,12 @@ public struct SwiftyNetworkingClient {
}
}

public func send<Model: Decodable>(_ request: SwiftyNetworkingRequest, decoding model: Model.Type, using decoder: JSONDecoder = JSONDecoder(), completion: @escaping (Result<Model, Error>) -> Void) {
public func send<Model: SwiftyNetworkingModel>(
_ request: SwiftyNetworkingRequest,
decoding model: Model.Type,
using decoder: JSONDecoder = JSONDecoder(),
completion: @escaping @Sendable (Result<Model, Error>) -> Void
) {
send(request) { result in
do {
switch result {
Expand All @@ -76,7 +86,11 @@ public struct SwiftyNetworkingClient {
}
}

public func send<Model: Decodable>(_ request: SwiftyNetworkingRequest, decoding model: Model.Type, using decoder: JSONDecoder = JSONDecoder()) async throws -> Model {
public func send<Model: SwiftyNetworkingModel>(
_ 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 {
Expand Down
51 changes: 32 additions & 19 deletions Sources/SwiftyNetworking/Delegates/SwiftyNetworkingDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,37 +7,50 @@

import Foundation

public class SwiftyNetworkingDelegate: NSObject, URLSessionTaskDelegate {
private var cache: NSCache<NSNumber, URLSessionTaskTransactionMetrics>

public class SwiftyNetworkingDelegate: NSObject, URLSessionTaskDelegate, @unchecked Sendable {
private let cache: NSCache<NSNumber, URLSessionTaskTransactionMetrics>
private let queue = DispatchQueue(label: "it.antoniowar.SwiftyNetworking.cache", attributes: .concurrent)

public init(cache: NSCache<NSNumber, URLSessionTaskTransactionMetrics> = NSCache(countLimit: 5)) {
self.cache = cache
}

public func fetchType(for request: URLRequest) -> FetchType? {
guard let metrics = cache.object(forKey: request.hashValue as NSNumber) else {
return nil
return queue.sync {
guard let metrics = cache.object(forKey: request.hashValue as NSNumber) else {
return nil
}
return metrics.resourceFetchType
}
return metrics.resourceFetchType
}

public func start(for request: URLRequest) -> Date? {
guard let metrics = cache.object(forKey: request.hashValue as NSNumber) else {
return nil
return queue.sync {
guard let metrics = cache.object(forKey: request.hashValue as NSNumber) else {
return nil
}
return metrics.requestStartDate
}
return metrics.requestStartDate
}

public func end(for request: URLRequest) -> Date? {
guard let metrics = cache.object(forKey: request.hashValue as NSNumber) else {
return nil
return queue.sync {
guard let metrics = cache.object(forKey: request.hashValue as NSNumber) else {
return nil
}
return metrics.responseEndDate
}
return metrics.responseEndDate
}

public func urlSession(_ session: URLSession, task: URLSessionTask, didFinishCollecting metrics: URLSessionTaskMetrics) {
for metrics in metrics.transactionMetrics {
cache.setObject(metrics, forKey: metrics.request.hashValue as NSNumber)

public func urlSession(
_ session: URLSession,
task: URLSessionTask,
didFinishCollecting metrics: URLSessionTaskMetrics
) {
queue.async(flags: .barrier) {
for metric in metrics.transactionMetrics {
self.cache.setObject(metric, forKey: metric.request.hashValue as NSNumber)
}
}
}
}
2 changes: 1 addition & 1 deletion Sources/SwiftyNetworking/Enums/Response.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import Foundation

public enum Response<Model: Decodable> {
public enum Response<Model: SwiftyNetworkingModel>: Sendable {
case loading
case success(Model)
case failure(Error)
Expand Down
7 changes: 3 additions & 4 deletions Sources/SwiftyNetworking/PropertyWrappers/Request.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import Foundation
import SwiftUI

@propertyWrapper
public struct Request<Model: Decodable>: DynamicProperty, Refreshable {
public struct Request<Model: SwiftyNetworkingModel>: DynamicProperty, Sendable, Refreshable {
public typealias Method = SwiftyNetworkingRequest.Method
@State private var response: Response<Model> = .loading
@State private var fetching: Bool = false
Expand Down Expand Up @@ -56,9 +56,7 @@ public struct Request<Model: Decodable>: DynamicProperty, Refreshable {
) {
self.init(
client: client,
url: URL(
string: url
),
url: URL(string: url),
method: method,
headers: headers,
body: body,
Expand Down Expand Up @@ -115,6 +113,7 @@ public struct Request<Model: Decodable>: DynamicProperty, Refreshable {
}
}

@MainActor
public func refresh() async {
await fetch()
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
//
// SwiftyNetworkingModel.swift
// SwiftyNetworking
//
// Created by Antonio Guerra on 22/09/24.
//

public typealias SwiftyNetworkingModel = Decodable & Sendable
Loading

0 comments on commit c66198a

Please sign in to comment.