[SWIFT] En utilisant le protocole, le processus de mise à jour de l'interface utilisateur après une communication asynchrone était très facile à comprendre.

introduction

Bonjour, c'est dayossi des ingénieurs iOS.

Je veux offrir un service qui rend ma famille heureuse Nous avons publié une application de journal familial appelée HaloHalo.

Cette fois, à propos du traitement après une communication asynchrone J'ai écrit que le protocole était très pratique.

(Les livrables de cet article sont listés en bas)

Qu'est-ce qui vous a donné envie de mettre à jour l'interface utilisateur de manière asynchrone en premier lieu?

«J'ai essayé de refléter la valeur dans CollectionView après avoir obtenu les données par communication asynchrone. D'une manière ou d'une autre, la méthode d'affichage ne fonctionne pas. "

Je pensais que c'était le déclencheur.

Quelle est la cause?

Cette fois, je me suis concentré sur le traitement de la communication asynchrone et je l'ai divisé en deux problèmes.

・ 1: Les données référencées sont incorrectes. ・ 2: la mise à jour de l'interface utilisateur est lente et ne peut pas être lissée

1: les données référencées sont incorrectes

-Lorsque la valeur acquise par l'API est stockée dans le modèle de données construit avec le type de classe La valeur stockée est un type de référence. Même après l'avoir remplacé par une variable, si vous réécrivez les données dans la variable Les données originales seront également réécrites.

・ Ainsi, par exemple, vous pouvez obtenir plusieurs données utilisateur en même temps. Si vous souhaitez afficher des valeurs spécifiques à l'utilisateur Les données peuvent être réécrites avant la mise à jour de l'interface utilisateur, Il peut ne pas s'afficher comme prévu.

[Source de référence] Problèmes de type et solutions de contournement sans valeur sémantique

2: la mise à jour de l'interface utilisateur est lente et ne peut pas être lissée

Comme le modèle MVC, la vue qui affiche l'interface utilisateur et le modèle qui contient les données nécessaires pour dessiner l'interface utilisateur sont en contact étroit. Lorsque tout le traitement est solidement écrit dans le contrôleur de vue et qu'il devient l'état du contrôleur de vue Fat Avec le processus d'acquisition des données du serveur via la communication API Parce que le processus de création de l'interface utilisateur se fait dans le même thread

Le traitement dans le thread principal a été arrêté, bien que ce soit peu de temps Le comportement de l'application s'arrêtera.

Par conséquent, je veux que l'interface utilisateur soit affichée de manière nette et élégante. Il y a un problème de blocage de l'écran.

[Source de référence] [Swift] Grand Central Dispatch (GCD) et Operation Queue Summary

Mesures prises jusqu'à présent

Pour contrer ces problèmes, j'ai souvent pris personnellement les éléments suivants.

Premièrement: les données référencées sont incorrectes

→ Concevoir une méthode de conservation des données

● Lorsque vous détenez des données, gérez-les avec le type valeur par struct

→ Si vous détenez les données JSON renvoyées par l'API en tant que type valeur par struct Parce que vous pouvez réduire considérablement le risque d'écrasement étrange lors de la réception de plusieurs résultats de communication.

● Gérez les données stockées en les plaçant dans une autre variable de type tableau ou dictionnaire

→ L'Arrey de Swift a une fonction pour conserver les données de type référence comme type valeur (... pratique !!!)

[Source de référence] Swift's Array était en fait incroyable)

Deuxièmement: la mise à jour de l'interface utilisateur est lente et ne peut pas être lissée

→ Passer au traitement asynchrone

● Insérer un traitement asynchrone à l'aide de DispatchQueue

→ Séparez le fil principal pour dessiner / mettre à jour l'interface utilisateur et le fil d'arrière-plan pour le traitement des données et la communication. Je voulais alléger le traitement du fil principal

[Source de référence] Exemple de traitement asynchrone de Swift (modèles multiples) [Swift] Grand Central Dispatch (GCD) et Operation Queue Summary

Cependant, comme moyen de communication pour refléter les données obtenues de manière asynchrone vers l'interface utilisateur Parce que j'écrivais un moyen de renvoyer la valeur avec un rappel

Le nid devenait de plus en plus profond et il était dans un état chaotique. .. .. Je ne veux pas le relire. .. .. Pleurs

class hoge{
  /*
Par exemple, après avoir récupéré les données utilisateur du serveur
GetImage dans HugaClass()Avec la méthode
Je veux aller chercher les données d'image...
  */

  //Instancier inutilement toute la classe...  
  let huga = HugaClass()
 
  //Emboîtement profond et référence forte dans la fermeture...
  func getUserData(){ (userData) in
      let userId = userData.userId
      huga.willFetchImage(userId){ (imageData) in
         self.imageData.image = imageData.image
      }
  }
}

C'était le plus gros goulot d'étranglement "Comment mettre à jour l'interface utilisateur en fonction des données acquises?" C'était le point.

Possibilité de protocole

L'introduction est devenue longue, mais le sujet principal est enfin là.

Pour appeler le processus implémenté dans la classe Chaque fois que vous instanciez une classe et appelez un processus dans cette classe Il devient difficile de comprendre où et quel processus a été écrit.

Bien qu'il puisse être exporté avec un petit nombre de codes, Il y a aussi la fragilité que les scènes qui peuvent être utilisées sont très limitées.

Pendant ce temps, je lisais le modèle de conception de l'application iOS

Model

Je vais expliquer le modèle lui-même et l'implémentation du modèle comme RxSwift des points de vue suivants. ・ Faites-en un protocole et rendez-le faiblement couplé et testable ・ Retourner Observable pour améliorer l'installation avec VIew Model

(Source: Introduction aux modèles de conception d'applications iOS p124)

J'ai été impressionné par le contenu de "Protocolisation, couplage lâche et testable".

En bref, via protocole Nous reconnaissons que nous devons extraire et transmettre uniquement les informations dont nous avons besoin.

// MARK:-Il est clair quel type de traitement sera transféré!
protocol HugaProtocol{
     func willFetchImage(_ userId:String)
}

class hoge: hugaProtocol{
  
  //Peut être livré par protocole, pas par classe
  private weak var repository:HugaProtocol?
  init(repository:HugaProtocol){
    self.repository = repository
  }

  func getUserData(){ (userData) in
     let userId = userData.userId
     input.willFetchImage(userId)
  }
}

Cette façon d'écrire rend inutile de dépendre d'une classe spécifique.

Vous n'avez pas à vous soucier d'appeler un processus dans une classe particulière Toute classe conforme au protocole Vous pouvez rendre disponible le traitement dans le protocole.

En d'autres termes, vous aurez la possibilité de modifier la conception.

De plus, c'est mieux que la méthode conventionnelle de coller des classes ensemble par traitement de rappel. Vous pouvez clairement voir les responsabilités de chaque classe, donc Cela permet de mieux comprendre où et ce qui est traité.

La différence entre orienté protocole et orienté objet, et les avantages de l'orientation protocole J'ai finalement remarqué ici. .. ..

[Source de référence] J'ai étudié la programmation orientée protocole WWDC 2015 "Programmation orientée protocole avec Swift"

Application au traitement asynchrone

La caractéristique du protocole est Le fait est que vous pouvez appeler un processus spécifique sans dépendre d'une classe spécifique.

Ainsi, dans le cycle de vie de CollectionView et TableView Même si vous ne forcez pas l'acquisition de données → dessin de l'interface utilisateur → mise à jour de l'interface utilisateur dans le même thread

Après avoir acquis les données, enregistrez les données, puis appelez le processus de rechargement. Vous pouvez utiliser la méthode de mise à jour de l'interface utilisateur → redessiner l'interface utilisateur.

Prise en charge flexible du traitement asynchrone par conception et protocole d'application Et il semble que l'animation fluide de l'interface utilisateur ne sera pas gâchée.

C'est déjà un protocole génial ... j'ai été impressionné !!

Utilisons davantage le protocole !!

Si vous pouvez utiliser le protocole, non seulement la conception sera facile à comprendre J'ai seulement le sentiment que cela peut être une application iOS qui résiste au changement. J'étais vraiment excité !!

Surtout pour les applications qui effectuent fréquemment des communications asynchrones Le protocole semble être très utile.

Je me ferai plus d'amis avec le protocole à partir de maintenant !!

Ce livrable

(Ceci est juste un exemple d'utilisation de protocole. Veuillez pardonner le fait qu'il ne peut pas être utilisé dans la pratique tel quel) https://github.com/Kohei312/CollectioView_loose-coupling

Ouvrages / articles de référence

Livres

Je voulais le lire plus tôt ... C'est facile à comprendre et je le recommande vraiment! Yoshitaka Seki, Shoshin Fumi, Kenji Tanaka et al. Introduction aux modèles de conception d'applications iOS (2018) PEAKS Publishing

À propos du type de valeur

Swift's Array était vraiment génial Type de valeur pure Swift Problèmes de type et solutions de contournement sans valeur sémantique

À propos du traitement asynchrone

Exemple de traitement asynchrone de Swift (modèles multiples) [Swift] Grand Central Dispatch (GCD) et Operation Queue Summary

À propos du protocole

Qu'est-ce que la programmation orientée protocole Protocole Swift

À propos des notifications d'informations telles que les délégués

[Swift] Développement iOS intégrant DDD Partie 1 ~ Cas d'utilisation et délégué ~ Modèle de notification entre Swift et objets [iOS] Comprenez fermement le délégué iOS Qu'est-ce que Delegate dans Swift et pourquoi l'utiliser [Swift] Flux de mise en œuvre du délégué

Dependency Injection(DI) Injection de dépendances: injection de dépendances Concevez avec Swift pour être facile à tester en utilisant le principe de l'inversion des dépendances

(+α) [Swift] Référence circulaire qui tend à créer une dépendance

Recommended Posts

En utilisant le protocole, le processus de mise à jour de l'interface utilisateur après une communication asynchrone était très facile à comprendre.