[Résolu] Alamofire attendre la réponse avant le return

Hello à tous,

J’ai créé une fonction qui est censée me retourner le résultat d’une requête Alamofire, mais voila, du fait qu’il est asynchrone, il me renvois le résultat sans attendre la fin d’Alamofire. Je sais pas si je suis clair :blush:

private func requeteVote(url:String) -> String {
    var result:String = "Wait for Alamofire"
    Alamofire.request(url, encoding: JSONEncoding.default).responseJSON { (response) in
        if let response = response.result.value,
            let JSON = response as? NSDictionary {
            print(JSON["result"]!) // ca va afficher ce que je veux, avec une petit latence, normal, c'est asynchrone
            result = JSON["result"] as! String
        }
    }
    print result //ca me permet de controller ce qui est retourné a quel moment
    return result
}

le return de la fonction n’attend pas la valeur de retour d’Alamofire avant de me la renvoyer.

Une petite idée sur comment faire ?

Merci pour votre aide!!! :wink:

Hello Didier,

Je ne connais pas très bien AlamoFire mais, de ce que j’en comprends, tout comme un reloadData de tableView par exemple, je pense que ça pourrait se régler en faisant le return de manière asynchrone.

Par exemple (mais certainement à adapter dans ton cas) :

DispatchQueue.main.async {
    return result
}

Hello,

Je te conseil d’utiliser une closure pour renvoyer la valeur de retour.

Du coup sa nous donne :

private func requeteVote(url:String,completionHandler: (_ result:String) → Void ) {
var result:String = « Wait for Alamofire »
Alamofire.request(url, encoding: JSONEncoding.default).responseJSON { (response) in
if let response = response.result.value,
let JSON = response as? NSDictionary {
print(JSON[« result »]!) // ca va afficher ce que je veux, avec une petit latence, normal, c’est asynchrone
result = JSON[« result »] as! String
print result //ca me permet de controller ce qui est retourné a quel moment
completionHandler(result)
}
}

}

Quand tu va compiler xcode va te rajouter un @escaping devan le completionhandler (je ne sais pa pourquoi) mais ne tkt pas c’est normal.

Et tu l’utilise comme ceci :

var myResult = String()
requestVote(url:« Some URl »,completionHandler:{ (result) in
myResult = result
})

Hello @Samir

Merci pour ta réponse, j’ai essayé, mais ça ne fonctionne pas dans mon cas. Après, je n’ai pas mis tout le code!

Voici en détail ce que j’ai fait, basé sur ta réponse :

class Vote {
    private static let url:String = "xxxxxxxxxxxxxxxxxxxxxx"
    func top(_ broadcastId:Int,completionHandler: @escaping (_ myResult:String) -> Void ){
        var myResult = String()
        var url = Vote.url
        url += "?vote=top"
        url += "&broadcastId=\(broadcastId)"
        requeteVote(url:url,completionHandler:{ (result) in
            myResult = result
        })
        completionHandler(myResult)
    }
    func flop(_ broadcastId:Int,completionHandler: @escaping (_ myResult:String) -> Void ) {
        var myResult = String()
        var url = Vote.url
        url += "?vote=flop"
        url += "&broadcastId=\(broadcastId)"
        requeteVote(url:url,completionHandler:{ (result) in
            myResult = result
        })
        completionHandler(myResult)
    }
    private func requeteVote(url:String,completionHandler: @escaping (_ result:String) -> Void ) {
        var result:String = "Wait for Alamofire"
        Alamofire.request(url, encoding: JSONEncoding.default).responseJSON { (response) in
            if let response = response.result.value,
                let JSON = response as? NSDictionary {
                print(JSON["result"]!)
                result = JSON["result"] as! String
                completionHandler(result)
            }
        }
    }
} 

que j’appelle comme ça dans mon ViewController.swift:

@IBAction func buttonVoteTop(_ sender: Any) {
    vote.top(self.currentTrack.broadcastId, completionHandler:{ (result) in
        self.ui_voteResultLabel.text = result
    })
}
@IBAction func buttonVoteFlop(_ sender: Any) {
    vote.flop(self.currentTrack.broadcastId, completionHandler:{ (result) in
        self.ui_voteResultLabel.text = result
    })
}

ui_voteResultLabel n’affiche rien… j’ai utilisé des closures comme tu me l’a suggéré dans mes 3 fonctions dans la class Vote, mais toujours rien :thinking:

hello @schtipoun!

Merci pour ta réponse, mais je n’y parviens pas… j’ai essayé dans tous les sens :sweat:

Il y a des chances que ce soit @Samir qui ait la bonne méthode puisque je n’ai jamais utilisé AlamoFire donc, en ce qui me concerne, c’était juste une piste :slight_smile:
Samir, lui, a l’air d’avoir déjà pratiqué :wink:

@schtipoun je pensais aussi partir sur un dispatch :wink:

Essaye sa , je pense que tu a fait une toute petite erreur :

