web-dev-qa-db-fra.com

Swift GET avec paramètres

Je suis très nouveau dans Swift, je vais donc avoir beaucoup de fautes dans mon code, mais ce que je cherche à faire, c’est d’envoyer une requête GET à un serveur localhost avec des paramètres. Plus encore, j'essaie d'y parvenir vu que ma fonction prend deux paramètres baseURL:string,params:NSDictionary. Je ne suis pas sûr de savoir comment combiner ces deux dans l'URLRequest réelle? Voici ce que j'ai essayé jusqu'à présent

    func sendRequest(url:String,params:NSDictionary){
       let urls: NSURL! = NSURL(string:url)
       var request = NSMutableURLRequest(URL:urls)
       request.HTTPMethod = "GET"
       var data:NSData! =  NSKeyedArchiver.archivedDataWithRootObject(params)
       request.HTTPBody = data
       println(request)
       var session = NSURLSession.sharedSession()
       var task = session.dataTaskWithRequest(request, completionHandler:loadedData)
       task.resume()

    }

}

func loadedData(data:NSData!,response:NSURLResponse!,err:NSError!){
    if(err != nil){
        println(err?.description)
    }else{
        var jsonResult: NSDictionary = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: nil) as NSDictionary
        println(jsonResult)

    }

}
64
MrSSS16

Lors de la construction d'une requête GET, il n'y a pas de corps dans la requête, mais tout se trouve dans l'URL. Pour construire une URL (et le pourcentage d’échappements corrects), vous pouvez également utiliser URLComponents.

var url = URLComponents(string: "https://www.google.com/search/")!

url.queryItems = [
    URLQueryItem(name: "q", value: "War & Peace")
]

La seule astuce est que la plupart des services Web ont besoin de + pourcentage de caractères échappés (car ils interpréteront cela comme un espace, comme dicté par le application/x-www-form-urlencoded spécification ). Mais URLComponents n'y échappera pas pour cent. Apple affirme que + est un caractère valide dans une requête et ne doit donc pas être échappé. Techniquement, ils ont raison, cela est autorisé dans une requête d'un URI, mais cela a une signification particulière dans application/x-www-form-urlencoded demandes et ne devrait vraiment pas être passé sans échapper.

Apple reconnaît que nous devons échapper pour cent au + caractères, mais conseille de le faire manuellement:

var url = URLComponents(string: "https://www.wolframalpha.com/input/")!

url.queryItems = [
    URLQueryItem(name: "i", value: "1+2")
]

url.percentEncodedQuery = url.percentEncodedQuery?.replacingOccurrences(of: "+", with: "%2B")

C’est une solution inélégante, mais elle fonctionne et c’est ce que Apple indique si vos requêtes peuvent inclure un + et vous avez un serveur qui les interprète comme des espaces.

Donc, en combinant cela avec votre routine sendRequest, vous vous retrouvez avec quelque chose comme:

func sendRequest(_ url: String, parameters: [String: String], completion: @escaping ([String: Any]?, Error?) -> Void) {
    var components = URLComponents(string: url)!
    components.queryItems = parameters.map { (key, value) in 
        URLQueryItem(name: key, value: value) 
    }
    components.percentEncodedQuery = components.percentEncodedQuery?.replacingOccurrences(of: "+", with: "%2B")
    let request = URLRequest(url: components.url!)

    let task = URLSession.shared.dataTask(with: request) { data, response, error in
        guard let data = data,                            // is there data
            let response = response as? HTTPURLResponse,  // is there HTTP response
            (200 ..< 300) ~= response.statusCode,         // is statusCode 2XX
            error == nil else {                           // was there no error, otherwise ...
                completion(nil, error)
                return
        }

        let responseObject = (try? JSONSerialization.jsonObject(with: data)) as? [String: Any]
        completion(responseObject, nil)
    }
    task.resume()
}

Et vous l'appeleriez comme:

sendRequest("someurl", parameters: ["foo": "bar"]) { responseObject, error in
    guard let responseObject = responseObject, error == nil else {
        print(error ?? "Unknown error")
        return
    }

    // use `responseObject` here
}

