Skip to content

Instantly share code, notes, and snippets.

@staticVoidMan
Last active October 10, 2023 13:39
Show Gist options
  • Select an option

  • Save staticVoidMan/b6049546f68994c8daf241495b100d52 to your computer and use it in GitHub Desktop.

Select an option

Save staticVoidMan/b6049546f68994c8daf241495b100d52 to your computer and use it in GitHub Desktop.
A scalable easy-to-use Network Manager
/*
Example based on API service: `https://api.quotable.io`
*/
import Foundation
//MARK: Usage Example
let provider: QuoteProvider = QuoteAPIProvider()
Task {
do {
let result = try await provider.getRandomQuote()
print(result)
} catch {
print(error)
}
}
Task {
do {
let result = try await provider.getQuote(withId: "zUEhc50DjK7")
print(result)
} catch {
print(error)
}
}
//MARK: NetworkManager component
enum NetworkManagerError: Error {
case invalidURL(string: String)
}
enum HTTPMethod: String {
case get = "GET"
case post = "POST"
}
protocol Requestable {
var base: String { get }
var route: String { get }
var httpMethod: HTTPMethod { get }
}
extension Requestable {
func buildRequest() throws -> URLRequest {
let urlString = base + route
guard let url = URL(string: urlString)
else { throw NetworkManagerError.invalidURL(string: urlString) }
var request = URLRequest(url: url)
request.httpMethod = httpMethod.rawValue
return request
}
}
final class RequestManager {
func send<T: Decodable>(_ request: Requestable) async throws -> T {
let request = try request.buildRequest()
let (data, _) = try await URLSession.shared.data(for: request)
let result = try JSONDecoder().decode(T.self, from: data)
return result
}
}
//MARK: Model
struct Quote: Decodable {
let id: String
let author: String
let content: String
enum CodingKeys: String, CodingKey {
case id = "_id"
case author
case content
}
}
//MARK: API
enum APIEndpoints {
case base
case randomQuote
case quote(id: String)
}
extension APIEndpoints {
var path: String {
switch self {
case .base:
"https://api.quotable.io/"
case .randomQuote:
"random"
case let .quote(id):
"quotes/\(id)"
}
}
}
//MARK: Provider
protocol QuoteProvider {
func getRandomQuote() async throws -> Quote
func getQuote(withId id: String) async throws -> Quote
}
// MARK: Request Builder
extension Requestable {
var base: String { APIEndpoints.base.path }
}
struct RandomQuoteRequest: Requestable {
var route: String { APIEndpoints.randomQuote.path }
var httpMethod: HTTPMethod { .get }
}
struct QuoteRequest: Requestable {
let id: String
var route: String { APIEndpoints.quote(id: id).path }
var httpMethod: HTTPMethod { .get }
}
final class QuoteAPIProvider: QuoteProvider {
lazy var manager = RequestManager()
func getRandomQuote() async throws -> Quote {
let request = RandomQuoteRequest()
return try await manager.send(request)
}
func getQuote(withId id: String) async throws -> Quote {
let request = QuoteRequest(id: id)
return try await manager.send(request)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment