Re-synchronisation des callback de multiples NSItemProvider (question générale sur la concurrence)
J'ai une petite question relative à la gestion la concurrence avec Swift. L'idée n'est pas de toucher à la nouveauté Swift Concurrency mais rester avec du bon vieux Grand Central Dispatch.
Voilà le soucis: j'ai une View
qui est censée accepter un drop de n fichiers. Avec SwiftUI on récupère un Array
de NSItemProvider
. J'ai l'habitude d'AppKit et de NSPasteBoard
qui n'utilise que des API synchrones mais là c'est pas le cas. Je suis obligé de passer pas la méthode qui commence par load
et fini avec un completion handler appelé de manière asynchrone.
C'est pas très grave quand on balade un objet mais là j'en ai n avec une spécificité si n == 1. Alors voilà ce que je voudrai faire de manière efficace :
En un bloc :
- Récupérer les
NSItemProvider
- Loader les URL correspondantes au fichier
- Filtrer les URL qui ne correspondent pas à une image
- Créer les
NSImage
s correspondantes - Mapper les images vers un autre type avec un peu de metadata et ma soupe à moi
Pour au final obtenir un Array
. Une fois que je l'ai je mets ça dans une propriété du modèle et il se démerde pour gérer le tout. Le soucis que j'ai c'est pour définir la méthode pour gérer le loading des URL en un bloc et finir avec un gros DispatchQueue.main.async { /* Mets la valeur dans le modèle */ }
des familles.
Quelqu'un peut me filer un coup de main ?
Réponses
Dans mon appli, j'utilise PromiseKit pour ce genre de choses… qu'on peut parait-il remplacer assez facilement par Combine.
Est-ce que ça te parle ? Je peux te donner un exemple de ce que ça donnerait.
Ben oui des promises et Combine ! J'ai vraiment besoin de vacances...
Merci @Céroce 😃
Heureux d'avoir servi de canard en plastique!
Je la connaissais pas cette expression tien !
Mon Combine était un peu rouillé ça a pris plus de temps que prévu. Quoi qu'il en soit voilà le résultat, ça peut aider du monde. Le code intéressant se trouve dans le
onDrop
le reste a été grandement simplifié:Pas certain d'avoir compris le problème, mais s'il s'agit de donner un point de rendez-vous à plusieurs taches exécutées en parallèle, il faut utiliser les dispatch groups en gcd.
Chaque fois que tu entres dans une nouvelle tâche tu fais un enter() sur le group, quand la tache se termine tu fais un leave(), quand tous les leave ont été fait un block que tu as fourni au préalable (via notify()) est exécuté.
Oui le soucis sous-jacent c'est exactement ça @FKDEV.
Et maintenant, à tête reposée, c'est un cas d'école typique pour l'utilisation des sémaphores.
Par contre pour la prochaine fois, c'est safe d'appeler les méthodes de
DispatchGroup
depuis n'import quel thread ? Je trouve pas l'info dans la doc.Si j'ai bien compris comment ça s'utilise, je dirais oui, car tu es obligé de le faire.
Notamment, dans l'exemple suivant, tu dois faire le enter() avant le début de la tâche sinon tu risques d'en louper.
Les sémaphores, cela fonctionne aussi, si tu connais le nombre de tache avant, ou alors tu peux t'en servir pour limiter le nombre de tâches en parallèle. Ici 5 tâches maximum :
@FKDEV est-ce qu'on peut annuler le DispatchGroup en cas d'échec? C'est l'un des intérêts de la solution à base de Promises/Futures.
C’est très basique, c’est à toi de gérer les erreurs.
L’utilisation de promises donne un code plus lisible, mais pour ma part je ne veux pas utiliser de librairie tierce partie pour gérer la structure d’une app.
Bah si t'es en mesure de comprendre et maintenir son code, pourquoi pas ?
Je préfère les décortiquer et prendre les parties qui m'intéressent, cela permet d'apprendre, de s'approprier le code.
En ce qui concerne la structure, si tu veux réutiliser des morceaux de ton app ou faire un portage, t'es vite coincé.
Pareil, s'il y a de une nouvelles features du système et que la librairie n'est plus maintenue.
Et puis tu te retrouves à apprendre à utiliser une API dont tu n'es pas sûr de la pérennité. Autant apprendre des API first-party.
Après tout dépend du contexte...