J'ai créé une classe utilitaire dans mon projet Swift qui gère toutes les demandes et réponses REST. J'ai construit une API REST simple pour pouvoir tester mon code. J'ai créé une méthode de classe qui doit renvoyer un NSArray, mais comme l'appel d'API est asynchrone, je dois retourner de la méthode à l'intérieur de l'appel async. Le problème est que l'async renvoie nul. Si je faisais cela dans Node, j'utiliserais les promesses JS mais je ne peux pas trouver une solution qui fonctionne dans Swift.
import Foundation
class Bookshop {
class func getGenres() -> NSArray {
println("Hello inside getGenres")
let urlPath = "http://creative.coventry.ac.uk/~bookshop/v1.1/index.php/genre/list"
println(urlPath)
let url: NSURL = NSURL(string: urlPath)
let session = NSURLSession.sharedSession()
var resultsArray:NSArray!
let task = session.dataTaskWithURL(url, completionHandler: {data, response, error -> Void in
println("Task completed")
if(error) {
println(error.localizedDescription)
}
var err: NSError?
var options:NSJSONReadingOptions = NSJSONReadingOptions.MutableContainers
var jsonResult = NSJSONSerialization.JSONObjectWithData(data, options: options, error: &err) as NSDictionary
if(err != nil) {
println("JSON Error \(err!.localizedDescription)")
}
//NSLog("jsonResults %@", jsonResult)
let results: NSArray = jsonResult["genres"] as NSArray
NSLog("jsonResults %@", results)
resultsArray = results
return resultsArray // error [anyObject] is not a subType of 'Void'
})
task.resume()
//return "Hello World!"
// I want to return the NSArray...
}
}
ios
rest
asynchronous
swift
Mark Tyers
la source
la source
Réponses:
Vous pouvez passer le rappel et appeler le rappel dans un appel asynchrone
quelque chose comme:
puis appelez cette méthode:
la source
override func viewDidLoad() { super.viewDidLoad() var genres = Bookshop.getGenres() // Missing argument for parameter #1 in call //var genres:NSArray //Bookshop.getGenres(genres) NSLog("View Controller: %@", genres) }
Swiftz propose déjà Future, qui est la pierre angulaire d'une promesse. Un avenir est une promesse qui ne peut pas échouer (tous les termes ici sont basés sur l'interprétation Scala, où une promesse est une monade ).
https://github.com/maxpow4h/swiftz/blob/master/swiftz/Future.swift
J'espère que cela finira par devenir une promesse complète de style Scala (je pourrai l'écrire moi-même à un moment donné; je suis sûr que d'autres PR seraient les bienvenus; ce n'est pas si difficile avec Future déjà en place).
Dans votre cas particulier, je créerais probablement un
Result<[Book]>
(basé sur la version d'Alexandros Salazar deResult
). Ensuite, votre signature de méthode serait:Remarques
get
dans Swift. Cela rompra certains types d'interopérabilité avec ObjC.Book
objet avant de renvoyer vos résultats sous forme de fichierFuture
. Ce système peut échouer de plusieurs manières, et il est beaucoup plus pratique de vérifier toutes ces choses avant de les regrouper dans un fichierFuture
. Il[Book]
est bien meilleur pour le reste de votre code Swift que de remettre un fichierNSArray
.la source
Future
. Mais jetez un œil à github.com/mxcl/PromiseKit cela fonctionne très bien avec Swiftz!get
préfixe indique le retour par référence dans ObjC (comme dans-[UIColor getRed:green:blue:alpha:]
). Quand j'ai écrit ceci, je craignais que les importateurs exploitent ce fait (pour renvoyer automatiquement un tuple par exemple). Il s'est avéré que non. Quand j'ai écrit ceci, j'avais probablement aussi oublié que KVC supporte les préfixes "get" pour les accesseurs (c'est quelque chose que j'ai appris et oublié plusieurs fois). Donc d'accord; Je n'ai rencontré aucun cas où le leaderget
casse les choses. C'est juste trompeur pour ceux qui connaissent la signification de «obtenir» ObjC.Le modèle de base consiste à utiliser la fermeture des gestionnaires d'achèvement.
Par exemple, dans le prochain Swift 5, vous utiliseriez
Result
:Et vous l'appeleriez comme ça:
Notez que ci-dessus, je renvoie le gestionnaire d'achèvement dans la file d'attente principale pour simplifier les mises à jour du modèle et de l'interface utilisateur. Certains développeurs s'opposent à cette pratique et utilisent n'importe quelle file d'attente
URLSession
utilisée ou utilisent leur propre file d'attente (obligeant l'appelant à synchroniser manuellement les résultats lui-même).Mais ce n'est pas important ici. Le problème clé est l'utilisation du gestionnaire d'achèvement pour spécifier le bloc de code à exécuter lorsque la requête asynchrone est effectuée.
Le modèle plus ancien, Swift 4, est:
Et vous l'appeleriez comme ça:
Notez que ci-dessus, j'ai retiré l'utilisation de
NSArray
(nous n'utilisons plus ces types Objective-C pontés ). Je suppose que nous avions unGenre
type et que nous l'avons probablement utiliséJSONDecoder
plutôt queJSONSerialization
pour le décoder. Mais cette question ne contenait pas suffisamment d'informations sur le JSON sous-jacent pour entrer dans les détails ici, j'ai donc omis cela pour éviter de brouiller le problème principal, l'utilisation de fermetures comme gestionnaires d'achèvement.la source
Result
dans Swift 4 et les versions antérieures, mais vous devez déclarer l'énumération vous-même. J'utilise ce genre de modèle depuis des années.Swift 4.0
Pour async Request-Response, vous pouvez utiliser le gestionnaire d'achèvement. Voir ci-dessous, j'ai modifié la solution avec le paradigme de la poignée d'achèvement.
Vous pouvez appeler cette fonction comme ci-dessous:
la source
Version Swift 3 de la réponse de @Alexey Globchastyy:
la source
J'espère que vous n'êtes pas encore coincé là-dessus, mais la réponse courte est que vous ne pouvez pas faire cela dans Swift.
Une autre approche consisterait à renvoyer un rappel qui fournira les données dont vous avez besoin dès qu'elles sont prêtes.
la source
callback
avecclosure
s comme vous le faites remarquer ou à utiliserdelegation
comme les anciennes API de cacaoIl existe 3 façons de créer des fonctions de rappel à savoir: 1. Gestionnaire d'achèvement 2. Notification 3. Délégués
Completion Handler L' intérieur du bloc de blocs est exécuté et renvoyé lorsque la source est disponible, le gestionnaire attendra que la réponse arrive pour que l'interface utilisateur puisse être mise à jour après.
Notification Un tas d'informations est déclenché sur toute l'application, Listner peut récupérer n utiliser ces informations. Moyen asynchrone d'obtenir des informations tout au long du projet.
Délégués L' ensemble de méthodes sera déclenché lorsque le délégué est appelé, la source doit être fournie via les méthodes elle-même
la source
la source
Il existe principalement 3 façons de réaliser un rappel dans Swift
Gestionnaire de fermetures / achèvement
Délégués
Notifications
Les observateurs peuvent également être utilisés pour être notifié une fois la tâche asynchrone terminée.
la source
Il existe des exigences très génériques que tout bon gestionnaire d'API devrait satisfaire: implémentera un client API orienté protocole.
Interface initiale APIClient
Maintenant, veuillez vérifier la structure complète de l'API
la source
Ceci est un petit cas d'utilisation qui pourrait être utile: -
Lors de l'appel de la fonction: -
la source