Swiftpack.co - Package - shortcut/NetworkKit

CircleCI

NetworkKit

This package provides basic Networking.

Usage

Request

You can make requests on the shared Network object at NK or create your own Network. For example if you wanted to request a decoded User object, you could do:

NK.request(UsersAPI.getUser(id: 123)).responseDecoded(of: User.self) { response in
    switch response.result {
    case let .success(data):
        print("success \(data)")
    case let .failure(error):
        print("error: \(error)")
    }
}

You can request a urlString, URL, URLRequest or a TargetType:

NK.request("http://google")
NK.request(URL(string: "http://google"))
NK.request(URLRequest(url: URL(string:"http://google")))
NK.request(target)

You can also do .response if you just want the data, or .responseString if you want a string

Response

The type of object you get back from a response is Response, which is a wrapper around the Result<SomeModel, NetworkError> and holds everything you might need about the response, such as the originating URLRequest, the URLResponse object and raw data.

public struct Response<Success, Failure: Error> {
    public var request: URLRequest?
    public var response: URLResponse?

    public var data: Data?
    public let result: Result<SuccessType, NetworkError>
}

extension Response {
    public var statusCode: Int?
    public func localizedStringForStatusCode() -> String? 
    public var allHeaderFields: [AnyHashable: Any]? 
}

To cancel a request, hold on to the Request object and cancel it whenever.

let request = NK.request(UsersAPI.getUsers)
request.response { response in 
// blabla
}

request.cancel()

TargetType

Create an object that conforms to TargetType to define the HTTP details of your API's endpoints

enum UsersAPI {
    case createUser(id: String)
    case getUsers(name: String)
}

extension UsersAPI: TargetType {
    var baseURL: URL { URL(string: "http://example.com/")! }
    
    var headerValues: HTTPHeaders? {
        ["api-subscription-key": "asdkfhaskjdfh",
         "Accept": "application/json"]
    }
    
    var path: String {
        switch self {
        case .createUser(let id, _):
            return "/\(id)"
        case .getUsers(let name):
            return "/session/nfc/\(name)"
        }
    }
    
    var method: HTTPMethod {
        switch self {
        case .createUser:
            return .post
        case .getUsers:
            return .get
        }
    }
    
    var bodyType: HTTPBodyType {
        switch self {
        case .createUser:
            return .json
        case .getUsers:
            return .none
        }
    }
    
    var body: Encodable? {
        switch self {
        case .createUser(_, let payload):
            return payload
        case .getUsers:
            return nil
        }
    }
}

Validation

You can validate requests after the response if you need to define what counts as an error, for example if the HTTP status code is not 200. You can chain multiple validations, custom or not

NK.request(UserAPI.getUser("bob").validate().responseDecoded(of: TestModel.self) { response in
    
}

Custom error parsing

If your server returns different schemas on errors that you'd like to parse, you can supply a type that will be used to parse that, in the cases when validation fails.

So for example if you are calling an authorization server that returns status code 401 and some json describing the error, you can parse that and get it in the NetworkError


network?.request(AuthAPI.authenticate(with: credentials))
    .validate()
    .responseDecoded(of: AuthState.self,
                     errorType: AuthError.self) { response in

        switch response.result {
        case .success:
            // do fun stuff
        case let .failure(error):
            // get the AuthError object
            if case let .errorResponse(errorObject) = error {
                // do fun stuff with your AuthError object
            }
        }
}

Request Adaptors

If you'd like to adapt the URLRequest before transport, you can pass in a RequestAdaptor to your request, for example if you'd like to add runtime authentication headers:

public struct AuthenticationAdapter: RequestAdapter {
    let accessToken: AccessToken

    public func adapt(_ urlRequest: URLRequest) -> URLRequest {
        var urlRequest = urlRequest
        urlRequest.setValue("Bearer \(accessToken)", forHTTPHeaderField: "Authorization")
        return urlRequest
    }
}

self.network.request(target)
    .withAdapter(AuthenticationAdapter(accessToken: accessToken))
    .responseDecoded(of: T.self) { response in
        switch response.result {
        case let .success(products):
            completion(.success(products))
        case let .failure(error):
            completion(.failure(ApplicationError.networkError(error)))
        }
}

Github

link
Stars: 0

Dependencies

Used By

Total: 0

Releases

0.1.5: Bug fix - 2020-02-04 12:41:41

Fixed retain cycle on requests

0.1.4: Validation and custom error parsing - 2020-01-31 08:44:09

  • add better validation of requests
  • be able to specify a model type for error responses (details in README)

bux fixes - 2019-12-05 15:03:14

  • update internal urlRequest after going through all the RequestAdapters
  • actually use the jsonDecoder passed in
  • don't let Request retain the parent Network

Add RequestAdapter - 2019-11-27 12:58:18

New protocol for adding adapters (middleware) to a request:

public protocol RequestAdapter {
    func adapt(_ urlRequest: URLRequest) -> URLRequest
}

Request now has adapters: [RequestAdapter] and func withAdapter(_ adapter: RequestAdapter) -> Self which you can use to chain the addition of adapters

Example:

                network.request(OrderAPI.getOrders)
                    .withAdapter(AuthenticationAdapter(accessToken: accessToken))
                    .responseDecoded(of: OrderResponse.self)

Fixes DiskRequest and adds TargetType properties for mock data - 2019-11-25 14:13:36

TargetType protocol now includes:

    var diskPath: String? { get } // for mocks using DiskRequest
    var diskDelay: TimeInterval { get }

in order for a target to define a local json file for the request, and an optional delay (to test loading UI for example)

New Network API - 2019-11-06 10:52:04

This release introduces an almost complete rewrite of the API. See README for more info

- 2019-10-22 12:29:47

- 2019-10-18 11:53:31