korgx9
korgx9

Reputation: 1264

Alamofire 3 Custom Encoding To Alamofire 4 Custom Encoding

I have method writing in Alamofire 3 with customParameterEncoding. This custom encoding just replaces "[]=" with "=" in queryString and returns it.

Alamofire.request(.GET, SearchURL, parameters: params, encoding: customEncoding, headers: headers).validate().responseJSON {
            response in
            switch response.result {
            case .success:
                print("success")
                break
            case .failure(let error):
                print("Error: " + error.localizedDescription)
                break
            }
        }

and custom encoding parameter

let customEncoding =  ParameterEncoding.Custom { requestConvertible, parameters in
    let (mutableRequest, error) = ParameterEncoding.URL.encode(requestConvertible, parameters: parameters)
    mutableRequest.URL = NSURL(string: mutableRequest.URLString.stringByReplacingOccurrencesOfString("%5B%5D=", withString: "="))
    return (mutableRequest, error)
}

How to convert customEncoding to Alamofire 4 version?

Upvotes: 7

Views: 4603

Answers (4)

Mahmoud Alaa
Mahmoud Alaa

Reputation: 3

Try Use URLEncoding(arrayEncoding: .noBrackets)

Alamofire.request(.GET, SearchURL, parameters: params, encoding: URLEncoding(arrayEncoding: .noBrackets), headers: headers).validate().responseJSON {
        response in
        switch response.result {
        case .success:
            print("success")
            break
        case .failure(let error):
            print("Error: " + error.localizedDescription)
            break
        }
    }

Upvotes: 0

Hiroki Matsue
Hiroki Matsue

Reputation: 111

Just separating structs for GET and POST also works.

http://matsue.github.io/post/how-to-remove-square-brackets-with-alamofire/

Alamofire 4 with Swift 3

// Remove square brackets for GET request
struct CustomGetEncoding: ParameterEncoding {
    func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest {
        var request = try URLEncoding().encode(urlRequest, with: parameters)
        request.url = URL(string: request.url!.absoluteString.replacingOccurrences(of: "%5B%5D=", with: "="))
        return request
    }
}

// Remove square brackets for POST request
struct CustomPostEncoding: ParameterEncoding {
    func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest {
        var request = try URLEncoding().encode(urlRequest, with: parameters)
        let httpBody = NSString(data: request.httpBody!, encoding: String.Encoding.utf8.rawValue)!
        request.httpBody = httpBody.replacingOccurrences(of: "%5B%5D=", with: "=").data(using: .utf8)
        return request
    }
}

// Use structs for requests 
Alamofire.request("http://example.com", method: .get, parameters: ["foo": ["bar1", "bar2"]], encoding: CustomGetEncoding())
Alamofire.request("http://example.com", method: .post, parameters: ["foo": ["bar1", "bar2"]], encoding: CustomPostEncoding())

Upvotes: 11

Hongli Yu
Hongli Yu

Reputation: 478

Looks like a bug by design in Alamofire: https://github.com/Alamofire/Alamofire/issues/329

This is my solution in Swift3 with both get & post method:

extension NSNumber {
fileprivate var isBool: Bool {
    return CFBooleanGetTypeID() == CFGetTypeID(self) 
  }
}

struct CustomEncoding: ParameterEncoding {
fileprivate func escape(_ string: String) -> String {
    let generalDelimitersToEncode = ":#[]@" // does not include "?" or "/" due to RFC 3986 - Section 3.4
    let subDelimitersToEncode = "!$&'()*+,;="

    var allowedCharacterSet = CharacterSet.urlQueryAllowed
    allowedCharacterSet.remove(charactersIn: "\(generalDelimitersToEncode)\(subDelimitersToEncode)")

    var escaped = ""
    if #available(iOS 8.3, *) {
        escaped = string.addingPercentEncoding(withAllowedCharacters: allowedCharacterSet) ?? string
    } else {
        let batchSize = 50
        var index = string.startIndex

        while index != string.endIndex {
            let startIndex = index
            let endIndex = string.index(index, offsetBy: batchSize, limitedBy: string.endIndex) ?? string.endIndex
            let range = startIndex..<endIndex

            let substring = string.substring(with: range)

            escaped += substring.addingPercentEncoding(withAllowedCharacters: allowedCharacterSet) ?? substring

            index = endIndex
        }
    }
    return escaped
}

fileprivate func queryComponents(fromKey key: String, value: Any) -> [(String, String)] {
    var components: [(String, String)] = []

    if let dictionary = value as? [String: Any] {
        for (nestedKey, value) in dictionary {
            components += queryComponents(fromKey: "\(key)[\(nestedKey)]", value: value)
        }
    } else if let array = value as? [Any] {
        for value in array {
            components += queryComponents(fromKey: "\(key)[]", value: value)
        }
    } else if let value = value as? NSNumber {
        if value.isBool {
            components.append((escape(key), escape((value.boolValue ? "1" : "0"))))
        } else {
            components.append((escape(key), escape("\(value)")))
        }
    } else if let bool = value as? Bool {
        components.append((escape(key), escape((bool ? "1" : "0"))))
    } else {
        components.append((escape(key), escape("\(value)")))
    }

    return components
}

fileprivate func query(_ parameters: [String: Any]) -> String {
    var components: [(String, String)] = []
    for key in parameters.keys.sorted(by: <) {
        let value = parameters[key]!
        components += queryComponents(fromKey: key, value: value)
    }
    return components.map { "\($0)=\($1)" }.joined(separator: "&")
}

func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest {
    var request: URLRequest = try urlRequest.asURLRequest()
    guard let parameters = parameters else { return request }
    guard let mutableRequest = (request as NSURLRequest).mutableCopy() as? NSMutableURLRequest else {
        // Handle the error
        return request
    }
    if request.urlRequest?.httpMethod == "GET" {
        mutableRequest.url = URL(string: (mutableRequest.url?.absoluteString.replacingOccurrences(of: "%5B%5D=", with: "="))!)
    }

    if request.urlRequest?.httpMethod == "POST" {
        mutableRequest.httpBody = query(parameters).data(using: .utf8, allowLossyConversion: false)
        if mutableRequest.httpBody != nil {
            let httpBody = NSString(data: mutableRequest.httpBody!, encoding: String.Encoding.utf8.rawValue)!
            mutableRequest.httpBody = httpBody.replacingOccurrences(of: "%5B%5D=", with: "=").data(using: String.Encoding.utf8)
        }
    }
    request = mutableRequest as URLRequest
    return request
  }
}

then send request:

let request = Alamofire.request(URLString, method: method, parameters: parameters, encoding: CustomEncoding(), headers: headers)
            request.responseData(queue: self.netWorkQueue) { (response) in
         //......//
}

Upvotes: 0

A.J.
A.J.

Reputation: 446

In Alamofire 4.0 you should use ParameterEncoding.

struct CustomEncoding: ParameterEncoding {
    func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest {
        var request = try! URLEncoding().encode(urlRequest, with: parameters)
        let urlString = request.url?.absoluteString.replacingOccurrences(of: "%5B%5D=", with: "=")
        request.url = URL(string: urlString!)
        return request
    }
}

Upvotes: 9

Related Questions