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
54 changes: 54 additions & 0 deletions Sources/SwiftNetKit/BaseRequest.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
//
// BaseRequest.swift
//
//
// Created by Sam Gilmore on 7/16/24.
//

import Foundation

public struct BaseRequest<Response: Decodable>: RequestProtocol {
typealias ResponseType = Response

let url: URL
let method: MethodType
let parameters: [String : Any]?
let headers: [String : String]?
let body: Data?

init(
url: URL,
method: MethodType,
parameters: [String : Any]? = nil,
headers: [String : String]? = nil,
body: Data? = nil
) {
self.url = url
self.method = method
self.parameters = parameters
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 {
urlRequest.httpBody = body
}

return urlRequest
}
}
14 changes: 14 additions & 0 deletions Sources/SwiftNetKit/Models/MethodType.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
//
// MethodType.swift
//
//
// Created by Sam Gilmore on 7/17/24.
//

enum MethodType: String {
case get = "GET"
case post = "POST"
case delete = "DELETE"
case put = "PUT"
case patch = "PATCH"
}
2 changes: 0 additions & 2 deletions Sources/SwiftNetKit/Models/NetworkError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@
// Created by Sam Gilmore on 7/16/24.
//

import Foundation

public enum NetworkError: Error {
case invalidResponse
case decodingFailed
Expand Down
12 changes: 12 additions & 0 deletions Sources/SwiftNetKit/Models/SessionConfiguration.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
//
// SessionConfiguration.swift
//
//
// Created by Sam Gilmore on 7/17/24.
//

public enum SessionConfiguration {
case `default`
case ephemeral
case background(String)
}
57 changes: 51 additions & 6 deletions Sources/SwiftNetKit/NetworkService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,37 @@

import Foundation

public struct NetworkService {
public struct NetworkService: NetworkServiceProtocol {

public init() {}
internal let session: URLSession

public func get<T: Decodable>(from url: URL, decodeTo type: T.Type) async throws -> T {
public init(configuration: SessionConfiguration = .default) {
switch configuration {
case .default:
self.session = URLSession(configuration: .default)
case .ephemeral:
self.session = URLSession(configuration: .ephemeral)
case .background(let identifier):
self.session = URLSession(configuration: .background(withIdentifier: identifier))
}
}

func start<Request: RequestProtocol>(_ request: Request) async throws -> Request.ResponseType {
do {
let (data, response) = try await URLSession.shared.data(from: url)
let urlRequest = request.buildURLRequest()

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

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

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

do {
let decodedObject = try JSONDecoder().decode(T.self, from: data)
let decodedObject = try JSONDecoder().decode(Request.ResponseType.self, from: data)
return decodedObject
} catch {
throw NetworkError.decodingFailed
Expand All @@ -33,4 +46,36 @@ public struct NetworkService {
throw NetworkError.requestFailed(error: error)
}
}

func start<Request: RequestProtocol>(_ request: Request, completion: @escaping (Result<Request.ResponseType, Error>) -> Void) {
let urlRequest = request.buildURLRequest()

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
}

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()
}
}
18 changes: 18 additions & 0 deletions Sources/SwiftNetKit/Protocols/NetworkServiceProtocol.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//
// NetworkServiceProtocol.swift
//
//
// Created by Sam Gilmore on 7/16/24.
//

import Foundation

protocol NetworkServiceProtocol {
var session: URLSession { get }

// Async/Await
func start<Request: RequestProtocol>(_ request: Request) async throws -> Request.ResponseType

// Completion Closure
func start<Request: RequestProtocol>(_ request: Request, completion: @escaping (Result<Request.ResponseType, Error>) -> Void)
}
20 changes: 20 additions & 0 deletions Sources/SwiftNetKit/Protocols/RequestProtocol.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//
// RequestProtocol.swift
//
//
// Created by Sam Gilmore on 7/16/24.
//

import Foundation

protocol RequestProtocol {
associatedtype ResponseType: Decodable

var url: URL { get }
var method: MethodType { get }
var parameters: [String: Any]? { get }
var headers: [String: String]? { get }
var body: Data? { get }

func buildURLRequest() -> URLRequest
}
48 changes: 42 additions & 6 deletions Tests/SwiftNetKitTests/NetworkServiceTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,49 @@ final class NetworkServiceTests: XCTestCase {
super.tearDown()
}

func testGetSuccess() async throws {
do {
let post: Post = try await networkService.get(from: getURL, decodeTo: Post.self)
XCTAssertEqual(post.id, 1)
} catch {
XCTFail("Should succeed, but failed with error: \(error)")
func testGetSuccessAsyncAwait() {
let expectation = XCTestExpectation(description: "Fetch data successfully")

Task {
do {
let baseRequest = BaseRequest<Post>(
url: self.getURL,
method: .get
)
let post: Post = try await self.networkService.start(baseRequest)
XCTAssertEqual(post.userId, 1)
XCTAssertEqual(post.id, 1)

expectation.fulfill()
} catch {
XCTFail("Failed with error: \(error)")
}
}

wait(for: [expectation], timeout: 5.0)
}

func testGetSuccessClosure() {
let expectation = XCTestExpectation(description: "Fetch data successfully")

let baseRequest = BaseRequest<Post>(
url: self.getURL,
method: .get
)

networkService.start(baseRequest) { result in
switch result {
case .success(let post):
XCTAssertEqual(post.userId, 1)
XCTAssertEqual(post.id, 1)

expectation.fulfill()
case .failure(let error):
XCTFail("Failed with error: \(error)")
}
}

wait(for: [expectation], timeout: 5.0)
}
}

Expand Down