This article is a translation of Alamofire Usage. If necessary, please refer to the original text.

The content of this article is also saved to my GitHub repository. If you find it useful, you can give it a Star. Thank you very much!

features

  • Linkable request/response functions
  • URL/JSON parameter encoding
  • Upload file/Data/stream/multi-form Data
  • Download files using request or recovery data
  • Use URLCredential for authentication
  • HTTP Response Validation
  • Upload and download closures with progress
  • CURL The output of the cURL command
  • Adjust and retry requests dynamically
  • The TLS certificate and public key are fixed
  • Network accessibility
  • Comprehensive unit and integration test coverage

Component library

In order for Alamofire to focus on core network implementation, the Alamofire Software Foundation has created additional component libraries that bring additional functionality to the Alamofire ecosystem.

  • AlamofireImage: an image library that includes the image response serializer,UIImageUIImageViewExtension, custom image filters, automatic memory clearing cache and priority-based image download system.
  • AlamofireNetworkActivityIndicator: use Alamofire control the visibility of the network activity indicator on iOS. It includes a configurable delay timer to help reduce flickering, and can support URLSession instances that are not managed by Alamofire.

Required operating environment

  • IOS 10.0+ / macOS 10.12+ / tvOS 10.0+ / watchOS 3.0+
  • Xcode 10.2 +
  • Swift 5+

Installation method

CocoaPods

source 'https://github.com/CocoaPods/Specs.git'
platform :ios.'10.0'
use_frameworks!

target 'Project Name' do
    pod 'Alamofire'.'~ > 5.0'
end
Copy the code

The iOS and Alamofire versions can be changed by themselves based on actual conditions. CocoaPods is a popular third-party library management tool, but I won’t go into details about other methods. For other integration methods, see the original documentation.

Basic usage

introduce

Alamofire provides an elegant and composable interface for HTTP web requests. It does not implement its own HTTP networking capabilities. Instead, it is built on top of a URL loading system provided by the Foundation framework. The core of the system is URLSession and URLSessionTask subclasses. Alamofire encapsulates these and many other APIs in an interface that is easier to use and provides the capabilities necessary for modern application development using the HTTP network. However, it’s important to understand where much of Alamofire’s core behavior comes from, so it’s important to be familiar with the URL loading system. Ultimately, Alamofire’s network features are limited by the capabilities of the system, and its behavior and best practices should always be remembered and followed.

In addition, networking in Alamofire (and the URL loading system) is done asynchronously. Asynchronous programming can be frustrating for programmers unfamiliar with the concept, but there are good reasons to do so.

In addition:AFNamespaces and references

The previous Alamofire document used an example like alamofire.request (). This API, while seemingly requiring the Alamofire prefix, actually works without it. The Request method and other functions are globally available in any file with the import Alamofire. As of Alamofire 5, this feature has been removed and changed to AF, which is a reference to session.default. This allows Alamofire to provide the same convenience without having to pollute the global namespace every time Alamofire is used or replicate the Session API globally. Similarly, types extended by Alamofire will use the AF attribute extension to separate functionality added by Alamofire from other extensions.

The initiating

Alamofire provides several convenient ways to make HTTP requests. At its simplest, simply provide a String that can be converted to a URL:

AF.request("https://httpbin.org/get").response { response in
    debugPrint(response)
}
Copy the code

All examples need to import Alamofire somewhere in the source file.

This is actually a form of the two top-level APIs on the Alamofire Session type for making requests. Its full definition is as follows:

open func request<Parameters: Encodable> (_ convertible: URLConvertible.method: HTTPMethod = .get,
    parameters: Parameters? = nil.encoder: ParameterEncoder = URLEncodedFormParameterEncoder.default,
    headers: HTTPHeaders? = nil.interceptor: RequestInterceptor? = nil
) -> DataRequest
Copy the code

This method creates a DataRequest that allows composing requests from components such as Method and HEADERS, as well as each incoming RequestInterceptors and Encodable parameters.

There are other methods that allow you to make a request using the Parameters dictionary and ParameterEncoding types. This API is no longer recommended and will eventually be deprecated and removed from Alamofire.

The second version of the API is much simpler:

open func request(
    _ urlRequest: URLRequestConvertible.interceptor: RequestInterceptor? = nil
) -> DataRequest
Copy the code

