Alamofire introduction notes

Posted by rcmehta_14 on Mon, 06 Dec 2021 23:30:30 +0100

Newcomers began to learn IOS development. Today, they read the basic commands of alamofire and referred to the official website. To sum up, the official documents are as follows. You can use cocapods to import the Alamo fire Library:

https://github.com/Alamofire/Alamofire/blob/master/Documentation/AdvancedUsage.md

  1. Send request:
AF.request("<url>").reponse { ... }

Complete definition of this method:

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

The example in the first line actually uses this simple definition:

open func request(_urlRequest: URLRequestConvertible, 
                  interceptor: RequestInterceptor?=nil) ->DataRequest
  1. You can use the Modifier to customize the request
AF.request("https://httpbin.org/get", requestModifier: { $0.timeoutInterval = 5 }).response(...)

The request is written in the closure expression.

AF.request("https://httpbin.org/get") { urlRequest in
urlRequest.timeoutInterval=5
urlRequest.allowsConstrainedNetworkAccess=false}
.response(...)
  1. Add parameters to the request
    The parameter must be of type encodeable and meet the ParameterEncoder protocol. The following example is an example of creating a Login request:
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)
}

There are two types of encoders, one is JSON format JSONParameterEncoder, and the other is urlencodedformulalparameterencoder.

  1. Send request in URL encoded format
    The API parameters for sending the request through the URL are different. The parameters include: (request path, request parameters (optional), request compiler (. methodDependent/.queryString/.httpBody)). The compiler determines according to the Http type of the request.

For example, POST request:
let parameters: [String: [String]] = [
"foo": ["bar"],
"baz": ["a", "b"],
"qux": ["x", "y", "z"]
]

// All three of these calls 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"

  1. URLEncodedFormEncoder
    Starting from Swift4.x, the hash algorithm for the dictionary will cause internal sorting confusion when the Collection type is passed in. You can use URLEncodedFormEncoder to sort the key value pairs to be encoded.
let encoder = URLEncodedFormParameterEncoder(encoder: URLEncodedFormEncoder(alphabetizeKeyValuePairs: false))

The parameters of Format are encoded according to different types, and the corresponding methods of each parameter are also different. For details, please refer to the official document:
https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#manual-authentication

  1. Send the request through JSON encoding
    Sending requests through JSON doesn't seem to be as cumbersome as URL s. There are three types of encoders, all of which can know the meaning according to the name. Examples are as follows:
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"]}

You can customize the JSON encoder behavior through the wearing JSONEncoder instance:

let encoder = JSONEncoder()
encoder.dateEncoding = .iso8601
encoder.keyEncodingStrategy = .convertToSnakeCase
let parameterEncoder = JSONParameterEncoder(encoder: encoder)

You can also manually create parameters externally:

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)
  1. HttpHeader:
    There are two ways to create an HttpHeader:
    One is similar to manually filling in key value pairs in the Header of POSTMAN:
let headers: HTTPHeaders = [
    "Authorization": "Basic VXNlcm5hbWU6UGFzc3dvcmQ=",
    "Accept": "application/json"
]

AF.request("https://httpbin.org/headers", headers: headers).responseDecodable(of: DecodableType.self) { response in
    debugPrint(response)
}

The other is similar to filling in username and password in POSTMAN:

let headers: HTTPHeaders = [
    .authorization(username: "Username", password: "Password"),
    .accept("application/json")
]

AF.request("https://httpbin.org/headers", headers: headers).responseDecodable(of: DecodableType.self) { response in
    debugPrint(response)
}
  1. Response validation
    Automatic verification (automatic verification will require the returned Http status code to be between 200-300):
AF.request("https://httpbin.org/get").validate().responseData { response in
    debugPrint(response)
}

Manually verify and add an expression to the parameter:

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)
        }
    }
  1. Response processing
    alamofire has five types of responders:
// 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,
                  dataPreprocessor: DataPreprocessor = DataResponseSerializer.defaultDataPreprocessor,
                  emptyResponseCodes: Set<Int> = DataResponseSerializer.defaultEmptyResponseCodes,
                  emptyRequestMethods: Set<HTTPMethod> = DataResponseSerializer.defaultEmptyRequestMethods,
                  completionHandler: @escaping (AFDataResponse<Data>) -> Void) -> Self

