[Résolu] Retourner un [EventLoopFuture<Type>]

Bonjour à tous,
je développe en ce moment un webservice Vapor, je reçoit un tableau en json contenant des éléments à modifier dans la db je le traite les modifies et les enregistres en db ça, ça marche seulement, je n’arrive pas à le faire de manière asynchrone car je souhaite retourner les éléments modifiés en json. Du coup j’ai été obligé de le faire de manière synchrone et si jamais il y a beaucoup d’éléments à modifier cela pourrait prendre pas mal de temps.

func modifyInstallations(req: Request) throws -> [Installation]{
    try req.auth.require(User.self)
    let token = try req.auth.require(UserToken.self)
    userTokenController.refreshToken(req: req, token: token)
    
    let database = req.db
    let modifiedInstallationArray = try req.content.decode([Installation.Modify].self)
    var installationsModified:[Installation] = []

    if modifiedInstallationArray.count > 0 {
        for installationUpdated in modifiedInstallationArray {
            Installation.find(installationUpdated.id, on: database)
                .unwrap(or: Abort(.notFound))
                .map({ (installationNeedUpdate) in
                    let installationHasUpdated = updateInstallationForDb(from: installationUpdated, to: installationNeedUpdate)
                    installationsModified.append(installationHasUpdated)
                    installationHasUpdated.update(on: database)
                })
        }
        return installationsModified
    }
    throw Abort(HTTPStatus.badRequest)
}
    
    
//        if modifiedInstallationArray.count > 0 {
//             return modifiedInstallationArray.compactMap { (updatedInstallation) -> EventLoopFuture<Installation> in
//                return Installation.find(updatedInstallation.id, on: database)
//                    .unwrap(or: Abort(.notFound))
//                    .map { (installationNeedUpdate) in
//                        return updateInstallation(from: updatedInstallation, to: installationNeedUpdate)
//                }
//            }
//        }
//        throw Abort(HTTPStatus.badRequest)
//    }

En gros j’avais essayé de le faire avec les lignes commenté mais cela me retourne un [EventLoopFuture] et en théorie cela pourrait marcher mais la route ne passe pas: "Referencing instance method ‹ post(_:use:) › on ‹ Array › requires that ‹ EventLoopFuture › conform to ‹ Content › "

Il faudrait que je retourne au pire un EventLoopFuture<[Installation]> mais je n’est pas trouvé comment faire. Merci d’avance de votre soutien

Bonjour @nicopicks

De ce que je vois, tu n’as pas utilisé Grand Central Disptach, qui gère justement toute la partie asynchrone sous Swift. As-tu vu le cours de Maxime sur les échanges de données web dans la partie « Créer des Apps iPhone avec SwiftUI » ? Il explique bien, je pense que tu trouveras ta solution dedans !

Je n’ai pas encore abordé la partie Vapor de mon côté. Donc je ne pourrais pas t’en dire beaucoup plus.
Bon courage.

Salut @Mrt1;

Dans le code commenté j’utilise compactMap qui apriori fonctionne comme un flatMap qui sont du code exécuté de manière asynchrone. Ceci dit j’avait tenté d’essayer d’utilisé dispatchqueu mais ça n’avais pas l’air de vouloir me donner quelque chose de mieux ^^.

@nicopicks Attention compactMap enlève les valeurs nil d’un tableau, c’est peut-être ça l’erreur. Par ailleurs, dans la doc async de Vapor celui-ci n’est pas mentionné.

https://docs.vapor.codes/4.0/async/

J’utilisé un flatMap à la base mais Xcode me disait que c’était déprécié et du coup me conseiller un compactMap. En fait il n’y a pas d’erreur dans le code le truc c’est que le compactMap ou flatMap retourne un « [EventLoopFuture] » (c’est logique mais ça m’embête :rofl:) et moi il me faudrait un « EventLoopFuture<[Installation]> » sinon la route ressort en erreur car un tableau d’EventLoopFuture n’est pas possible dans une fonction rootable ^^

[EDIT]: J’ai fini par trouvé comment gérer les tableaux dans Vapor et la gestion du futur sur la doc de Vapor. Il faut utilisé flatten à la fin des fonction pour pouvoir retourner un EventLoopFuture<[Installation]> plutôt qu’un [EventLoopFuture]

func modifyInstallations(req: Request) throws -> EventLoopFuture<[Installation]> {
        try req.auth.require(User.self)
        let token = try req.auth.require(UserToken.self)
        try userTokenController.refreshToken(req: req, token: token)
        return try req.content.decode([Installation.Modify].self)
            .map({ (installationUpdated) in
                 Installation.find(installationUpdated.id, on: req.db)
                    .unwrap(or: Abort(.badRequest))
                    .flatMap { (installationNeedUpdate) -> EventLoopFuture<Installation> in
                        let installationHasUpdated = updateInstallationForDb(from: installationUpdated, to: installationNeedUpdate)
                        return installationHasUpdated.save(on: req.db).transform(to: installationHasUpdated)
                    }
            }).flatten(on: req.eventLoop)
    }

Source: Doc Vapor

En espérant pouvoir aider d’autres personnes :grin:

1 « J'aime »

Merci de ton retour @nicopicks, j’étais tellement fixé sur les event loop futur que, je n’ai pas pensé à ta base de données. En tout cas, c’est bon à savoir.