Personnellement, j’utiliserais JSONDecoder de nos jours et renverrais un struct personnalisé au lieu d’un dictionnaire, mais ce n’est pas vraiment pertinent ici. J'espère que cela illustre l'idée de base sur la manière de coder les paramètres dans l'URL d'une requête GET.


Voir révision précédente de cette réponse pour Swift 2 et les pourcentages d’échappement manuels.

115
Rob

Utilisez NSURLComponents pour construire votre NSURL comme ceci

var urlComponents = NSURLComponents(string: "https://www.google.de/maps/")!

urlComponents.queryItems = [
  NSURLQueryItem(name: "q", value: String(51.500833)+","+String(-0.141944)),
  NSURLQueryItem(name: "z", value: String(6))
]
urlComponents.URL // returns https://www.google.de/maps/?q=51.500833,-0.141944&z=6

font: https://www.ralfebert.de/snippets/ios/encoding-nsurl-get-parameters/

86
Ben-Hur Batista

J'utilise ceci, essayez-le dans la cour de récréation. Définir les urls de base comme Struct dans les constantes

struct Constants {

    struct APIDetails {
        static let APIScheme = "https"
        static let APIHost = "restcountries.eu"
        static let APIPath = "/rest/v1/alpha/"
    }
}

private func createURLFromParameters(parameters: [String:Any], pathparam: String?) -> URL {

    var components = URLComponents()
    components.scheme = Constants.APIDetails.APIScheme
    components.Host   = Constants.APIDetails.APIHost
    components.path   = Constants.APIDetails.APIPath
    if let paramPath = pathparam {
        components.path = Constants.APIDetails.APIPath + "\(paramPath)"
    }
    if !parameters.isEmpty {
        components.queryItems = [URLQueryItem]()
        for (key, value) in parameters {
            let queryItem = URLQueryItem(name: key, value: "\(value)")
            components.queryItems!.append(queryItem)
        }
    }

    return components.url!
}

let url = createURLFromParameters(parameters: ["fullText" : "true"], pathparam: "IN")

//Result url= https://restcountries.eu/rest/v1/alpha/IN?fullText=true
4
anoop4real

Swift:

extension URL {
    func getQueryItemValueForKey(key: String) -> String? {
        guard let components = NSURLComponents(url: self, resolvingAgainstBaseURL: false) else {
              return nil
        }

        guard let queryItems = components.queryItems else { return nil }
     return queryItems.filter {
                 $0.name.lowercased() == key.lowercased()
                 }.first?.value
    }
}

Je l'ai utilisé pour obtenir le nom de l'image pour UIImagePickerController dans func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]):

var originalFilename = ""
if let url = info[UIImagePickerControllerReferenceURL] as? URL, let imageIdentifier = url.getQueryItemValueForKey(key: "id") {
    originalFilename = imageIdentifier + ".png"
    print("file name : \(originalFilename)")
}
2
Danut Pralea

Vous pouvez étendre votre Dictionary pour ne fournir que stringFromHttpParameter que si la clé et la valeur sont conformes à CustomStringConvertable comme ceci

extension Dictionary where Key : CustomStringConvertible, Value : CustomStringConvertible {
  func stringFromHttpParameters() -> String {
    var parametersString = ""
    for (key, value) in self {
      parametersString += key.description + "=" + value.description + "&"
    }
    return parametersString
  }
}

ceci est beaucoup plus propre et empêche les appels accidentels à stringFromHttpParameters sur des dictionnaires qui n'ont pas d'affaires appelant cette méthode

0
Reza Shirazian

Cette extension proposée par @Rob fonctionne pour Swift 3.0.1

Je n'ai pas pu compiler la version qu'il a incluse dans son message avec Xcode 8.1 (8B62)

extension Dictionary {

    /// Build string representation of HTTP parameter dictionary of keys and objects
    ///
    /// :returns: String representation in the form of key1=value1&key2=value2 where the keys and values are percent escaped

    func stringFromHttpParameters() -> String {

        var parametersString = ""
        for (key, value) in self {
            if let key = key as? String,
               let value = value as? String {
                parametersString = parametersString + key + "=" + value + "&"
            }
        }
        parametersString = parametersString.substring(to: parametersString.index(before: parametersString.endIndex))
        return parametersString.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)!
    }

}
0
etayluz