class Vote {
private static let url:String = "xxxxxxxxxxxxxxxxxxxxxx"
func top(_ broadcastId:Int,completionHandler: @escaping (_ myResult:String) -> Void ){
    var myResult = String()
    var url = Vote.url
    url += "?vote=top"
    url += "&broadcastId=\(broadcastId)"
    requeteVote(url:url,completionHandler:{ (result) in
        myResult = result
        completionHandler(myResult)
    })
  // Tu doi placer ton completionHandler dans le requeteVote
   // completionHandler(myResult)
}
func flop(_ broadcastId:Int,completionHandler: @escaping (_ myResult:String) -> Void ) {
    var myResult = String()
    var url = Vote.url
    url += "?vote=flop"
    url += "&broadcastId=\(broadcastId)"
    requeteVote(url:url,completionHandler:{ (result) in
        myResult = result
      completionHandler(myResult)
    })
   // idem ici 
   // completionHandler(myResult)
}
private func requeteVote(url:String,completionHandler: @escaping (_ result:String) -> Void ) {
    var result:String = "Wait for Alamofire"
    Alamofire.request(url, encoding: JSONEncoding.default).responseJSON { (response) in
        if let response = response.result.value,
            let JSON = response as? NSDictionary {
            print(JSON["result"]!)
            result = JSON["result"] as! String
            completionHandler(result)
        }
    }
}

}
Il faut toujours faire attention quand on fait des tâche asynchrone d’attendre une réponse avant de continuer.

1 « J'aime »

Effectivement, j’avais pas vu ça, pourtant je l’ai fait dans la fonction requeteVote()!

Merci en tout cas, ça fonctionne désormais! :grinning:

Histoire que je comprenne réellement la démarche, as-tu la possibilité de m’expliquer ce qui fait que ça attend le résultat avec une closure? :face_with_monocle:

Je ne sais pas si la notion de closure et claire pour toi mais je vais essayer d’expliquer du mieu que je peu.

Alor en fait l’idée est de passer en paramètre d’une fonction une “autre fonction”, et l’idée c’est d’appeler cette fonction une fois la tâche terminer.

Donc imaginons l’exemple suivant:

func faireTache(param1 :String , param2: (_ param)->Void ){
     //Processing 
    let kkchose = autrekkchose
    // ici on va appeler le param2 qui est une fonction et on va lui passer en paramètre notre "kkchose"
   param2(kkchose) 
}

et on l’utilise comme sa

faireTache(param1:"Fais sa", param2:{ (param) in
   // ici param = kkchose 
}) 

Et donc grace a cette méthode tu peu faire du code asynchrone puisque ce qui se trouve entre les crochet ne s’execute qu’un losrque param2 et appeler.

En fait si tu regarde le code de alamofire tu vera que c’est exactement cette technique qui est utiliser.

Je suis désolé de ne pas être plus claire, je fait donc appel a @maxime.britto pour mieux t’éclairer :slight_smile: .

1 « J'aime »

Effectivement, c’est pas encore très clair cette histoire de closure pour moi… je vais re-re-regarder les vidéos de @mbritto

Merci pour les explications @Samir :wink:

3 « J'aime »

Je vais essayer de prendre un autre exemple

func count(completion:(_terminer:Bool)->Void{
          
       for x in 0...100000000000000000{
                     //On fai un calcul de malade
            }
         completion(true)
    }


 func test(){
   print("debut de la fonction")
    count({ (terminer) in
         print("calcul terminer")
     })
   print("fonction terminer")
}

Si on observe la consol on devrai avoir

debut de la fonction 
fonction termine 
calcul terminer
1 « J'aime »

Oui effectivement @Samir ce dernier exemple est plus explicit.

1 « J'aime »

Eh bin moi c’est toujours le meme bor@&&&&&&l

Je trouve que ton exemple est bon :+1: Mon pseudo est @mbritto je ne reçoit pas les notifications avec @maxime.britto. Ça devait être un compte de tests que je dois supprimer :slight_smile:
Les closures sont souvent complexes à comprendre mais ici le problème venait surtout de la notion asynchrone.
@didier tu ne peux pas retourner le résultat d’un téléchargement avec le return d’une fonction, ni juste après avoir lancé le téléchargement car celui-ci est toujours en cours.
Une requête réseau est lente, très lente, très très lente à l’echelle de l’exécution de lignes de code. On choisit donc de les exécuter en asynchrone. Quand tu lances un téléchargement avec alamofire en fait tu ne fais que programmer une tâche future qui se fera dans quelques millisecondes (voire quelques secondes). Pour pouvoir traiter le résultat on transmet le code à exécuter lorsque le téléchargement sera terminé sous la forme de closure. Alamofire va sauvegarder cette closure pendant tout le temps du téléchargement et lorsque tout sera terminé il exécutera le code de cette closure en lui passant le résultat en paramètre.

Quand tu fais ceci

En fait tu programmes le téléchargement mais juste après l’avoir programmé tu essaies d’utiliser la variable myResult alors qu’elle est encore vide.
Elle sera remplie plus tard, bien après que cette fonction soit terminée, quand la closure sera exécutée

La correction de @Samir consiste à attendre que le chargement soit terminé et traité avant d’utiliser myResult en le plaçant dans la closure que tu envoies à alamofire.

2 « J'aime »