1
votes

Retourne la réponse en tant qu'objet dans Swift

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


2 commentaires

Comment appelle ça? À quoi ressemble la complétion réussie? Vos ré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 les résultats ? Ne serait-il pas préférable d'en faire un JSON puis de l'analyser?


3 Réponses :


0
votes

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.


0 commentaires

0
votes

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


0 commentaires

0
votes

Tout d'abord, n'utilisez pas NSDictionary dans Swift, utilisez [String: Any] natif et déclarez le type comme facultatif pour renvoyer nil code > si une erreur se produit.

Et ne jamais utiliser .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)
                }
            }
        }
    }
}

Votre erreur est que vous ne considérez pas la fermeture, vous devez exécuter tout le code dans le gestionnaire de complétion

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


4 commentaires

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