2014-12-31 9 views
40

Sono molto nuovo a swift, quindi probabilmente avrò molti errori nel mio codice ma quello che sto cercando di ottenere è inviare una richiesta GET a un server localhost con paramters. Più quindi sto cercando di raggiungerlo data la mia funzione prendere due parametri baseURL:string,params:NSDictionary. Non sono sicuro di come combinare questi due nel URLRequest effettivo? Ecco quello che ho provato finoraSwift Richiesta GET con parametri

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) 

    } 

} 

risposta

74

Quando si costruisce una richiesta GET, non c'è il corpo alla richiesta, ma tutto va sull'URL. Per creare un URL (e percorrerlo correttamente per percorrerlo), puoi anche utilizzare URLComponents.

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

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

L'unico trucco è che la maggior parte dei servizi web hanno bisogno + carattere di percentuale sfuggito (perché ti interpretano che, come un carattere di spazio come dettato dalla application/x-www-form-urlencoded specification). Ma lo URLComponents non lo sfugge per cento. Apple sostiene che + è un carattere valido in una query e pertanto non deve essere scappato. Tecnicamente, sono corretti, è consentito in una query di un URI, ma ha un significato speciale nelle richieste application/x-www-form-urlencoded e in realtà non dovrebbe essere passato senza caratteri di escape.

di Apple riconosce che dobbiamo cento sfuggire alle + personaggi, ma consiglia che lo facciamo manualmente:

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

Questo è un poco elegante work-around, ma funziona, ed è quello che Apple consiglia se il vostro le query possono includere un carattere + e si dispone di un server che li interpreta come spazi.

Quindi, combinando che con la vostra sendRequest di routine, si finisce con qualcosa di simile:

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

E che ci si chiami come:

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

    // use `responseObject` here 
} 

Personalmente, mi piacerebbe utilizzare JSONDecoder al giorno d'oggi e restituire un numero personalizzato struct anziché un dizionario, ma non è molto pertinente qui. Si spera che questo illustri l'idea di base su come codificare per percentuale i parametri nell'URL di una richiesta GET.


Vedi previous revision of this answer per Swift 2 e cento manuale di fuga rendition.

+0

Grazie per la risposta fantastica. Ho solo una domanda, sono ancora un po 'confuso su cosa sta facendo la stringa di estensione ai valori? Inoltre quando dovrei usare 'HttpBody' allora? – MrSSS16

+0

L'estensione della stringa è percentuale che sfugge ai valori per RFC 3986. Ci sono determinati caratteri che hanno significati speciali negli URL (es. '&' Separa un parametro dal successivo, quindi se '&' si verifica in valore, non puoi lasciarlo andare senza caratteri di escape). Per quanto riguarda 'HTTPBody', dovresti _non_ usarlo nella richiesta' GET'; È usato in 'POST' ma non' GET'. – Rob

+0

Sembra così semplice, quali sono i vantaggi di utilizzare qualcosa come https://github.com/xyyc/SwiftSocket/ invece di questo? Mi dispiace, sono nuovo di tutto questo. – Jigzat

72

Utilizzare NSURLComponents per costruire il tuo NSURL come questo

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/

+4

Nonostante la risposta impressionante di Rob, la tua è più semplice e funziona. –

+3

Questa dovrebbe essere la risposta accettata. Si consiglia di utilizzare NSURLComponents insieme agli elementi di query per costruire URL. Molto più sicuro e meno incline all'errore. –

0

io uso:

let dictionary = ["method":"login_user", 
        "cel":mobile.text! 
        "password":password.text!] as Dictionary<String,String> 

for (key, value) in dictionary { 
    data=data+"&"+key+"="+value 
    } 

request.HTTPBody = data.dataUsingEncoding(NSUTF8StringEncoding); 
0

Questa estensione che @ Rob suggerito opere di Swift 3.0.1

I non è stato in grado di compilare la versione che ha incluso nel suo post con 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)! 
    } 

} 
2

Swift 3:

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

ho usato per ottenere il nome dell'immagine per UIImagePickerController in 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)") 
} 
3

Sto usando questo, provatelo nel parco giochi. Definire gli URL di base come Struct in Costanti

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 
0

È possibile estendere la vostra Dictionary a fornire solo stringFromHttpParameter se entrambi chiave e il valore conformi alle CustomStringConvertable come questo

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

questo è molto più pulito e impedisce le chiamate accidentali stringFromHttpParameters sui dizionari che non hanno business chiamando quel metodo