This method creates a DataRequest for any type that follows Alamofire’s URLRequestConvertible protocol. All parameters that differ from the previous version are encapsulated in this value, which results in a very powerful abstraction. This will be discussed in our advanced usage.

HTTP Methods

The HTTPMethod type lists HTTP methods as defined in RFC 7231 §4.3:

public struct HTTPMethod: RawRepresentable.Equatable.Hashable {
    public static let connect = HTTPMethod(rawValue: "CONNECT")
    public static let delete = HTTPMethod(rawValue: "DELETE")
    public static let get = HTTPMethod(rawValue: "GET")
    public static let head = HTTPMethod(rawValue: "HEAD")
    public static let options = HTTPMethod(rawValue: "OPTIONS")
    public static let patch = HTTPMethod(rawValue: "PATCH")
    public static let post = HTTPMethod(rawValue: "POST")
    public static let put = HTTPMethod(rawValue: "PUT")
    public static let trace = HTTPMethod(rawValue: "TRACE")

    public let rawValue: String

    public init(rawValue: String) {
        self.rawValue = rawValue
    }
}
Copy the code

These values can be passed to the AF.request API as method arguments:

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)
Copy the code

It is important to remember that different HTTP methods may have different semantics and require different encoding of parameters, depending on what the server expects. For example, URLSession or Alamofire do not support passing body data in a GET request and will return an error.

Alamofire also provides an extension to URLRequest to bridge the string back to the HTTPMethod attribute of the HTTPMethod value:

public extension URLRequest {
    /// Returns the `httpMethod` as Alamofire's `HTTPMethod` type.
    var method: HTTPMethod? {
        get { return httpMethod.flatMap(HTTPMethod.init)}set { httpMethod = newValue?.rawValue }
    }
}
Copy the code

If you need to use HTTP methods that are not supported by Alamofire’s HTTPMethod type, you can extend that type to add custom values:

extension HTTPMethod {
    static let custom = HTTPMethod(rawValue: "CUSTOM")}Copy the code

Request parameters and parameter encoders

Alamofire supports any Encodable type as a request parameter. These parameters are then passed in types that comply with the ParameterEncoder protocol, added to the URLRequest, and sent over the network. Alamofire contains two types of follow ParameterEncoder: JSONParameterEncoder and URLEncodedFormParameterEncoder. These types cover the most common encodings used by modern services.

struct Login: Encodable {
    let email: String
    let password: String
}

let login = Login(email: "[email protected]", password: "testPassword")

AF.request("https://httpbin.org/post",
           method: .post,
           parameters: login,
           encoder: JSONParameterEncoder.default).response { response in
    debugPrint(response)
}
Copy the code

URLEncodedFormParameterEncoder

URLEncodedFormParameterEncoder string coding for URL encoding will be value to set it to or attached to any existing URL query string, or is set to the HTTP request body. You can control where the encoding string is set by setting the encoding destination. URLEncodedFormParameterEncoder. There are at least three types of Destination enumeration:

  • .methodDependent– for.get,.head,.deleteRequest, which applies the encoded query string to an existing query string; For other types of requests, it is set to HTTP Body.
  • .queryString– Sets or appends an encoding string to the requested URL.
  • .httpBody– Sets the encoding string toURLRequestThe HTTP body.

If content-Type is not already set, the HTTP header of an encoded request with an HTTP body is set to Application/X-www-form-urlencoded; Charset = utf-8.

Internally, URLEncodedFormParameterEncoder use URLEncodedFormEncoder Encodable type code into a URL String in the form of coding. This encoder can be used to customize various types of encodings, These include Array using ArrayEncoding, Bool using BoolEncoding, Data using DataEncoding, Date using DateEncoding, and keys using KeyEncoding And a space using SpaceEncoding.

GET requests with URL-encoded parameters
let parameters = ["foo": "bar"]

// The following three methods are equivalent
AF.request("https://httpbin.org/get", parameters: parameters) // encoding defaults to `URLEncoding.default`
AF.request("https://httpbin.org/get", parameters: parameters, encoder: URLEncodedFormParameterEncoder.default)
AF.request("https://httpbin.org/get", parameters: parameters, encoder: URLEncodedFormParameterEncoder(destination: .methodDependent))

