AlamofireURLRequestConfigurable 1.2

AlamofireURLRequestConfigurable 1.2

TestsTested
LangLanguage SwiftSwift
License MIT
ReleasedLast Release Oct 2019
SPMSupports SPM

Maintained by George Marmaridis.



URLRequestConfigurable for Alamofire

A replacement for Alamofire's URLRequestConvertible for even cleaner and flexible type safe routing.

Wait, but why?

This is an example for URLRequestConvertible taken straight from Alamofire's documentation:

enum Router: URLRequestConvertible {
    case createUser(parameters: Parameters)
    case readUser(username: String)
    case updateUser(username: String, parameters: Parameters)
    case destroyUser(username: String)

    static let baseURLString = "https://example.com"

    var method: HTTPMethod {
        switch self {
        case .createUser:
            return .post
        case .readUser:
            return .get
        case .updateUser:
            return .put
        case .destroyUser:
            return .delete
        }
    }

    var path: String {
        switch self {
        case .createUser:
            return "/users"
        case .readUser(let username):
            return "/users/\(username)"
        case .updateUser(let username, _):
            return "/users/\(username)"
        case .destroyUser(let username):
            return "/users/\(username)"
        }
    }

    // MARK: URLRequestConvertible

    func asURLRequest() throws -> URLRequest {
        let url = try Router.baseURLString.asURL()

        var urlRequest = URLRequest(url: url.appendingPathComponent(path))
        urlRequest.httpMethod = method.rawValue

        switch self {
        case .createUser(let parameters):
            urlRequest = try URLEncoding.default.encode(urlRequest, with: parameters)
        case .updateUser(_, let parameters):
            urlRequest = try URLEncoding.default.encode(urlRequest, with: parameters)
        default:
            break
        }

        return urlRequest
    }
}

It's not really easy to understand what's going on here with a quick look, is it? That's because the URL request's configuration is scattered throughout the implementation, also leading to multiple switch statements. This is what the same example looks like when written using URLRequestConfigurable:

enum Router: URLRequestConfigurable {
    case createUser(parameters: Parameters)
    case readUser(username: String)
    case updateUser(username: String, parameters: Parameters)
    case destroyUser(username: String)

    static let baseURLString = "http://example.com"

    // MARK: URLRequestConfigurable
    var urlRequestConfiguration: URLRequestConfiguration {
        switch self {
        case .createUser(let parameters):
            return URLRequestConfiguration(url: "\(Router.baseURLString)/users",
                                           method: .post,
                                           parameters: parameters,
                                           encoding: URLEncoding.default)
        case .readUser(let username):
            return URLRequestConfiguration(url: "\(Router.baseURLString)/users/\(username)",
                                           method: .get)
        case .updateUser(let username, let parameters):
            return URLRequestConfiguration(url: "\(Router.baseURLString)/users/\(username)",
                                           method: .put,
                                           parameters: parameters,
                                           encoding: URLEncoding.default)
        case .destroyUser(let username):
            return URLRequestConfiguration(url: "\(Router.baseURLString)/users/\(username)",
                                           method: .delete)
        }
    }
}

More structured and readable, right? With URLRequestConfigurable, the URL request's configuration is enforced to be declared in one place and one place only. This results in a consistent clean look across all Routers.

Also note that as of version 1.1 all the values but the url can be omitted if not needed, reducing the number of lines used even further.

Lets look at another example from Alamofire:

enum Router: URLRequestConvertible {
    case search(query: String, page: Int)

    static let baseURLString = "https://example.com"
    static let perPage = 50

    // MARK: URLRequestConvertible

    func asURLRequest() throws -> URLRequest {
        let result: (path: String, parameters: Parameters) = {
            switch self {
            case let .search(query, page) where page > 0:
                return ("/search", ["q": query, "offset": Router.perPage * page])
            case let .search(query, _):
                return ("/search", ["q": query])
            }
        }()

        let url = try Router.baseURLString.asURL()
        let urlRequest = URLRequest(url: url.appendingPathComponent(result.path))

        return try URLEncoding.default.encode(urlRequest, with: result.parameters)
    }
}

And again, look how the example is transformed into something more pleasant to the eye with URLRequestConfigurable:

enum Router: URLRequestConfigurable {
    case Search(query: String, page: Int)

    static let baseURLString = "http://example.com"
    static let perPage = 50

    // MARK: URLRequestConfigurable
    var urlRequestConfiguration: URLRequestConfiguration {
        switch self {
        case .Search(let query, let page) where page > 1:
            return URLRequestConfiguration(url: "\(Router.baseURLString)/search",
                                           method: .get,
                                           parameters: ["q": query, "offset": Router.perPage * page],
                                           encoding: URLEncoding.default)
        case .Search(let query, _):
            return URLRequestConfiguration(url: "\(Router.baseURLString)/search",
                                           method: .get,
                                           parameters: ["q": query],
                                           encoding: URLEncoding.default)
        }
    }
}

Usage

Using URLRequestConfigurable is as easy as making your Routers conform to the URLRequestConfigurable protocol. You can then use Alamofire normally to perform the requests like before:

Alamofire.SessionManager.default.request(Router.get())
.responseJSON { response in
    if let JSON = response.result.value {
        print("JSON: \(JSON)")
    }
}

Example

To run the example project, clone the repo, and run pod install from the Example directory first.

If you want to get results back from GiantBomb (optional), you will need to create your own GiantBomb API key here.

Requirements

  • Alamofire
  • iOS 8.0+ / Mac OS X 10.9+ / tvOS 9.0+ / watchOS 2.0+
  • Xcode 7.3+

Installation

AlamofireURLRequestConfigurable is available through CocoaPods. To install it, simply add the following line to your Podfile:

Swift 3.0

pod 'AlamofireURLRequestConfigurable', '~> 1.1'

Swift 2.x

pod 'AlamofireURLRequestConfigurable', '1.0.1'

Author

George Marmaridis

License

URLRequestConfigurable is available under the MIT license. See the LICENSE file for more info.