J'ai une fonction qui se connecte à une API pour récupérer des données. L'API prend deux paramètres accessCode (fournis par l'utilisateur dans une zone de texte), puis UDID (UDID de leur appareil). Je peux analyser les données à partir de la fonction, mais uniquement localement. J'ai besoin de stocker les valeurs renvoyées mais je ne sais pas comment les renvoyer correctement. Essentiellement, j'en ai besoin pour renvoyer l'objet json en tant que dictionnaire (je pense ...) afin qu'il puisse être analysé en dehors de la tâche asynchrone. J'ai lu la documentation rapide et c'est là que j'ai trouvé comment faire les requêtes, mais je ne trouve pas de moyen de stocker les valeurs renvoyées en mémoire pour un accès en dehors de la fonction.
func getResponse(accessCode:String, UDID:String, _ completion: @escaping (NSDictionary) -> ()) { let urlPath = "https://apihosthere.com/api/validate?accessCode=" + accessCode + "&UDID=" + UDID guard let url = URL(string: urlPath) else { return } let task = URLSession.shared.dataTask(with: url) { (data, response, error) in guard let data = data else { return } do { if let jsonResult = try JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.mutableContainers) as? NSDictionary { let results = jsonResult as? NSDictionary print(results) completion(results!) } } catch { //Catch Error here... } } task.resume() }
3 Réponses :
Votre fermeture complétion
doit gérer les données obtenues. Vous appelleriez la fonction comme ceci:
getResponse(accessCode: "code", UDID: "udid", completion: { result in // Do whatever you need to do with the dictionary result }
De plus, je vous recommande de changer votre NSDictionary
avec un Dictionary
rapide.
C'est ce que l'API renvoie comme réponse
@IBAction func StartWizard(_ sender: UIButton) { //Store entered access code let accessCode = AccessCodeField.text! var accessCodeFound: Bool? = nil //Call API to validate Access Code getResponse(accessCode:accessCode, UDID:myDeviceUDID) { result in accessCodeFound = result["Found"] as! Bool print("Value of Found during function:") print(accessCodeFound) //accessCodeFound = true } //If access code is valid, go to License view print("Value of Found after function:") print(accessCodeFound) //accessCodeFound = nil ??? //it seems the value is getting reset after the function completes if accessCodeFound == true{ //Load License View let storyboard = UIStoryboard(name: "Main", bundle: nil) let licenseController = storyboard.instantiateViewController(identifier: "LicenseViewPanel") self.show(licenseController, sender: Any?.self) } }
Et c'est ce qui appelle la fonction. J'appelle après avoir cliqué sur un bouton après la saisie d'un code d'accès dans un champ de texte.
{ AccessCode = 00000000; Client = "0000 - My Company Name"; EmailAddress = "brandon@brandonthomas.me"; FirstName = Brandon; Found = 1; LastName = Thomas; Status = A; UDIDregistered = 1; }
Tout d'abord, n'utilisez pas Et ne jamais utiliser Votre erreur est que vous ne considérez pas la fermeture, vous devez exécuter tout le code dans le gestionnaire de complétion NSDictionary
dans Swift, utilisez [String: Any]
natif et déclarez le type comme facultatif pour renvoyer nil code > si une erreur se produit.
.mutableContainers
dans Swift, l'option est inutile. @IBAction func StartWizard(_ sender: UIButton) {
//Store entered access code
let accessCode = AccessCodeField.text!
//Call API to validate Access Code
getResponse(accessCode:accessCode, UDID:myDeviceUDID) { [weak self] result in
if let accessCodeFound = result?["Found"] as? Bool {
print("Value of Found during function:")
//If access code is valid, go to License view
print(accessCodeFound)
if accessCodeFound {
//Load License View
DispatchQueue.main.async {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let licenseController = storyboard.instantiateViewController(identifier: "LicenseViewPanel")
self?.show(licenseController, sender: self)
}
}
}
}
}
func getResponse(accessCode:String, UDID:String, completion: @escaping ([String:Any]?) -> Void)) {
let urlPath = "https://apihosthere.com/api/validate?accessCode=" + accessCode + "&UDID=" + UDID
guard let url = URL(string: urlPath) else { return }
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
if let error = error else {
print(error)
completion(nil)
return
}
do {
if let jsonResult = try JSONSerialization.jsonObject(with: data!) as? [String:Any] {
print(jsonResult)
completion(jsonResult)
} else {
completion(nil)
}
} catch {
print(error)
completion(nil)
}
}
task.resume()
}
OK donc le reste du code doit être à l'intérieur du gestionnaire d'achèvement, cela a du sens. Existe-t-il un moyen de sauvegarder les valeurs pour qu'elles soient également accessibles en dehors de celle-ci? Comme si je voulais stocker accessCodeFound et les autres variables que je dois créer pour stocker le reste des valeurs extraites de l'appel d'API, existe-t-il un moyen de les rendre globales afin qu'elles soient accessibles sur le contrôleur de vue suivant?
Créez un dictionnaire dans le contrôleur LicenseViewPanel
et attribuez-lui (non emballé) le résultat
avant la ligne show
. Cependant, il est fortement recommandé d'analyser le JSON dans une structure avec Decodable
.
Très bien, je vais examiner la mise en place. Actuellement, lorsque j'exécute la fonction StartWizard dans votre réponse, j'obtiens une erreur: - [UIViewController initWithCoder:] doit être utilisé uniquement à partir du thread principal "Il semble que les contrôleurs de vue ne peuvent être modifiés que sur le thread principal plutôt que dans la tâche async. Comment pourrait-on contourner cela?
Il y a de l'argent! Je vous remercie beaucoup pour votre aide. Je vais mettre en place une structure codable pour contenir les valeurs
Comment appelle ça? À quoi ressemble la
complétion
réussie? Vosrésultats
s’impriment-ils correctement?Vous voulez juste n'importe quelle valeur dans
résultats
en dehors de cela si let fonctionne? Ou vous n'arrivez pas à obtenir de valeur dans lesrésultats
? Ne serait-il pas préférable d'en faire un JSON puis de l'analyser?