// https://httpbin.org/get?foo=bar
Copy the code
POST requests with URL-encoded parameters
let parameters: [String: [String]] = [
    "foo": ["bar"]."baz": ["a"."b"]."qux": ["x"."y"."z"]]// The following three methods are equivalent
AF.request("https://httpbin.org/post", method: .post, parameters: parameters)
AF.request("https://httpbin.org/post", method: .post, parameters: parameters, encoder: URLEncodedFormParameterEncoder.default)
AF.request("https://httpbin.org/post", method: .post, parameters: parameters, encoder: URLEncodedFormParameterEncoder(destination: .httpBody))

// HTTP body: "qux[]=x&qux[]=y&qux[]=z&baz[]=a&baz[]=b&foo[]=bar"
Copy the code

Configures the ordering of encoded parameters

Starting with Swift 4.2, Swift’s Dictionary type uses a random algorithm that produces a random internal order at run time and is different each time the application is started. This can cause encoded parameters to change their order, which can affect caching and other behavior. By default, URLEncodedFormEncoder sorts its encoded key-value pairs. While this generates constant output for all Encodable types, it may not match the actual encoding order implemented for that type. You can set alphabetizeKeyValuePairs to false to return to the order of implementation, so this will become a random Dictionary order.

You can create your own URLEncodedFormParameterEncoder, during initialization, can set the value of alphabetizeKeyValuePairs in URLEncodedFormEncoder parameters:

let encoder = URLEncodedFormParameterEncoder(encoder: URLEncodedFormEncoder(alphabetizeKeyValuePairs: false))
Copy the code
configurationArrayParameter encoding

Since there is no specification on how to encode collection types, by default, Alamofire follows the following convention: append [] to a key of array values (foo[]=1&foo[]=2), and append a key of nested dictionary values surrounded by brackets (foo[bar]=baz).

URLEncodedFormEncoder. ArrayEncoding enumeration offers the following method to encode the array parameters:

  • .brackets– Appends a set of empty brackets after the key for each value. This is the default.
  • .noBrackets– No parentheses attached. Key is encoded as-is.

By default, Alamofire uses.brackets encoding where foo = [1, 2] is encoded as foo[]=1&foo[]=2.

With.nobrackets encoding, foo= [1, 2] is encoded as foo=1&foo=2.

You can create your own URLEncodedFormParameterEncoder, during initialization, can set the value of arrayEncoding in URLEncodedFormEncoder parameters:

let encoder = URLEncodedFormParameterEncoder(encoder: URLEncodedFormEncoder(arrayEncoding: .noBrackets))
Copy the code
configurationBoolParameter encoding

URLEncodedFormEncoder. BoolEncoding enumeration offers the following method for encoding Bool parameter:

  • .numeric– thetrueEncoded as1.falseEncoded as0. This is the default.
  • .literal– thetruefalseEncoded as string text.

By default, Alamofire uses.numeric.

You can create your own URLEncodedFormParameterEncoder, during initialization, can set the value of boolEncoding in URLEncodedFormEncoder parameters:

let encoder = URLEncodedFormParameterEncoder(encoder: URLEncodedFormEncoder(boolEncoding: .numeric))
Copy the code
configurationDataParameter encoding

DataEncoding includes the following methods for encoding Data parameters:

  • .deferredToData– useDataThe built-inEncodableSupport.
  • .base64DataA string encoded as base64. This is the default.
  • .custom((Data) -> throws -> String)– Uses the given closure pairDataEncode.

You can create your own URLEncodedFormParameterEncoder, during initialization, can set the value of dataEncoding in URLEncodedFormEncoder parameters:

let encoder = URLEncodedFormParameterEncoder(encoder: URLEncodedFormEncoder(dataEncoding: .base64))
Copy the code
configurationDateParameter encoding

Since there are many ways to encode Date as a String, DateEncoding includes the following methods for encoding Date arguments:

  • .deferredToDate– useDateThe built-inEncodableSupport. This is the default.
  • .secondsSince1970DateThe number of seconds encoded as zero hour UTC, January 1, 1970.
  • .millisecondsSince1970DateThe number of milliseconds encoded as zero point UTC, January 1, 1970.
  • .iso8601– according to ISO 8601 and RFC3339 standardsDateEncode.
  • .formatted(DateFormatter)– Use the givenDateFormatterDateEncode.
  • .custom((Date) throws -> String)– Uses the given closure pairDateEncode.

