Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
190 changes: 182 additions & 8 deletions Sources/SwiftNetKit/NetworkService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import Foundation

public struct NetworkService: NetworkServiceProtocol {
public class NetworkService {

internal let session: URLSession

Expand Down Expand Up @@ -35,8 +35,8 @@ public struct NetworkService: NetworkServiceProtocol {
self.session = URLSession(configuration: sessionConfiguration)
}

private func configureCache<T>(for urlRequest: inout URLRequest, with request: Request<T>) {
if let cacheConfig = request.cacheConfiguration {
private func configureCache(for urlRequest: inout URLRequest, with cacheConfiguration: CacheConfiguration?) {
if let cacheConfig = cacheConfiguration {
let cache = URLCache(
memoryCapacity: cacheConfig.memoryCapacity,
diskCapacity: cacheConfig.diskCapacity,
Expand All @@ -57,7 +57,7 @@ public struct NetworkService: NetworkServiceProtocol {
}
}

func start<T>(
func start<T: Codable>(
_ request: Request<T>,
retries: Int = 0,
retryInterval: TimeInterval = 1.0
Expand All @@ -66,7 +66,7 @@ public struct NetworkService: NetworkServiceProtocol {

CookieManager.shared.includeCookiesIfNeeded(for: &urlRequest, includeCookies: request.includeCookies)

self.configureCache(for: &urlRequest, with: request)
self.configureCache(for: &urlRequest, with: request.cacheConfiguration)

var currentAttempt = 0
var lastError: Error?
Expand Down Expand Up @@ -103,7 +103,7 @@ public struct NetworkService: NetworkServiceProtocol {
throw NetworkError.requestFailed(error: lastError ?? NetworkError.unknown)
}

func start<T>(
func start<T: Codable>(
_ request: Request<T>,
retries: Int = 0,
retryInterval: TimeInterval = 1.0,
Expand All @@ -113,7 +113,7 @@ public struct NetworkService: NetworkServiceProtocol {

CookieManager.shared.includeCookiesIfNeeded(for: &urlRequest, includeCookies: request.includeCookies)

self.configureCache(for: &urlRequest, with: request)
self.configureCache(for: &urlRequest, with: request.cacheConfiguration)

var currentAttempt = 0

Expand All @@ -132,7 +132,7 @@ public struct NetworkService: NetworkServiceProtocol {
}

CookieManager.shared.saveCookiesIfNeeded(from: response, saveResponseCookies: request.saveResponseCookies)

guard let httpResponse = response as? HTTPURLResponse else {
completion(.failure(NetworkError.invalidResponse))
return
Expand All @@ -158,4 +158,178 @@ public struct NetworkService: NetworkServiceProtocol {

attempt()
}

}

extension NetworkService {

func startBatch<T>(
_ requests: [Request<T>],
retries: Int = 0,
retryInterval: TimeInterval = 1.0,
exitEarlyOnFailure: Bool = false
) async throws -> [Result<T, Error>] {
var results = [Result<T, Error>](repeating: .failure(NetworkError.unknown), count: requests.count)
var encounteredError: Error?

try await withThrowingTaskGroup(of: (Int, Result<T, Error>).self) { group in
for (index, request) in requests.enumerated() {
group.addTask {
do {
let response: T = try await self.start(request)
return (index, .success(response))
} catch {
return (index, .failure(error))
}
}
}

for try await (index, result) in group {
if case .failure(let error) = result {
if exitEarlyOnFailure {
encounteredError = error
group.cancelAll()
break
}
}
results[index] = result
}
}

if exitEarlyOnFailure, let error = encounteredError {
throw error
}

return results
}

func startBatch<T>(
_ requests: [Request<T>],
retries: Int = 0,
retryInterval: TimeInterval = 1.0,
exitEarlyOnFailure: Bool = false,
completion: @escaping (Result<[Result<T, Error>], Error>) -> Void
) {
var results = [Result<T, Error>](repeating: .failure(NetworkError.unknown), count: requests.count)
var encounteredError: Error?
let dispatchGroup = DispatchGroup()
let queue = DispatchQueue(label: "startBatch.queue", attributes: .concurrent)

for (index, request) in requests.enumerated() {
dispatchGroup.enter()
queue.async {
self.start(request) { result in
if exitEarlyOnFailure, case .failure(let error) = result {
encounteredError = error
}

results[index] = result
dispatchGroup.leave()
}

if exitEarlyOnFailure, encounteredError != nil {
dispatchGroup.wait()
dispatchGroup.leave()
return
}
}
}

dispatchGroup.notify(queue: .main) {
if let error = encounteredError {
completion(.failure(error))
} else {
completion(.success(results))
}
}
}

// Explicitly specify decoding type
private func startWithExplicitType(
_ request: any RequestProtocol,
responseType: Decodable.Type,
retries: Int,
retryInterval: TimeInterval
) async throws -> Any {
var urlRequest = request.buildURLRequest()

CookieManager.shared.includeCookiesIfNeeded(for: &urlRequest, includeCookies: request.includeCookies)
self.configureCache(for: &urlRequest, with: request.cacheConfiguration)

var currentAttempt = 0
var lastError: Error?

while currentAttempt <= retries {
do {
let (data, response) = try await session.data(for: urlRequest)

CookieManager.shared.saveCookiesIfNeeded(from: response, saveResponseCookies: request.saveResponseCookies)

guard let httpResponse = response as? HTTPURLResponse else {
throw NetworkError.invalidResponse
}

guard (200..<300).contains(httpResponse.statusCode) else {
throw NetworkError.serverError(statusCode: httpResponse.statusCode)
}

do {
let decodedObject = try JSONDecoder().decode(responseType, from: data)
return decodedObject
} catch {
throw NetworkError.decodingFailed
}
} catch {
lastError = error
currentAttempt += 1
if currentAttempt <= retries {
try await Task.sleep(nanoseconds: UInt64(retryInterval * 1_000_000_000))
}
}
}

throw NetworkError.requestFailed(error: lastError ?? NetworkError.unknown)
}

func startBatchWithMultipleTypes(
_ requests: [any RequestProtocol],
retries: Int = 0,
retryInterval: TimeInterval = 1.0,
exitEarlyOnFailure: Bool = false
) async throws -> [Result<Any, Error>] {
var results = [Result<Any, Error>](repeating: .failure(NetworkError.unknown), count: requests.count)
var encounteredError: Error?

try await withThrowingTaskGroup(of: (Int, Result<Any, Error>).self) { group in
for (index, request) in requests.enumerated() {
let responseType = request.responseType

group.addTask {
do {
let result = try await self.startWithExplicitType(request, responseType: responseType, retries: retries, retryInterval: retryInterval)
return (index, .success(result))
} catch {
return (index, .failure(error))
}
}
}

for try await (index, result) in group {
if case .failure(let error) = result {
if exitEarlyOnFailure {
encounteredError = error
group.cancelAll()
break
}
}
results[index] = result
}
}

if exitEarlyOnFailure, let error = encounteredError {
throw error
}

return results
}
}
27 changes: 0 additions & 27 deletions Sources/SwiftNetKit/Protocols/NetworkServiceProtocol.swift

This file was deleted.

5 changes: 4 additions & 1 deletion Sources/SwiftNetKit/Protocols/RequestProtocol.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@

import Foundation

protocol RequestProtocol {
protocol RequestProtocol {
associatedtype Response: Codable

var url: URL { get }
var method: MethodType { get }
var parameters: [String: Any]? { get }
Expand All @@ -16,6 +18,7 @@ protocol RequestProtocol {
var cacheConfiguration: CacheConfiguration? { get }
var includeCookies: Bool { get }
var saveResponseCookies: Bool { get }
var responseType: Response.Type { get }

func buildURLRequest() -> URLRequest
}
1 change: 1 addition & 0 deletions Sources/SwiftNetKit/Request.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ public class Request<Response: Codable>: RequestProtocol {
let cacheConfiguration: CacheConfiguration?
let includeCookies: Bool
let saveResponseCookies: Bool
var responseType: Response.Type { return Response.self }

init(
url: URL,
Expand Down
Loading