// Response String Handler - Serialized into String
func responseString(queue: DispatchQueue = .main,
                    dataPreprocessor: DataPreprocessor = StringResponseSerializer.defaultDataPreprocessor,
                    encoding: String.Encoding? = nil,
                    emptyResponseCodes: Set<Int> = StringResponseSerializer.defaultEmptyResponseCodes,
                    emptyRequestMethods: Set<HTTPMethod> = StringResponseSerializer.defaultEmptyRequestMethods,
                    completionHandler: @escaping (AFDataResponse<String>) -> Void) -> Self

// Response Decodable Handler - Serialized into Decodable Type
func responseDecodable<T: Decodable>(of type: T.Type = T.self,
                                     queue: DispatchQueue = .main,
                                     dataPreprocessor: DataPreprocessor = DecodableResponseSerializer<T>.defaultDataPreprocessor,
                                     decoder: DataDecoder = JSONDecoder(),
                                     emptyResponseCodes: Set<Int> = DecodableResponseSerializer<T>.defaultEmptyResponseCodes,
                                     emptyRequestMethods: Set<HTTPMethod> = DecodableResponseSerializer<T>.defaultEmptyRequestMethods,
                                     completionHandler: @escaping (AFDataResponse<T>) -> Void) -> Self

1) Response Handler - does not do any processing, only returns according to the url:

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

2) Response Data Handler - extract the data of the response by serialization:

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

3) Response String Handler - String to retrieve the response by serialization:

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

4) Response Decodable Handler - converts Data to a DataDecoder by serialization (the type is defined by itself),

struct DecodableType: Decodable { let url: String }

AF.request("https://httpbin.org/get").responseDecodable(of: DecodableType.self) { response in
    debugPrint("Response: \(response)")
}
  1. authentication
    Several ways of certification:

1) Available directly at url:

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

AF.request("https://httpbin.org/basic-auth/\(user)/\(password)")
    .authenticate(username: user, password: password)
    .responseDecodable(of: DecodableType.self) { response in
        debugPrint(response)
    }

2) For the above variant, instantiate the URLCredential object:

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)
    .responseDecodable(of: DecodableType.self) { response in
        debugPrint(response)
    }

3) Manual authentication, improve reuse and simplify code (there is no need to call the authenticate API, which is annoying):

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)
    .responseDecodable(of: DecodableType.self) { response in
        debugPrint(response)
    }
  1. Download file
    Alamo fire supports downloading to memory or hard disk. The downloaded files will be placed in a temporary folder and need to be transferred in time.

1) Basic examples:

AF.download("https://httpbin.org/image/png").responseURL { response in
    // Read file from provided file URL.
}

2) Download to destination file:
As mentioned earlier, all file downloads will be placed in a temporary area, and a destination needs to be made. The destination is provided, and the closure operation will be performed before the file is moved to the destination. Two types can be specified:
. createIntermediateDirectories: if specified, creates an intermediate directory for the target 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)
    }
}

More simply, you can directly use the API to complete the download operation:

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

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

3) Report download progress:
Using the downloadProgress API, you can report the download progress to the user.

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)
        }
    }

4) Cancel and resume Download:
Canceling the download provides two API s:
Cancel (productingresumedata: bool): allows you to control whether to resume downloading. It can only be used on DownloadResponse
Cancel (byproducingresumedata: (resumedata: data?) - > void): the download can be resumed, but it can only be used in the completed program

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)
    }
}
  1. Upload file
    If there is only a small amount of data (such as sending a data object), it is sufficient to use request. But if you want to send pictures and so on, you can use upload ()
    Here are three examples of uploading file s:
    1)
let data = Data("data".utf8)

AF.upload(data, to: "https://httpbin.org/post").responseDecodable(of: DecodableType.self) { response in
    debugPrint(response)
}

2)

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

AF.upload(fileURL, to: "https://httpbin.org/post").responseDecodable(of: DecodableType.self) { response in
    debugPrint(response)
}

3)

AF.upload(multipartFormData: { multipartFormData in
    multipartFormData.append(Data("one".utf8), withName: "one")
    multipartFormData.append(Data("two".utf8), withName: "two")
}, to: "https://httpbin.org/post")
    .responseDecodable(of: DecodableType.self) { response in
        debugPrint(response)
    }
  1. Show upload progress
    Like downloading, uploading can also show the progress of uploading. Use the uploadProgress API:
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)")
    }
    .responseDecodable(of: DecodableType.self) { response in
        debugPrint(response)
    }

Topics: Swift iOS