You can create your own URLEncodedFormParameterEncoder, during initialization, can set the value of dateEncoding in URLEncodedFormEncoder parameters:

let encoder = URLEncodedFormParameterEncoder(encoder: URLEncodedFormEncoder(dateEncoding: .iso8601))
Copy the code
Configure the Coding of Coding Keys

Due to the variety of key argument styles, KeyEncoding provides the following methods to customize key encoding from lowerCamelCase:

  • .useDefaultKeys– Use the key specified by each type. This is the default.
  • .convertToSnakeCase-Convert key to Snake case:oneTwoThreebecomeone_two_three.
  • .convertToKebabCase-Convert key to keBAB case:oneTwoThreebecomeone-two-three.
  • .capitalized– Capitalize the first letter, for exampleoneTwoThreeintoOneTwoThree.
  • .uppercased– All letters in uppercase:oneTwoThreebecomeONETWOTHREE.
  • .lowercased– All lowercase letters:oneTwoThreebecomeonetwothree.
  • .custom((String) -> String)– Encodes the key using the given closure.

You can create your own URLEncodedFormParameterEncoder, during initialization, can set the value of keyEncoding in URLEncodedFormEncoder parameters:

let encoder = URLEncodedFormParameterEncoder(encoder: URLEncodedFormEncoder(keyEncoding: .convertToSnakeCase))
Copy the code
Configures the encoding of space

Older form encoders used + to encode Spaces, and some servers still want to use this encoding instead of the modern percentage encoding, so Alamofire contains the following method for encoding Spaces:

.percentEscaped – Encodes space characters by applying standard percentage escapes. “Code is “%20”. This is the default. .pluspress – Replaces space characters with + and encodes “+”.

You can create your own URLEncodedFormParameterEncoder, during initialization, can set the value of spaceEncoding in URLEncodedFormEncoder parameters:

let encoder = URLEncodedFormParameterEncoder(encoder: URLEncodedFormEncoder(spaceEncoding: .plusReplaced))
Copy the code

JSONParameterEncoder

JSONParameterEncoder uses Swift’s JSONEncoder to encode Encodable values and set the result to the httpBody of URLRequest. If the Content-Type is not already set, it is set to Application /json.

POST requests for JSON-encoded parameters
let parameters: [String: [String]] = [
    "foo": ["bar"]."baz": ["a"."b"]."qux": ["x"."y"."z"]]AF.request("https://httpbin.org/post", method: .post, parameters: parameters, encoder: JSONParameterEncoder.default)
AF.request("https://httpbin.org/post", method: .post, parameters: parameters, encoder: JSONParameterEncoder.prettyPrinted)
AF.request("https://httpbin.org/post", method: .post, parameters: parameters, encoder: JSONParameterEncoder.sortedKeys)

// HTTP body: {"baz":["a","b"],"foo":["bar"],"qux":["x","y","z"]}
Copy the code
The customJSONEncoder

You can customize the behavior of JSONParameterEncoder by passing a custom JSONEncoder instance to it:

let encoder = JSONEncoder()
encoder.dateEncoding = .iso8601
encoder.keyEncodingStrategy = .convertToSnakeCase
let parameterEncoder = JSONParameterEncoder(encoder: encoder)
Copy the code
The manual forURLRequestParameter coding

The ParameterEncoder APIs can also be used outside of Alamofire by encoding parameters directly in the URLRequest.

let url = URL(string: "https://httpbin.org/get")!
var urlRequest = URLRequest(url: url)

let parameters = ["foo": "bar"]
let encodedURLRequest = try URLEncodedFormParameterEncoder.default.encode(parameters, into: urlRequest)
Copy the code

HTTP Headers

Alamofire contains its own HTTPHeaders type, which is a representation of a sequential and case-insensitive HTTPHeader name/value pair. The HTTPHeader type encapsulates a single name/value pair and provides various static values for the commonly used HEADERS.

Adding custom HTTPHeaders to Request is as simple as passing a value to the Request method:

let headers: HTTPHeaders = [
    "Authorization": "Basic VXNlcm5hbWU6UGFzc3dvcmQ="."Accept": "application/json"
]

