⚠️ Archived - Use Alamofire 5 or later instead
BothamNetworking is a networking framework written in Swift.
This project will help you to setup all your API clients and implement your networking layer easily. BothamNetworking provides classes to send HTTP requests and obtain HTTP responses ready to parse with just a few lines.
In addition, BothamNetworking adds a Request/Response interceptor mechanisms to be able to modify the HTTPRequest before being sent and the HTTPResponse after being received. This mechanism can be used to implement authentication policies, add headers to a request or add log traces. BothamNetworking contains some already implemented interceptors like NSLogInterceptor
, JSONHeadersRequestInterceptor
and some authentication mechanisms like BaseAuthentication
.
##Usage
This framework contains all the classes needed to implement your networking layer with an easy to use facade named BothamAPIClient
. If you love Swift Playgrounds clone this project and review the playgrounds we've created to show how to use this framework.
###Send a request using different HTTP methods:
let apiClient = BothamAPIClient(baseEndpoint: "https://github.com/gitapi/repos/Karumi/BothamNetworking")
apiClient.GET("/issues")
apiClient.POST("/issues")
apiClient.PUT("/issues")
apiClient.DELETE("/issues/1")
###Add headers to the request:
apiClient.GET("/issues", headers: ["User-Agent": "BothamNetworking Headers", "Accept": "application/json; q=0.5"])
###Add parameters to the request:
apiClient.DELETE("/issues", parameters: ["id": "1"])
###Add a body to the request:
apiClient.POST("/authorizations", body: ["scopes": ["repo_status", "user:email"]])
The body encoding will be determined by the HTTP headers used. To encode your body using json add a "ContentType: application/json" header to your request
###Request execution result:
BothamNetworking uses Result
return type composed by HTTPResponse
or BothamAPIClientError
instances. We have added a ResultType
extension to be able to provide an easy to use mechanism to parse your response information using SwiftyJSON
as parsing library.
apiClient.GET("/repos") { result in
result.mapJSON { json in
for result in json["results"].arrayValue {
let id = result["id"].stringValue
let name = result["name"].stringValue
}
}
}
This is the information available in the HTTPResponse
struct:
public struct HTTPResponse {
public let statusCode: Int
public let headers: CaseInsensitiveDictionary<String>?
public let body: NSData
...
}
The errors BothamNetworking can return are:
public enum BothamAPIClientError: ErrorType, Equatable {
case HTTPResponseError(statusCode: Int, body: NSData)
case NetworkError
case HTTPClientError(error: NSError)
case ParsingError(error: NSError)
case UnsupportedURLScheme
case Retry
}
###Interceptors:
BothamRequestInterceptor
and BothamResponseInterceptor
are two protocols you can use to modify a HTTPRequest
instance before sending it or a HTTPResponse
before receiving it. This mechansim can be used to implement authentication policies, add default information to a request, add log traces or retry requests. An example could be NSLogInterceptor
, JSONHeadersRequestInterceptor
or BasicAuthentication
.
class JSONHeadersRequestInterceptor: BothamRequestInterceptor {
func intercept(request: HTTPRequest) -> HTTPRequest {
return request.appendingHeaders(["Content-Type": "application/json", "Accept": "application:json"])
}
}
public protocol BasicAuthentication: BothamRequestInterceptor, BothamResponseInterceptor {
var credentials: (username: String, password: String) { get }
func onAuthenticationError(realm: String) -> Void
}
extension BasicAuthentication {
public func intercept(request: HTTPRequest) -> HTTPRequest {
let userPass = "\(credentials.username):\(credentials.password)"
let userPassData = userPass.dataUsingEncoding(NSUTF8StringEncoding)!
let base64UserPass = userPassData.base64EncodedStringWithOptions([])
let header = ["Authorization" : "Basic \(base64UserPass)"]
return request.appendingHeaders(header)
}
public func intercept(response: HTTPResponse,
completion: (Result<HTTPResponse, BothamAPIClientError>) -> Void) {
if response.statusCode == 401, let unauthorizedHeader = response.headers?["WWW-Authenticate"] {
let regex = try! NSRegularExpression(pattern: "Basic realm=\"(.*)\"", options: [])
let range = NSMakeRange(0, unauthorizedHeader.utf8.count)
if let match = regex.firstMatchInString(unauthorizedHeader, options: [], range: range) {
let realm = (unauthorizedHeader as NSString).substringWithRange(match.rangeAtIndex(1))
onAuthenticationError(realm)
}
}
completion(Result.Success(response))
}
}
Interceptors can be added to a BothamAPIClient
instance or to all the BothamAPIClient
instances at the same time. Interceptors added globally will be evaluated before and after every request independently of the BothamAPIClient
instance. Interceptors added locally will be just appplied to the BothamAPIClient
instance where you add those interceptors.
let apiClient = BothamAPIClient(baseEndpoint: "https://github.com/gitapi/repos/Karumi/")
//Add interceptors locally
apiClient.requestInterceptors.append(NSLogInterceptor())
apiClient.responseInterceptors.append(NSLogInterceptor())
//Add interceptors globally
BothamAPIClient.globalRequestInterceptors.append(NSLogInterceptor())
BothamAPIClient.globalResponseInterceptors.append(NSLogInterceptor())
###Retry requests:
To be able to retry a request add a BothamResponseInterceptor
and return a BothamAPIClientError.RetryError
when needed. BothamAPIClient
will automatically retry the original request you sent.
class RetryInterceptor: BothamResponseInterceptor {
func intercept(response: HTTPResponse, completion: (Result<HTTPResponse, BothamAPIClientError>) -> Void) {
if response.statusCode == 401 {
completion(Result.Failure(.RetryError))
} else {
completion(Result.Success(response))
}
}
}
Be careful when using this mechanism as you can create an infinite loop or DOS yourself. Remember you should always call completion callback to do not break the BothamResponseInterceptor
chain.
Copyright 2016 Karumi
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.