5
votes

I'm using Moya Swift framework for networking layer which is constructed on top of Alamofire.

Currently, I'm trying to send request with URL encoded parameters that have same keys.

i.e. http://some-site/request?param=v1&param=v2&param=v3

I've already tried to group these params into Set or NSSet or Array like this but nothing helps to achieve desired result.

["param": ["v1", "v2", "v3"]];

["param": Set(arrayLiteral: "v1", "v2", "v3")]

Any help would be appreciated either with Moya or with Alamofire itself.

Edit: Here is some sample code to give basic idea:

Api Router setup

import Moya

// MARK:- Enum Declaration

enum ApiRouter {
    case XAuth(login: String, password: String)
    case SomeRequest(params: [String])
}

// MARK:- Moya Path

extension ApiRouter: MoyaPath {
    var path: String {
        switch self {
        case .XAuth:
            return "/authorization"
        case .SomeRequest:
            return "/some-request"
        }
    }
}

// MARK:- Moya Target

extension ApiRouter: MoyaTarget {
    private var base: String {
        return "http://some-site"
    }
    var baseURL: NSURL {
        return NSURL(string: base)!
    }

    var parameters: [String: AnyObject] {
        switch self {
        case .XAuth(let login, let password):
            return [
                "email": login,
                "password": password
            ]
        case .SomeRequest(let params):
            return [
                "params": params
            ]
    }

    var method: Moya.Method {
        switch self {
        case .XAuth:
            return .POST
        case .SomeRequest,
            return .GET
        }
    }

    var sampleData: NSData {
        switch self {
        case .XAuth:
            return "{}".dataUsingEncoding(NSUTF8StringEncoding)
        case .ServiceRequests:
            return "{}".dataUsingEncoding(NSUTF8StringEncoding)
        }
    }
}

Api Provider setup

    let endpointsClosure = { (target: ApiRouter) -> Endpoint<ApiRouter> in
    let endpoint = Endpoint<ApiRouter>(
        URL: target.baseURL.URLByAppendingPathComponent(target.path).absoluteString!,
        sampleResponse: EndpointSampleResponse.Success(200, { target.sampleData }),
        method: target.method,
        parameters: target.parameters,
        parameterEncoding: parameterEncoding(target)
    )
    switch target {
    case .XAuth:
        return endpoint
    default:
        let token = "some-token"
        return endpoint.endpointByAddingHTTPHeaderFields(["Authorization": "Bearer: \(token)"])
    }
}

func parameterEncoding(target: ApiRouter) -> Moya.ParameterEncoding {
    switch target {
    case .XAuth:
        return .JSON
    case .SomeRequest:
        return .URL
    }
}

let apiProvider = MoyaProvider(endpointsClosure: endpointsClosure)

apiProvider.request(ApiRouter.SomeRequest(params: ["v1", "v2", "v3"], completion: { (data, statusCode, response, error) in
    /* ... */
})

Thanks.

3
Can you please specify, your API is "GET" or "POST"?Sohil R. Memon
@SohilR.Memon it's GET requestVoronov Alexander
Can you please post some code?Sohil R. Memon
And can you please show the api which you are using to testSohil R. Memon
@SohilR.Memon encode what?! Look, what i need to do, is to send GET request with URL parameters that have same keys using Moya framework. Isolate your thoughts from any networking and servers, think of this in terms of strings. In the end I need to get this URL string http://some-site/request?param=v1&param=v2&param=v3. It has nothing to do with POST and other stuff, just plain GET request with same param keys using Moya or Alamofire framework. Do you know how to achieve this?Voronov Alexander

3 Answers

8
votes

So I found a solution which is actually pretty simple and obvious. Reading Alamofire's documentation I found this:

Since there is no published specification for how to encode collection types, Alamofire follows the convention of appending [] to the key for array values (foo[]=1&foo[]=2), and appending the key surrounded by square brackets for nested dictionary values (foo[bar]=baz).

So, for this cases there's Custom ParameterEncoding option which takes closure where you can actually specify your own implementation of how you want parameters to be formed.

Here's the same question with the same answer.

4
votes

Moya is a good idea, but I actually feel that with some thinking, we can construct a network abstraction layer using Swift without much code.

Our goal is :

  • Flexibility, to be able to edit or add new endpoints efficiently
  • Readability, to have a good idea of how our API work at a glance
  • Code safety, with typed parameters to allow all the pre-compilation goodness (completion, validation) we expect from Xcode.
  • Easy debugging, meaning being able to insert logs before and after web requests

Here is what I ended up with on a dummy project :

public class API {

public static let baseURL: String = "http://colourlovers.com/api"

public enum Endpoints {

    case Colors(String)
    case Palettes(String)
    case Patterns(String)

    public var method: Alamofire.Method {
        switch self {
        case .Colors,
             .Palettes,
             .Patterns:
            return Alamofire.Method.GET
        }
    }

    public var path: String {
        switch self {
        case .Colors:
            return baseURL+"/colors"
        case .Palettes:
            return baseURL+"/palettes"
        case .Patterns:
            return baseURL+"/patterns"
        }
    }

    public var parameters: [String : AnyObject] {
        var parameters = ["format":"json"]
        switch self {
        case .Colors(let keywords):
            parameters["keywords"] = keywords
            break
        case .Palettes(let keywords):
            parameters["keywords"] = keywords
            break
        case .Patterns(let keywords):
            parameters["keywords"] = keywords
            break
        }
        return parameters
    }
}

public static func request(
    endpoint: API.Endpoints,
    completionHandler: Response<AnyObject, NSError> -> Void)
    -> Request {

        let request =  Manager.sharedInstance.request(
            endpoint.method,
            endpoint.path,
            parameters: endpoint.parameters,
            encoding: .URL,
            headers: nil
            ).responseJSON { response in

                if (response.result.error) != nil {
                    DDLogError("\n<----\n" + response.result.error!.description)
                    completionHandler(response)
                } else {
                    DDLogInfo("\n<----\n" + response.response!.description)
                    completionHandler(response)
                }
        }
        DDLogInfo("\n---->\n" + request.description)
        return request
    }
}
0
votes

You can simple create the String using format and pass it as a Request URL:

http://some-site/request?param=v1&param=v2&param=v3

String url: String = String(format: "http://some-site/request?param=%@&param=%@&param=%@", v1, v2, v3)

Hope this helps!