AF.request("https://httpbin.org/headers", headers: headers).responseJSON { response in
    debugPrint(response)
}
Copy the code

HTTPHeaders can also be constructed from an HTTPHeader array:

let headers: HTTPHeaders = [
    .authorization(username: "Username", password: "Password"),
    .accept("application/json")]AF.request("https://httpbin.org/headers", headers: headers).responseJSON { response in
    debugPrint(response)
}
Copy the code

For HTTP Headers that do not change, it is recommended to set them on the URLSessionConfiguration so that they are automatically applied to any URLSessionTask created by the underlying URLSession.

The default Alamofire Session provides a default set of Headers for each Request. These include:

  • Accept-EncodingBy default,br; Q = 1.0, gzip; Q = 0.8, deflate; Q = 0.6, according to theRFC 7230 § holdings.
  • Accept-Language, the default value is a maximum of six preferred languages in the systemen; Q = 1.0, according to theRFC 7231 § 5.3.5.
  • User-AgentContains version information about the current application. Such as:IOS Example / 1.0 (com) alamofire) iOS - Example; build:1; IOS Alamofire / 5.0.0 13.0.0), according to theRFC 7231 § 5.5.3.

If you need to customize these Headers, you should create a custom URLSessionConfiguration, update the defaultHTTPHeaders attribute, and apply the configuration to the new Session instance. Using URLSessionConfiguration. Af. Default to customize configuration, will retain Alamofire default headers.

In response to verify

By default, Alamofire treats any completed request as a success, regardless of the content of the response. If the response has an unacceptable status code or MIME type, calling validate() before the response handler causes an error to be generated.

Automatic validation

The validate() API automatically verifies whether the status code is in 200.. <300, and whether the content-Type header of the response matches the Accept of the request, if provided.

AF.request("https://httpbin.org/get").validate().responseJSON { response in
    debugPrint(response)
}
Copy the code

Manual verification

AF.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 let .failure(error):
            print(error)
        }
    }
Copy the code

Response processing

Alamofire’s DataRequest and DownloadRequest have corresponding response types: DataResponse

and DownloadResponse

. Both types consist of two generics: serialization type and error type. By default, all response values will generate an AFError error type (DataResponse

). Alamofire uses the simpler AFDataResponse

and AFDownloadResponse

in its public API, which always have the AFError error type. UploadRequest is a subclass of DataRequest and uses the same DataResponse type.


,>
,>
,>

The DataResponse that processes a DataRequest or UploadRequest issued in Alamofire involves linking the response handler, such as responseJSON linking to the DataRequest:

AF.request("https://httpbin.org/get").responseJSON { response in
    debugPrint(response)
}
Copy the code

In the example above, the responseJSON handler is added to the DataRequest for execution after the DataRequest completes. The argument passed to the Handler closure is the AFDataResponse

value generated by the JSONResponseSerializer from the response property.

This closure does not block execution waiting for a response from the server, but is added as a callback to process the response once it is received. The result of the request is only available within the scope of the response closure. Any execution that depends on the response or data received from the server must be done in the response closure.

Alamofire’s network requests are completed asynchronously. Asynchronous programming can be frustrating for programmers unfamiliar with the concept, but there are good reasons to do so.

By default, Alamofire contains six different data response handlers, including:


// Response Handler - Unserialized Response
func response(
    queue: DispatchQueue = .main,
    completionHandler: @escaping (AFDataResponse<Data? - > >)Void
) -> Self

// Response Serializer Handler - Serialize using the passed Serializer
func response<Serializer: DataResponseSerializerProtocol> (queue: DispatchQueue = .main,
    responseSerializer: Serializer.completionHandler: @escaping (AFDataResponse<Serializer.SerializedObject- > >)Void
) -> Self

// Response Data Handler - Serialized into Data
func responseData(
    queue: DispatchQueue = .main,
    completionHandler: @escaping (AFDataResponse<Data- > >)Void
) -> Self

// Response String Handler - Serialized into String
func responseString(
    queue: DispatchQueue = .main,
    encoding: String.Encoding? = nil.completionHandler: @escaping (AFDataResponse<String- > >)Void
) -> Self

