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
34 changes: 0 additions & 34 deletions Sources/SwiftNetKit/BaseRequest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,38 +29,4 @@ public struct BaseRequest<Response: Decodable>: RequestProtocol {
self.headers = headers
self.body = body
}

func buildURLRequest() -> URLRequest {
var urlRequest = URLRequest(url: self.url)

if let parameters = self.parameters {
let queryItems = parameters.map { key, value in
URLQueryItem(name: key, value: "\(value)")
}
var urlComponents = URLComponents(url: self.url, resolvingAgainstBaseURL: false)
urlComponents?.queryItems = queryItems
urlRequest.url = urlComponents?.url
}

urlRequest.httpMethod = self.method.rawValue
urlRequest.allHTTPHeaderFields = self.headers

if let body = self.body {
switch body {
case .data(let data):
urlRequest.httpBody = data
case .string(let string):
urlRequest.httpBody = string.data(using: .utf8)
case .jsonEncodable(let encodable):
let jsonData = try? JSONEncoder().encode(encodable)
urlRequest.httpBody = jsonData

if headers?["Content-Type"] == nil {
urlRequest.setValue("application/json", forHTTPHeaderField: "Content-Type")
}
}
}

return urlRequest
}
}
1 change: 1 addition & 0 deletions Sources/SwiftNetKit/Models/NetworkError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ public enum NetworkError: Error {
case decodingFailed
case serverError(statusCode: Int)
case requestFailed(error: Error)
case unknown
}
141 changes: 91 additions & 50 deletions Sources/SwiftNetKit/NetworkService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,71 +11,112 @@ public struct NetworkService: NetworkServiceProtocol {

internal let session: URLSession

public init(configuration: SessionConfiguration = .default) {
public init(configuration: SessionConfiguration = .default, timeoutInterval: TimeInterval? = nil) {
let sessionConfiguration: URLSessionConfiguration
switch configuration {
case .default:
self.session = URLSession(configuration: .default)
sessionConfiguration = URLSessionConfiguration.default
case .ephemeral:
self.session = URLSession(configuration: .ephemeral)
sessionConfiguration = URLSessionConfiguration.ephemeral
case .background(let identifier):
self.session = URLSession(configuration: .background(withIdentifier: identifier))
sessionConfiguration = URLSessionConfiguration.background(withIdentifier: identifier)
}

if let timeoutInterval = timeoutInterval {
sessionConfiguration.timeoutIntervalForRequest = timeoutInterval
sessionConfiguration.timeoutIntervalForResource = timeoutInterval
}

self.session = URLSession(configuration: sessionConfiguration)
}

func start<Request: RequestProtocol>(_ request: Request) async throws -> Request.ResponseType {
do {
let urlRequest = request.buildURLRequest()

let (data, response) = try await session.data(for: urlRequest)

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

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

func start<Request: RequestProtocol>(
_ request: Request,
retries: Int = 0,
retryInterval: TimeInterval = 1.0
) async throws -> Request.ResponseType {
var currentAttempt = 0
var lastError: Error?

while currentAttempt <= retries {
do {
let decodedObject = try JSONDecoder().decode(Request.ResponseType.self, from: data)
return decodedObject
let urlRequest = request.buildURLRequest()

let (data, response) = try await session.data(for: urlRequest)

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(Request.ResponseType.self, from: data)
return decodedObject
} catch {
throw NetworkError.decodingFailed
}
} catch {
throw NetworkError.decodingFailed
lastError = error
currentAttempt += 1
if currentAttempt <= retries {
try await Task.sleep(nanoseconds: UInt64(retryInterval * 1_000_000_000))
}
}
} catch {
throw NetworkError.requestFailed(error: error)
}

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

func start<Request: RequestProtocol>(_ request: Request, completion: @escaping (Result<Request.ResponseType, Error>) -> Void) {
let urlRequest = request.buildURLRequest()
func start<Request: RequestProtocol>(
_ request: Request,
retries: Int = 0,
retryInterval: TimeInterval = 1.0,
completion: @escaping (Result<Request.ResponseType, Error>) -> Void
) {
var currentAttempt = 0

session.dataTask(with: urlRequest) { data, response, error in
if let error = error {
completion(.failure(NetworkError.requestFailed(error: error)))
return
}

guard let httpResponse = response as? HTTPURLResponse else {
completion(.failure(NetworkError.invalidResponse))
return
}

guard (200..<300).contains(httpResponse.statusCode) else {
completion(.failure(NetworkError.serverError(statusCode: httpResponse.statusCode)))
return
}
func attempt() {
let urlRequest = request.buildURLRequest()

if let data = data {
do {
let decodedObject = try JSONDecoder().decode(Request.ResponseType.self, from: data)
completion(.success(decodedObject))
} catch {
completion(.failure(NetworkError.decodingFailed))
session.dataTask(with: urlRequest) { data, response, error in
if let error = error {
if currentAttempt < retries {
currentAttempt += 1
DispatchQueue.global().asyncAfter(deadline: .now() + retryInterval) {
attempt()
}
} else {
completion(.failure(NetworkError.requestFailed(error: error)))
}
return
}
} else {
completion(.failure(NetworkError.invalidResponse))
}
}.resume()

guard let httpResponse = response as? HTTPURLResponse else {
completion(.failure(NetworkError.invalidResponse))
return
}

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

if let data = data {
do {
let decodedObject = try JSONDecoder().decode(Request.ResponseType.self, from: data)
completion(.success(decodedObject))
} catch {
completion(.failure(NetworkError.decodingFailed))
}
} else {
completion(.failure(NetworkError.invalidResponse))
}
}.resume()
}

attempt()
}
}
14 changes: 11 additions & 3 deletions Sources/SwiftNetKit/Protocols/NetworkServiceProtocol.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,17 @@ import Foundation
protocol NetworkServiceProtocol {
var session: URLSession { get }

// Async/Await
func start<Request: RequestProtocol>(_ request: Request) async throws -> Request.ResponseType
// Async / Await
func start<Request: RequestProtocol>(
_ request: Request,
retries: Int,
retryInterval: TimeInterval
) async throws -> Request.ResponseType

// Completion Closure
func start<Request: RequestProtocol>(_ request: Request, completion: @escaping (Result<Request.ResponseType, Error>) -> Void)
func start<Request: RequestProtocol>(
_ request: Request, retries: Int,
retryInterval: TimeInterval,
completion: @escaping (Result<Request.ResponseType, Error>) -> Void
)
}
36 changes: 36 additions & 0 deletions Sources/SwiftNetKit/Protocols/RequestProtocol.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,39 @@ protocol RequestProtocol {

func buildURLRequest() -> URLRequest
}

extension RequestProtocol {
func buildURLRequest() -> URLRequest {
var urlRequest = URLRequest(url: self.url)

if let parameters = self.parameters {
let queryItems = parameters.map { key, value in
URLQueryItem(name: key, value: "\(value)")
}
var urlComponents = URLComponents(url: self.url, resolvingAgainstBaseURL: false)
urlComponents?.queryItems = queryItems
urlRequest.url = urlComponents?.url
}

urlRequest.httpMethod = self.method.rawValue
urlRequest.allHTTPHeaderFields = self.headers

if let body = self.body {
switch body {
case .data(let data):
urlRequest.httpBody = data
case .string(let string):
urlRequest.httpBody = string.data(using: .utf8)
case .jsonEncodable(let encodable):
let jsonData = try? JSONEncoder().encode(encodable)
urlRequest.httpBody = jsonData

if headers?["Content-Type"] == nil {
urlRequest.setValue("application/json", forHTTPHeaderField: "Content-Type")
}
}
}

return urlRequest
}
}