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)
}
}
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.
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/
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
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)")
}
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
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)!
}
}