// Response JSON Handler - Serialized into Any Using JSONSerialization
func responseJSON(
    queue: DispatchQueue = .main,
    options: JSONSerialization.ReadingOptions = .allowFragments,
    completionHandler: @escaping (AFDataResponse<Any- > >)Void
) -> Self

// Response Decodable Handler - Serialized into Decodable Type
func responseDecodable<T: Decodable> (of type: T.Type = T.self.queue: DispatchQueue = .main,
    decoder: DataDecoder = JSONDecoder(),
    completionHandler: @escaping (AFDataResponse<T- > >)Void
) -> Self
Copy the code

None of the response Handlers perform any validation of the HTTPURLResponse returned from the server.

For example, 400.. < 500 and 500.. Response status codes in the <600 range do not automatically trigger an error. Alamofire uses response validation links to do this.

The response Handler

The response handler does not evaluate any response data. It simply forwards all information directly from the URLSessionDelegate. It is equivalent to using cURL to perform the request.

AF.request("https://httpbin.org/get").response { response in
    debugPrint("Response: \(response)")}Copy the code

We strongly recommend that you utilize the Response and Result types to leverage other Response serializers.

In response to the Data Handler

The responseData Handler uses the DataResponseSerializer to extract and validate the data returned by the server. If no error occurs and Data is returned, the response result will be.success and value will be the Data returned from the server.

AF.request("https://httpbin.org/get").responseData { response in
    debugPrint("Response: \(response)")}Copy the code

The response String Handler

The responseString Handler uses the StringResponseSerializer 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 to String, the response result will be.success and the value will be of type String.

AF.request("https://httpbin.org/get").responseString { response in
    debugPrint("Response: \(response)")}Copy the code

If no encoding is specified, Alamofire uses the text encoding specified in the server HTTPURLResponse. If the server response cannot determine the text encoding, the default is.isolatin1.

The JSON response Handler

ResponseJSON handler using JSONResponseSerializer using the specified JSONSerialization. ReadingOptions converts data from server to Any type. If no errors occur and the server data is successfully serialized into a JSON object, the response AFResult will be.success and the value will be of type Any.

AF.request("https://httpbin.org/get").responseJSON { response in
    debugPrint("Response: \(response)")}Copy the code

The responseDecodable Handler

ResponseDecodable handler using DecodableResponseSerializer and designated DataDecoder (Decoder protocol abstraction, Can be decoded from Data) converts the Data returned by the server to the Decodable type passed in. If no errors occur and the server data is successfully decoded to type Decodable, the response Result will be.success and value will be the type passed in.

struct HTTPBinResponse: Decodable {
    let url: String
}

AF.request("https://httpbin.org/get").responseDecodable(of: HTTPBinResponse.self) { response in
    debugPrint("Response: \(response)")}Copy the code

Chain response handlers

Handlers can also be connected to:

Alamofire.request("https://httpbin.org/get")
    .responseString { response in
        print("Response String: \(response.value)")
    }
    .responseJSON { response in
        print("Response JSON: \(response.value)")}Copy the code

Note that using multiple response Handlers for the same request requires serializing the server data multiple times, once for each response Handler. As a best practice, you should generally avoid using multiple response Handlers for the same request, especially in a production environment. They can only be used for debugging or when no better option is available.

Response Handler queue

By default, closures passed to the response handler are executed on the.main queue, but a specified DispatchQueue can be passed to execute the closure. The actual serialization work (converting Data to other types) is always performed on background queues.

let utilityQueue = DispatchQueue.global(qos: .utility)

AF.request("https://httpbin.org/get").responseJSON(queue: utilityQueue) { response in
    print("Executed on utility queue.")
    debugPrint(response)
}
Copy the code

In response to the cache

The response cache is handled using the system’s own URLCache. It provides a composite cache in memory and on disk, and allows you to manage the size of memory and disk used for caching.

By default, Alamofire uses the urlCache.shared instance. To customize URLCache instances to use, see Advanced Usage.

The authentication

The authentication system used to own URLCredential and URLAuthenticationChallenge processing.

These authentication APIs are used to prompt authorized servers, not the header APIs typically used for requiring authentication or equivalent.

Supported authentication schemes:

  • HTTP Basic
  • HTTP Digest
  • Kerberos
  • NTLM

HTTP Basic authentication

