Browse Source

Start refactoring Usage document.

Jon Shier 6 years ago
parent
commit
ef505b3d37
1 changed files with 265 additions and 194 deletions
  1. 265 194
      Documentation/Usage.md

+ 265 - 194
Documentation/Usage.md

@@ -1,235 +1,91 @@
-## Usage
+# Using Alamofire
 
-### Making a Request
+--
 
-```swift
-import Alamofire
-
-Alamofire.request("https://httpbin.org/get")
-```
-
-### Response Handling
-
-Handling the `Response` of a `Request` made in Alamofire involves chaining a response handler onto the `Request`.
-
-```swift
-Alamofire.request("https://httpbin.org/get").responseJSON { response in
-    print("Request: \(String(describing: response.request))")   // original url request
-    print("Response: \(String(describing: response.response))") // http url response
-    print("Result: \(response.result)")                         // response serialization result
-
-    if let json = response.result.value {
-        print("JSON: \(json)") // serialized json response
-    }
-
-    if let data = response.data, let utf8Text = String(data: data, encoding: .utf8) {
-        print("Data: \(utf8Text)") // original server data as UTF8 string
-    }
-}
-```
-
-In the above example, the `responseJSON` handler is appended to the `Request` to be executed once the `Request` is complete. Rather than blocking execution to wait for a response from the server, a [callback](https://en.wikipedia.org/wiki/Callback_%28computer_programming%29) in the form of a closure is specified to handle the response once it's received. The result of a request is only available inside the scope of a response closure. Any execution contingent on the response or data received from the server must be done within a response closure.
-
-> Networking in Alamofire is done _asynchronously_. Asynchronous programming may be a source of frustration to programmers unfamiliar with the concept, but there are [very good reasons](https://developer.apple.com/library/ios/qa/qa1693/_index.html) for doing it this way.
-
-Alamofire contains five different response handlers by default including:
-
-```swift
-// Response Handler - Unserialized Response
-func response(
-    queue: DispatchQueue?,
-    completionHandler: @escaping (DefaultDataResponse) -> Void)
-    -> Self
-
-// Response Data Handler - Serialized into Data
-func responseData(
-    queue: DispatchQueue?,
-    completionHandler: @escaping (DataResponse<Data>) -> Void)
-    -> Self
-
-// Response String Handler - Serialized into String
-func responseString(
-    queue: DispatchQueue?,
-    encoding: String.Encoding?,
-    completionHandler: @escaping (DataResponse<String>) -> Void)
-    -> Self
-
-// Response JSON Handler - Serialized into Any
-func responseJSON(
-    queue: DispatchQueue?,
-    completionHandler: @escaping (DataResponse<Any>) -> Void)
-    -> Self
-
-// Response PropertyList (plist) Handler - Serialized into Any
-func responsePropertyList(
-    queue: DispatchQueue?,
-    completionHandler: @escaping (DataResponse<Any>) -> Void))
-    -> Self
-```
-
-None of the response handlers perform any validation of the `HTTPURLResponse` it gets back from the server.
-
-> For example, response status codes in the `400..<500` and `500..<600` ranges do NOT automatically trigger an `Error`. Alamofire uses [Response Validation](#response-validation) method chaining to achieve this.
-
-#### Response Handler
-
-The `response` handler does NOT evaluate any of the response data. It merely forwards on all information directly from the URL session delegate. It is the Alamofire equivalent of using `cURL` to execute a `Request`.
-
-```swift
-Alamofire.request("https://httpbin.org/get").response { response in
-    print("Request: \(response.request)")
-    print("Response: \(response.response)")
-    print("Error: \(response.error)")
-
-    if let data = response.data, let utf8Text = String(data: data, encoding: .utf8) {
-    	print("Data: \(utf8Text)")
-    }
-}
-```
-
-> We strongly encourage you to leverage the other response serializers taking advantage of `Response` and `AFResult` types.
-
-#### Response Data Handler
-
-The `responseData` handler uses the `responseDataSerializer` (the object that serializes the server data into some other type) to extract the `Data` returned by the server. If no errors occur and `Data` is returned, the response `AFResult` will be a `.success` and the `value` will be of type `Data`.
-
-```swift
-Alamofire.request("https://httpbin.org/get").responseData { response in
-    debugPrint("All Response Info: \(response)")
-
-    if let data = response.result.value, let utf8Text = String(data: data, encoding: .utf8) {
-    	print("Data: \(utf8Text)")
-    }
-}
-```
-
-#### Response String Handler
+### Introduction
+Alamofire provides an elegant and composable interface to HTTP network requests. It does not implement its own HTTP networking functionality. Instead it builds on top of Apple's [URL Loading System](https://developer.apple.com/documentation/foundation/url_loading_system/) provided by the Foundation framework. At the core of the system is [`URLSession`](https://developer.apple.com/documentation/foundation/urlsession) and the [`URLSessionTask`](https://developer.apple.com/documentation/foundation/urlsessiontask) subclasses. Alamofire wraps these APIs, and many others, in an easier to use interface and provides a variety of functionality necessary for modern application development using HTTP networking. However, it's important to know where many of Alamofire's core behaviors come from, so familiarity with the URL Loading System is important. Ultimately, the networking features of Alamofire are limited by the capabilities of that system, and the behaviors and best practices should always be remembered and observed.
 
-The `responseString` handler uses the `responseStringSerializer` to convert the `Data` returned by the server into a `String` with the specified encoding. If no errors occur and the server data is successfully serialized into a `String`, the response `AFResult` will be a `.success` and the `value` will be of type `String`.
+Additionally, networking in Alamofire (and the URL Loading System in general) is done _asynchronously_. Asynchronous programming may be a source of frustration to programmers unfamiliar with the concept, but there are [very good reasons](https://developer.apple.com/library/ios/qa/qa1693/_index.html) for doing it this way.
 
-```swift
-Alamofire.request("https://httpbin.org/get").responseString { response in
-    print("Success: \(response.result.isSuccess)")
-    print("Response String: \(response.result.value)")
-}
-```
-
-> If no encoding is specified, Alamofire will use the text encoding specified in the `HTTPURLResponse` from the server. If the text encoding cannot be determined by the server response, it defaults to `.isoLatin1`.
+#### Aside: The `AF` Namespace
+Previous versions of Alamofire's documentation used examples like `Alamofire.request()`. This API, while it appeared to require the `Alamofire` prefix, in fact worked fine without it. The `request` method and other functions were available globally in any file with `import Alamofire`. Starting in Alamofire 5, this functionality has been moved out of the global [namespace](https://en.wikipedia.org/wiki/Namespace) and into the `AF` enumeration, which acts as a namespace. This allows Alamofire to offer the same convenience functionality while not having to pollute the global namespace every time Alamofire is used. Similarly, types extended by Alamofire will use a `.af.` extension prefix to separate the functionality Alamofire offers from other extensions.
 
-#### Response JSON Handler
-
-The `responseJSON` handler uses the `responseJSONSerializer` to convert the `Data` returned by the server into an `Any` type using the specified `JSONSerialization.ReadingOptions`. If no errors occur and the server data is successfully serialized into a JSON object, the response `AFResult` will be a `.success` and the `value` will be of type `Any`.
+### Making Requests
+Alamofire provides a variety of convenient methods for making HTTP requests. At the simplest level, just provide a `String` that can be converted into a `URL`:
 
 ```swift
-Alamofire.request("https://httpbin.org/get").responseJSON { response in
+AF.request("https://httpbin.org/get").response { (response) in
     debugPrint(response)
-
-    if let json = response.result.value {
-        print("JSON: \(json)")
-    }
-}
-```
-
-> All JSON serialization is handled by the `JSONSerialization` API in the `Foundation` framework.
-
-#### Chained Response Handlers
-
-Response handlers can even be chained:
-
-```swift
-Alamofire.request("https://httpbin.org/get")
-    .responseString { response in
-        print("Response String: \(response.result.value)")
-    }
-    .responseJSON { response in
-        print("Response JSON: \(response.result.value)")
-    }
-```
-
-> It is important to note that using multiple response handlers on the same `Request` requires the server data to be serialized multiple times. Once for each response handler.
-
-#### Response Handler Queue
-
-Response handlers by default are executed on the main dispatch queue. However, a custom dispatch queue can be provided instead.
-
-```swift
-let utilityQueue = DispatchQueue.global(qos: .utility)
-
-Alamofire.request("https://httpbin.org/get").responseJSON(queue: utilityQueue) { response in
-    print("Executing response handler on utility queue")
 }
 ```
+> All examples require `import Alamofire` somewhere in the source file.
 
-### Response Validation
-
-By default, Alamofire treats any completed request to be successful, regardless of the content of the response. Calling `validate` before a response handler causes an error to be generated if the response had an unacceptable status code or MIME type.
-
-#### Manual Validation
+This is actually one form of the two top-level Alamofire APIs for making requests. Its full definition looks like this:
 
 ```swift
-Alamofire.request("https://httpbin.org/get")
-    .validate(statusCode: 200..<300)
-    .validate(contentType: ["application/json"])
-    .responseData { response in
-        switch response.result {
-        case .success:
-            print("Validation Successful")
-        case .failure(let error):
-            print(error)
-        }
-    }
+public static func request<Parameters: Encodable>(_ url: URLConvertible,
+                                                  method: HTTPMethod = .get,
+                                                  parameters: Parameters? = nil,
+                                                  encoder: ParameterEncoder = URLEncodedFormParameterEncoder.default,
+                                                  headers: HTTPHeaders? = nil,
+                                                  interceptor: RequestInterceptor? = nil) -> DataRequest
 ```
+This method allows the composition of requests from individual components, such as the `method` and `headers`, while also allowing per-request `RequestInterceptor`s and `Encodable` parameters.
 
-#### Automatic Validation
+> There are additional methods that allow you to make requests using `Parameters` dictionaries. This API is no longer recommended and will eventually be deprecated and removed from Alamofire.
 
-Automatically validates status code within `200..<300` range, and that the `Content-Type` header of the response matches the `Accept` header of the request, if one is provided.
+The second version of this API is much simpler:
 
 ```swift
-Alamofire.request("https://httpbin.org/get").validate().responseJSON { response in
-    switch response.result {
-    case .success:
-        print("Validation Successful")
-    case .failure(let error):
-        print(error)
-    }
-}
+public static func request(_ urlRequest: URLRequestConvertible, 
+                           interceptor: RequestInterceptor? = nil) -> DataRequest
 ```
 
-### Response Caching
-
-Response Caching is handled on the system framework level by [`URLCache`](https://developer.apple.com/reference/foundation/urlcache). It provides a composite in-memory and on-disk cache and lets you manipulate the sizes of both the in-memory and on-disk portions.
-
-> By default, Alamofire leverages the shared `URLCache`. In order to customize it, see the [Session Manager Configurations](AdvancedUsage.md#session-manager) section.
+This method creates a `DataRequest` for any type conforming to Alamofire's `URLRequestConvertible` protocol. All of the different parameters from the previous version are incapsulated into that value, which can give rise to very powerful abstractions. This is discussed later in this documentation.
 
-### HTTP Methods
+#### HTTP Methods
 
 The `HTTPMethod` enumeration lists the HTTP methods defined in [RFC 7231 §4.3](https://tools.ietf.org/html/rfc7231#section-4.3):
 
 ```swift
 public enum HTTPMethod: String {
-    case options = "OPTIONS"
+    case connect = "CONNECT"
+    case delete  = "DELETE"
     case get     = "GET"
     case head    = "HEAD"
+    case options = "OPTIONS"
+    case patch   = "PATCH"
     case post    = "POST"
     case put     = "PUT"
-    case patch   = "PATCH"
-    case delete  = "DELETE"
     case trace   = "TRACE"
-    case connect = "CONNECT"
 }
 ```
 
-These values can be passed as the `method` argument to the `Alamofire.request` API:
+These values can be passed as the `method` argument to the `AF.request` API:
 
 ```swift
-Alamofire.request("https://httpbin.org/get") // method defaults to `.get`
+AF.request("https://httpbin.org/get")
+AF.request("https://httpbin.org/post", method: .post)
+AF.request("https://httpbin.org/put", method: .put)
+AF.request("https://httpbin.org/delete", method: .delete)
+```
 
-Alamofire.request("https://httpbin.org/post", method: .post)
-Alamofire.request("https://httpbin.org/put", method: .put)
-Alamofire.request("https://httpbin.org/delete", method: .delete)
+It's important to remember that the different HTTP methods may have different semantics and require different parameter encodings depending on what the server expects. For instance, passing body data in a `GET` requests can cause timeouts or other errors when communicating with servers that don't support that configuration.
+
+Alamofire also offers an extension on `URLRequest` to bridge the `httpMethod` property that returns a `String` to an `HTTPMethod` value:
+
+```swift
+public extension URLRequest {
+    /// Returns the `httpMethod` as Alamofire's `HTTPMethod` type.
+    var method: HTTPMethod? {
+        get { return httpMethod.flatMap(HTTPMethod.init) }
+        set { httpMethod = newValue?.rawValue }
+    }
+}
 ```
 
-> The `Alamofire.request` method parameter defaults to `.get`.
+If you need to use an HTTP method that Alamofire's `HTTPMethod` type doesn't support, you can still set the `String` `httpMethod` property on `URLRequest` directly.
+
 
 ### Parameter Encoding
 
@@ -401,6 +257,221 @@ The default Alamofire `SessionManager` provides a default set of headers for eve
 
 If you need to customize these headers, a custom `URLSessionConfiguration` should be created, the `defaultHTTPHeaders` property updated and the configuration applied to a new `SessionManager` instance.
 
+#### Request Parameters and Parameter Encoders
+
+Alamofire supports sending any `Encodable`-conforming type as the parameters of a request. It also provides two builtin types conforming to the `ParameterEncoder` protocol: `URLFormEndcodedParameterEncoder` and `JSONParameterEncoder`. These types cover the most common encodings used by modern services (XML encoding is left as an exercise for the reader). 
+
+```swift
+struct Login: Encodable {
+    let email: String
+    let password: String
+}
+let login = Login(email: "test@test.test", password: "testPassword")
+AF.request("https://httpbin.org/post",
+           method: .post,
+           parameters: login,
+           encoder: JSONParameterEncoder.default).response { (response) in
+    debugPrint(response)
+}
+```
+
+
+
+### Response Handling
+
+Handling the `Response` of a `Request` made in Alamofire involves chaining a response handler onto the `Request`.
+
+```swift
+Alamofire.request("https://httpbin.org/get").responseJSON { response in
+    print("Request: \(String(describing: response.request))")   // original url request
+    print("Response: \(String(describing: response.response))") // http url response
+    print("Result: \(response.result)")                         // response serialization result
+
+    if let json = response.result.value {
+        print("JSON: \(json)") // serialized json response
+    }
+
+    if let data = response.data, let utf8Text = String(data: data, encoding: .utf8) {
+        print("Data: \(utf8Text)") // original server data as UTF8 string
+    }
+}
+```
+
+In the above example, the `responseJSON` handler is appended to the `Request` to be executed once the `Request` is complete. Rather than blocking execution to wait for a response from the server, a [callback](https://en.wikipedia.org/wiki/Callback_%28computer_programming%29) in the form of a closure is specified to handle the response once it's received. The result of a request is only available inside the scope of a response closure. Any execution contingent on the response or data received from the server must be done within a response closure.
+
+> Networking in Alamofire is done _asynchronously_. Asynchronous programming may be a source of frustration to programmers unfamiliar with the concept, but there are [very good reasons](https://developer.apple.com/library/ios/qa/qa1693/_index.html) for doing it this way.
+
+Alamofire contains five different response handlers by default including:
+
+```swift
+// Response Handler - Unserialized Response
+func response(
+    queue: DispatchQueue?,
+    completionHandler: @escaping (DefaultDataResponse) -> Void)
+    -> Self
+
+// Response Data Handler - Serialized into Data
+func responseData(
+    queue: DispatchQueue?,
+    completionHandler: @escaping (DataResponse<Data>) -> Void)
+    -> Self
+
+// Response String Handler - Serialized into String
+func responseString(
+    queue: DispatchQueue?,
+    encoding: String.Encoding?,
+    completionHandler: @escaping (DataResponse<String>) -> Void)
+    -> Self
+
+// Response JSON Handler - Serialized into Any
+func responseJSON(
+    queue: DispatchQueue?,
+    completionHandler: @escaping (DataResponse<Any>) -> Void)
+    -> Self
+
+// Response PropertyList (plist) Handler - Serialized into Any
+func responsePropertyList(
+    queue: DispatchQueue?,
+    completionHandler: @escaping (DataResponse<Any>) -> Void))
+    -> Self
+```
+
+None of the response handlers perform any validation of the `HTTPURLResponse` it gets back from the server.
+
+> For example, response status codes in the `400..<500` and `500..<600` ranges do NOT automatically trigger an `Error`. Alamofire uses [Response Validation](#response-validation) method chaining to achieve this.
+
+#### Response Handler
+
+The `response` handler does NOT evaluate any of the response data. It merely forwards on all information directly from the URL session delegate. It is the Alamofire equivalent of using `cURL` to execute a `Request`.
+
+```swift
+Alamofire.request("https://httpbin.org/get").response { response in
+    print("Request: \(response.request)")
+    print("Response: \(response.response)")
+    print("Error: \(response.error)")
+
+    if let data = response.data, let utf8Text = String(data: data, encoding: .utf8) {
+    	print("Data: \(utf8Text)")
+    }
+}
+```
+
+> We strongly encourage you to leverage the other response serializers taking advantage of `Response` and `AFResult` types.
+
+#### Response Data Handler
+
+The `responseData` handler uses the `responseDataSerializer` (the object that serializes the server data into some other type) to extract the `Data` returned by the server. If no errors occur and `Data` is returned, the response `AFResult` will be a `.success` and the `value` will be of type `Data`.
+
+```swift
+Alamofire.request("https://httpbin.org/get").responseData { response in
+    debugPrint("All Response Info: \(response)")
+
+    if let data = response.result.value, let utf8Text = String(data: data, encoding: .utf8) {
+    	print("Data: \(utf8Text)")
+    }
+}
+```
+
+#### Response String Handler
+
+The `responseString` handler uses the `responseStringSerializer` to convert the `Data` returned by the server into a `String` with the specified encoding. If no errors occur and the server data is successfully serialized into a `String`, the response `AFResult` will be a `.success` and the `value` will be of type `String`.
+
+```swift
+Alamofire.request("https://httpbin.org/get").responseString { response in
+    print("Success: \(response.result.isSuccess)")
+    print("Response String: \(response.result.value)")
+}
+```
+
+> If no encoding is specified, Alamofire will use the text encoding specified in the `HTTPURLResponse` from the server. If the text encoding cannot be determined by the server response, it defaults to `.isoLatin1`.
+
+#### Response JSON Handler
+
+The `responseJSON` handler uses the `responseJSONSerializer` to convert the `Data` returned by the server into an `Any` type using the specified `JSONSerialization.ReadingOptions`. If no errors occur and the server data is successfully serialized into a JSON object, the response `AFResult` will be a `.success` and the `value` will be of type `Any`.
+
+```swift
+Alamofire.request("https://httpbin.org/get").responseJSON { response in
+    debugPrint(response)
+
+    if let json = response.result.value {
+        print("JSON: \(json)")
+    }
+}
+```
+
+> All JSON serialization is handled by the `JSONSerialization` API in the `Foundation` framework.
+
+#### Chained Response Handlers
+
+Response handlers can even be chained:
+
+```swift
+Alamofire.request("https://httpbin.org/get")
+    .responseString { response in
+        print("Response String: \(response.result.value)")
+    }
+    .responseJSON { response in
+        print("Response JSON: \(response.result.value)")
+    }
+```
+
+> It is important to note that using multiple response handlers on the same `Request` requires the server data to be serialized multiple times. Once for each response handler.
+
+#### Response Handler Queue
+
+Response handlers by default are executed on the main dispatch queue. However, a custom dispatch queue can be provided instead.
+
+```swift
+let utilityQueue = DispatchQueue.global(qos: .utility)
+
+Alamofire.request("https://httpbin.org/get").responseJSON(queue: utilityQueue) { response in
+    print("Executing response handler on utility queue")
+}
+```
+
+### Response Validation
+
+By default, Alamofire treats any completed request to be successful, regardless of the content of the response. Calling `validate` before a response handler causes an error to be generated if the response had an unacceptable status code or MIME type.
+
+#### Manual Validation
+
+```swift
+Alamofire.request("https://httpbin.org/get")
+    .validate(statusCode: 200..<300)
+    .validate(contentType: ["application/json"])
+    .responseData { response in
+        switch response.result {
+        case .success:
+            print("Validation Successful")
+        case .failure(let error):
+            print(error)
+        }
+    }
+```
+
+#### Automatic Validation
+
+Automatically validates status code within `200..<300` range, and that the `Content-Type` header of the response matches the `Accept` header of the request, if one is provided.
+
+```swift
+Alamofire.request("https://httpbin.org/get").validate().responseJSON { response in
+    switch response.result {
+    case .success:
+        print("Validation Successful")
+    case .failure(let error):
+        print(error)
+    }
+}
+```
+
+### Response Caching
+
+Response Caching is handled on the system framework level by [`URLCache`](https://developer.apple.com/reference/foundation/urlcache). It provides a composite in-memory and on-disk cache and lets you manipulate the sizes of both the in-memory and on-disk portions.
+
+> By default, Alamofire leverages the shared `URLCache`. In order to customize it, see the [Session Manager Configurations](AdvancedUsage.md#session-manager) section.
+
+
+
 ### Authentication
 
 Authentication is handled on the system framework level by [`URLCredential`](https://developer.apple.com/reference/foundation/nsurlcredential) and [`URLAuthenticationChallenge`](https://developer.apple.com/reference/foundation/urlauthenticationchallenge).
@@ -718,4 +789,4 @@ $ curl -i \
     "https://httpbin.org/get?foo=bar"
 ```
 
----
+---