Podczas budowania GET
żądania nie ma treści, ale raczej wszystko trafia do adresu URL. Aby zbudować adres URL (i odpowiednio zmienić wartość procentową zmiany znaczenia), możesz również użyć URLComponents
.
var url = URLComponents(string: "https://www.google.com/search/")!
url.queryItems = [
URLQueryItem(name: "q", value: "War & Peace")
]
Jedyna sztuczka polega na tym, że większość usług internetowych wymaga +
znaku procentowego ucieczki (ponieważ będą interpretować to jako znak spacji zgodnie ze application/x-www-form-urlencoded
specyfikacją ). Ale URLComponents
procent nie ucieknie od tego. Firma Apple twierdzi, że +
jest to prawidłowy znak w zapytaniu i dlatego nie należy go zmieniać. Technicznie są poprawne, że jest to dozwolone w zapytaniu o identyfikator URI, ale ma to specjalne znaczenie w application/x-www-form-urlencoded
żądaniach i naprawdę nie powinno być przekazywane bez zmiany znaczenia.
Apple przyznaje, że musimy procentować unikaniem +
znaków, ale radzi, abyśmy robili to ręcznie:
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")
Jest to nieeleganckie obejście, ale działa i Apple radzi, jeśli twoje zapytania mogą zawierać +
znak, a masz serwer, który interpretuje je jako spacje.
Łącząc to ze swoją sendRequest
rutyną, otrzymujesz coś takiego:
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,
let response = response as? HTTPURLResponse,
(200 ..< 300) ~= response.statusCode,
error == nil else {
completion(nil, error)
return
}
let responseObject = (try? JSONSerialization.jsonObject(with: data)) as? [String: Any]
completion(responseObject, nil)
}
task.resume()
}
I nazwałbyś to tak:
sendRequest("someurl", parameters: ["foo": "bar"]) { responseObject, error in
guard let responseObject = responseObject, error == nil else {
print(error ?? "Unknown error")
return
}
}
Osobiście użyłbym JSONDecoder
teraz i zwróciłbym struct
raczej niestandardowy niż słownik, ale to nie jest tutaj naprawdę istotne. Mamy nadzieję, że ilustruje to podstawową koncepcję kodowania procentowego parametrów w adresie URL żądania GET.
Zobacz poprzednią wersję tej odpowiedzi dla wersji Swift 2 i ręcznych wersji ze ucieczką procentową.
extension string
robi z wartościami? Kiedy miałbymHttpBody
wtedy użyć ?&
Oddzielają jeden parametr od następnego, więc jeśli&
występuje w wartości, nie można go puścić bez zmiany znaczenia). W związku zHTTPBody
tym nie powinieneś używać go naGET
żądanie; Jest używany,POST
ale nieGET
.NSMutableURLRequest
, wskazując, że chodzi o coś więcej, niż sugeruje PO.NSJSONSerialization.dataWithJSONObject(parameters, options: nil, error: nil)
?parameters
będąc tablicą słowników[String: String]
Użyj NSURLComponents, aby zbudować swój identyfikator NSURL w ten sposób
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
źródło
Używam tego, spróbuj na placu zabaw. Zdefiniuj podstawowe adresy URL jako Struct w stałych
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
źródło
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 } }
Użyłem go, aby uzyskać nazwę obrazu
UIImagePickerController
wfunc 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)") }
źródło
Możesz rozszerzyć swoje,
Dictionary
aby zapewnić tylkostringFromHttpParameter
wtedy, gdy zarówno klucz, jak i wartość są zgodne zCustomStringConvertable
tymextension Dictionary where Key : CustomStringConvertible, Value : CustomStringConvertible { func stringFromHttpParameters() -> String { var parametersString = "" for (key, value) in self { parametersString += key.description + "=" + value.description + "&" } return parametersString } }
jest to znacznie czystsze i zapobiega przypadkowym wywołaniom
stringFromHttpParameters
słowników, które nie mają żadnego interesu w wywoływaniu tej metodyźródło
To rozszerzenie, które @Rob zasugerował, działa dla Swift 3.0.1
Nie byłem w stanie skompilować wersji, którą zamieścił w swoim poście, z 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)! } }
źródło
Używam:
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);
źródło