URLAuthenticationChallenge Request authenticate method will be used for questioning and automatically provide URLCredential (if applicable) :

let user = "user"
let password = "password"

AF.request("https://httpbin.org/basic-auth/\(user)/\(password)")
    .authenticate(username: user, password: password)
    .responseJSON { response in
        debugPrint(response)
    }
Copy the code

useURLCredentialTo verify

let user = "user"
let password = "password"

let credential = URLCredential(user: user, password: password, persistence: .forSession)

AF.request("https://httpbin.org/basic-auth/\(user)/\(password)")
    .authenticate(with: credential)
    .responseJSON { response in
        debugPrint(response)
    }
Copy the code

Note that when using URLCredential for authentication, if the server issues a challenge, the underlying URLSession will actually issue two requests. The first request will not include a credential that “might” trigger a server challenge. Alamofire then receives the challenge, appends the credential, and the underlying URLSession retries the request.

Manual verification

If you are communicating with apis that always require Authenticate or similar headers without prompting, you can manually add:

let user = "user"
let password = "password"

let headers: HTTPHeaders = [.authorization(username: user, password: password)]

AF.request("https://httpbin.org/basic-auth/user/password", headers: headers)
    .responseJSON { response in
        debugPrint(response)
    }
Copy the code

However, headers that must be part of all requests are usually better handled as part of a custom URLSessionConfiguration or by using a RequestAdapter.

Download data to a file

In addition to extracting data into memory, Alamofire also provides session. download, DownloadRequest, and DownloadResponse

to make it easy to download data to disk. While downloading into memory is useful for small loads (such as most JSON API responses), retrieving larger resources (such as images and videos) should be downloaded to disk to avoid memory problems in your application.
,>

AF.download("https://httpbin.org/image/png").responseData { response in
    if let data = response.value {
        let image = UIImage(data: data)
    }
}
Copy the code

DownloadRequest has most of the same response handlers as DataRequest. However, because it downloads data to disk, the serialized response involves reading from disk and possibly reading large amounts of data into memory. It is important to keep these facts in mind when designing download handling.

The location of the downloaded file

All downloaded data is initially stored in the system’s temporary directory. It will eventually be deleted by the system at some point in the future, so it’s important to move the file somewhere else if it needs longer life.

You can provide a Destination closure to move a file from a temporary directory to its final location. The Options specified in the closure are executed before the temporary file is actually moved to the destinationURL. The two Options currently supported are:

  • .createIntermediateDirectories– If specified, an intermediate directory is created for the destination URL.
  • .removePreviousFile– If specified, deletes the previous file from the destination URL.
let destination: DownloadRequest.Destination = { _._ in
    let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
    let fileURL = documentsURL.appendingPathComponent("image.png")

    return (fileURL, [.removePreviousFile, .createIntermediateDirectories])
}

AF.download("https://httpbin.org/image/png", to: destination).response { response in
    debugPrint(response)

    if response.error = = nil.let imagePath = response.fileURL?.path {
        let image = UIImage(contentsOfFile: imagePath)
    }
}
Copy the code

You can also use the suggested Destination API:

let destination = DownloadRequest.suggestedDownloadDestination(for: .documentDirectory)

AF.download("https://httpbin.org/image/png", to: destination)
Copy the code

Download progress

Many times it is helpful to report the download progress to the user. Any DownloadRequest can use downloadProgress to report downloadProgress.

AF.download("https://httpbin.org/image/png")
    .downloadProgress { progress in
        print("Download Progress: \(progress.fractionCompleted)")
    }
    .responseData { response in
        if let data = response.value {
            let image = UIImage(data: data)
        }
    }
Copy the code

The URLSession progress reporting APIs (also of Alamofire) will only work if the server correctly returns the Content-Length header that can be used to calculate progress. Without this header, progress will remain at 0.0 until the download is complete, at which point progress will jump to 1.0.

The downloadProgress API can also accept a queue parameter that defines which DispatchQueue to call the downloadProgress closure on.

let utilityQueue = DispatchQueue.global(qos: .utility)

AF.download("https://httpbin.org/image/png")
    .downloadProgress(queue: utilityQueue) { progress in
        print("Download Progress: \(progress.fractionCompleted)")
    }
    .responseData { response in
        if let data = response.value {
            let image = UIImage(data: data)
        }
    }
