/Networking

A networking layer abstraction + URLSession implementation.

Primary LanguageSwift

Networking

codecov CodeFactor

Networking layer abstraction

Instalation

Add this to Cartfile:

git "https://github.com/bocato/Networking.git" ~> 1.0

Then:

$ carthage update

Setup

  • Use as an abstraction and implement your own dispatcher or use the provided URLSessionDispatcher.

Requests

enum PokemonsRequest: Request {
    
    case list(limit: Int?)
    
    var path: String {
        switch self {
        case .list:
            return "pokemon"
        }
    }
    
    var method: HTTPMethod {
        switch self {
        case .list:
            return .get
        }
    }
    
    var headers: [String : String]? {
        return nil
    }
    
    var parameters: RequestParameters? {
        switch self {
        case let .list(limit):
            guard let limit = limit else { return nil }
            return .url(["limit": limit])
        }
    }
    
}

Service

Requesting Data

final class PokemonsDataService: NetworkingService  {
    
    // MARK: - Properties
    
    var dispatcher: URLRequestDispatching
    
    // MARK: - Initialization
    
    init(dispatcher: URLRequestDispatching) {
        self.dispatcher = dispatcher
    }
    
    // MARK: - Requests
    
    func getList(completion: @escaping (Result<Data, Error>) -> Void) {
        
        let request: PokemonsRequest = .list
        
        dispatcher.execute(request: request) { (result) in
            switch result {
            case let .failure(error):
                completion(.failure(error))
            case .success(data):
                guard let data = data else {
                    completion(.failure(NSError()))
                    return
                }
                completion(data)
            }
        }
        
    }
    
}

Requesting Codable Objects

final class PokemonsDataService: CodableRequesting  {
    
    // MARK: - Properties
    
    var dispatcher: URLRequestDispatching
    
    // MARK: - Initialization
    
    init(dispatcher: URLRequestDispatching) {
        self.dispatcher = dispatcher
    }
    
    // MARK: - Requests
    
    func getList(completion: @escaping (Result<[Pokemon], NetworkingError>) -> Void) {
        
        let request: PokemonsRequest = .list
        
        requestCodable(request, ofType: [Pokemon].self) { (networkingResult) in
            switch networkingResult {
            case let .failure(error):
                completion(.failure(error))
            case .success(data):
                guard let data = data else {
                    completion(.failure(.noData))
                    return
                }
                completion(data)
            }
        }
        
    }
    
}

URLRequestAdapter

The URLRequestAdapter protocol allows each request that conforms with URLRequestProtocol request made on a URLRequestDispatcher to be inspected and adapted before being created. One very specific way to use an adapter is to append an Authorization header to requests behind a certain type of authentication.

final class AccessTokenAdapter: URLRequestAdapter {
    private let accessToken: String

    init(accessToken: String) {
        self.accessToken = accessToken
    }

    func adapt(_ urlRequest: URLRequest) throws -> URLRequest {
        var urlRequest = urlRequest

        if let urlString = urlRequest.url?.absoluteString, urlString.hasPrefix("https://httpbin.org") {
            urlRequest.setValue("Bearer " + accessToken, forHTTPHeaderField: "Authorization")
        }

        return urlRequest
    }
}

The request will be like this:

enum PokemonsRequest: Request {
    
    case list(limit: Int?, accessToken: String)
    
    var path: String {
        switch self {
        case .list:
            return "pokemon"
        }
    }
    
    var method: HTTPMethod {
        switch self {
        case .list:
            return .get
        }
    }
    
    var headers: [String : String]? {
        return nil
    }
    
    var parameters: RequestParameters? {
        switch self {
        case let .list(limit):
            guard let limit = limit else { return nil }
            return .url(["limit": limit])
        }
    }
    
    var adapters: [URLRequestAdapters]? {
        switch self {
            case let .list(_, accessToken):
            return [AccessTokenAdapter(accessToken: accessToken)]
        }
    }
    
}