Copy the code

Cancel and resume downloads

In addition to having the Cancel () API for all request classes, DownloadRequest can also generate recovery data that can be used to resume downloads later. This API comes in two forms: 1) Cancel (producingResumeData: Bool), which allows control over whether recovery data is generated, but is only available in DownloadResponse; 2) Cancel (byProducingResumeData: (_ resumeData: Data?) -> Void), which performs the same operation, but the recovery data is available in the Completion Handler.

If the DownloadRequest is cancelled or interrupted, the underlying URLSessionDownloadTask may generate recovery data. If this happens, you can reuse the recovered data to restart the stopped DownloadRequest.

Important note: On some versions of all Apple platforms (iOS 10-10.2, macOS 10.12-10.12.2, tvOS 10-10.1, watchOS 3-3.1.1), ResumeData was corrupted on background URLSessionConfiguration. There is a potential error in the resumeData generation logic, namely a data write error, and the download cannot always be recovered. For more information about this error and possible workarounds, see this Stack Overflow post.

var resumeData: Data!

let download = AF.download("https://httpbin.org/image/png").responseData { response in
    if let data = response.value {
        let image = UIImage(data: data)
    }
}

// download.cancel(producingResumeData: true) // Makes resumeData available in response only.
download.cancel { data in
    resumeData = data
}

AF.download(resumingWith: resumeData).responseData { response in
    if let data = response.value {
        let image = UIImage(data: data)
    }
}
Copy the code

Upload data to the server

Request () is usually sufficient when sending a relatively small amount of data to the server using JSON or URL-encoded parameters. If you need to send a lot of Data from memory, file urls, or Data in InputStream, then an upload() is what you want to use.

Upload the Data

let data = Data("data".utf8)

AF.upload(data, to: "https://httpbin.org/post").responseJSON { response in
    debugPrint(response)
}
Copy the code

Upload a file

let fileURL = Bundle.main.url(forResource: "video", withExtension: "mov")

AF.upload(fileURL, to: "https://httpbin.org/post").responseJSON { response in
    debugPrint(response)
}
Copy the code

Upload multiple form data

AF.upload(multipartFormData: { multipartFormData in
    multipartFormData.append(Data("one".utf8), withName: "one")
    multipartFormData.append(Data("two".utf8), withName: "two")
}, to: "https://httpbin.org/post")
    .responseJSON { response in
        debugPrint(response)
    }
Copy the code

Upload progress

While the user is waiting for the upload to complete, it can sometimes be convenient to show the user the progress of the upload. Any UploadRequest can use uploadProgress and downloadProgress to report the upload and downloadProgress of the response data download.

let fileURL = Bundle.main.url(forResource: "video", withExtension: "mov")

AF.upload(fileURL, to: "https://httpbin.org/post")
    .uploadProgress { progress in
        print("Upload Progress: \(progress.fractionCompleted)")
    }
    .downloadProgress { progress in
        print("Download Progress: \(progress.fractionCompleted)")
    }
    .responseJSON { response in
        debugPrint(response)
    }
Copy the code

Statistical indicators

URLSessionTaskMetrics

Alamofire collects URLSessionTaskMetrics for each Request. URLSessionTaskMetrics encapsulates some fantastic statistics about the underlying network connection, request, and response times.

AF.request("https://httpbin.org/get").responseJSON { response in
    print(response.metrics)
}
Copy the code

The cURL command output is displayed

Debugging platform issues can be frustrating. Fortunately, Alamofire’s Request type generates the equivalent cURL command for debugging purposes. Due to the asynchronous nature of the Alamofire Request creation, there are both synchronous and asynchronous versions of the API. To get the cURL command as quickly as possible, link the cURL description to the request:

AF.request("https://httpbin.org/get")
    .cURLDescription { description in
        print(description)
    }
    .responseJSON { response in
        debugPrint(response.metrics)
    }
Copy the code

This will produce:

$ curl -v \ -X GET \ -H "Accept-Language: en; Q =1.0" \ -h "accept-encoding: br; Q = 1.0, gzip; Q = 0.9, deflate; Q =0.8" \ -h "user-agent: Demo/1.0 (com.demo.demo; build:1; IOS 13.0.0) Alamofire/1.0" \ "https://httpbin.org/get"Copy the code