Présentation de Clean Swift Architecture

Bonjour lecteur!

Dans cet article, je parlerai de l'architecture des applications iOS - Clean Swift . Nous allons considérer les principaux points théoriques et analyser un exemple en pratique.



Théorie


Pour commencer, nous analyserons la terminologie de base de l'architecture. Dans Clean Swift, une application se compose de scènes, c'est-à-dire chaque écran d'application est une scène. L'interaction principale dans la scène passe par une boucle séquentielle entre les composants de ViewController -> Interactor -> Presenter . C'est ce qu'on appelle un cycle VIP .

Le pont entre les composants est le fichier Modèles , qui stocke les données transmises. Il existe également un routeur , qui est responsable du transfert et du transfert des données entre les scènes, et Worker , qui reprend une partie de la logique d' Interactor .



Afficher


Storyboards, XIB ou éléments d'interface utilisateur écrits via du code.

ViewController


Responsable uniquement de la configuration et de l'interaction avec View . Le contrôleur ne doit contenir aucune logique métier, interactions réseau, informatique, etc.
Sa tâche consiste à traiter des événements avec Visualiser , afficher ou envoyer des données (sans traitement ni vérification) dans Interactor .

Interactractor


Il contient la logique métier de la scène.

Il fonctionne avec les modules réseau, base de données et appareils.

Interactor reçoit une demande de ViewController (avec des données ou vide), la traite et, si nécessaire, transfère de nouvelles données à Presenter .

Présentateur


Il est engagé dans la préparation des données à afficher.

Par exemple, ajoutez un masque à un numéro de téléphone ou faites la première lettre dans la capitale du titre.
Il traite les données reçues d' Interactor , puis les renvoie au ViewController .

Les modèles


Un ensemble de structures pour transférer des données entre les composants du cycle VIP . Chaque cercle du cycle a 3 types de structures:

  • Demande - Structure de données (texte de TextField, etc.) pour le transfert de ViewController à Interactor
  • Réponse - Structure avec des données (téléchargées du réseau, etc.) pour le transfert d' Interactor à Presenter
  • ViewModel - Structure avec des données traitées (formatage de texte, etc.) dans Presenter pour un retour à ViewController

Ouvrier


Décharge Interactor , prenant une partie de la logique métier de l'application si Interactor se développe rapidement.

Vous pouvez également créer des travailleurs communs à toutes les scènes, si leur fonctionnalité est utilisée dans plusieurs scènes.

Par exemple, dans Worker, vous pouvez définir la logique de travail avec un réseau ou une base de données.

Routeur


Toute la logique responsable des transitions et du transfert de données entre les scènes est supprimée dans le routeur .



Pour clarifier l'image du cycle VIP , je vais donner un exemple standard: l'autorisation.

  1. L'utilisateur a entré son nom d'utilisateur et son mot de passe, cliqué sur le bouton d'autorisation
  2. Le ViewController déclenche une IBAction , après quoi une structure est créée avec les données utilisateur entrées dans TextFields, (Modèles -> Demande)
  3. La structure créée est passée à la méthode fetchUser dans Interactor'e
  4. L'interacteur envoie une demande au réseau et reçoit une réponse sur le succès de l'autorisation
  5. Sur la base des données reçues, crée une structure avec le résultat (Modèles -> Réponse) et est passée à la méthode presentUser dans Presenter'e
  6. Presenter formate les données selon les besoins et les renvoie (Modèles -> ViewModel) à la méthode displayUser dans ViewController'e
  7. ViewController affiche les données reçues à l'utilisateur. Dans le cas d'une autorisation, une erreur peut s'afficher ou une transition vers une autre scène peut être déclenchée à l'aide du routeur

Ainsi, nous obtenons une structure unique et cohérente, avec la répartition des responsabilités en composants.

Pratique


Voyons maintenant un petit exemple pratique qui montre comment se déroule le cycle VIP . Dans cet exemple, nous simulerons le chargement des données lors de l'ouverture d'une scène (écran). J'ai marqué les principales sections du code avec des commentaires.

L'ensemble du cycle VIP est lié à des protocoles, ce qui permet de remplacer n'importe quel module sans perturber l'application.
Un protocole DisplayLogic est créé pour ViewController , un lien vers lequel est transmis à Presenter pour un rappel ultérieur. Pour Interactor , deux protocoles BusinessLogic sont créés, qui sont chargés d'appeler les méthodes de ViewController et DataSource , de stocker les données et de transférer une autre scène via le routeur vers Interactor . Le présentateur souscrit au protocole PresentationLogic pour appeler depuis Interactor . L'élément de connexion de tout cela est les modèles . Il contient des structures à l'aide desquelles des informations sont échangées entre les composants du cycle VIP . Nous allons commencer l'analyse du code avec.



Les modèles


Dans l'exemple ci-dessous, pour la scène Home , j'ai créé un fichier HomeModels qui contient un ensemble de requêtes pour la boucle VIP .
La demande FetchUser sera responsable du chargement des données utilisateur, que nous examinerons plus loin.

// Models
/// VIP
enum HomeModels {
/// VIP
enum FetchUser {
/// Interactor View Controller
struct Request {
let userName: String
}
/// Presentor Interactor
struct Response {
let userPhone: String
let userEmail: String
}
/// View Controller Presentor
struct ViewModel {
let userPhone: String
let userEmail: String
}
}
}

ViewController


Lorsque la classe est initialisée, nous instancions les classes Interactor et Presenter de cette scène et établissons des dépendances entre elles.
Plus loin dans ViewController'e, il n'y a qu'un lien vers Interactor . En utilisant ce lien, nous allons créer une demande à la méthode fetchUser (request :) dans Interactor pour démarrer le cycle VIP .

Ici, il convient de prêter attention à la façon dont la demande à Interactor se produit. Dans la méthode loadUserInfromation () , nous créons une instance de la structure Request , où nous transmettons la valeur initiale. Il peut être extrait de TextField , de tableaux, etc. Une instance de la structure Request est transmise à la méthode fetchUser (request :) , qui se trouve dans le protocole BusinessLogic de notre Interactor .

// ViewController
///
protocol HomeDisplayLogic: class {
///
func displayUser(_ viewModel: HomeModels.FetchUser.ViewModel)
}
final class HomeViewController: UIViewController {
/// Interactor'a
var interactor: HomeBusinessLogic?
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
///
private func setup() {
// VIP
let interactor = HomeInteractor()
let presenter = HomePresenter()
//
interactor.presenter = presenter
presenter.viewController = self
// Interactor View Controller
self.interactor = interactor
}
override func viewDidLoad() {
super.viewDidLoad()
//
fetchUser()
}
/// Interactor
private func loadUserInfromation() {
// Interactor
let request = HomeModels.FetchUser.Request(userName: "Aleksey")
// Interactor'a
interactor?.fetchUser(request)
}
}
/// HomeDisplayLogic
extension HomeViewController: HomeDisplayLogic {
func displayUser(_ viewModel: HomeModels.FetchUser.ViewModel) {
print(viewModel)
}
}

Interactractor


Une instance de la classe Interactor contient un lien vers le protocole PresentationLogic , sous lequel Presenter est signé.

La méthode fetchUser (request :) peut contenir n'importe quelle logique de chargement de données. Par exemple, je viens de créer des constantes, avec des données prétendument obtenues.

Dans la même méthode, une instance de la structure Response est créée et remplie avec les paramètres obtenus précédemment. La réponse est transmise à PresentationLogic à l'aide de la méthode presentUser (response :) . En d'autres termes, nous avons obtenu ici les données brutes et les avons transmises à Presenter pour traitement.

// Interactor
/// Interactor'a
protocol HomeBusinessLogic: class {
///
func fetchUser(_ request: HomeModels.FetchUser.Request)
}
final class HomeInteractor: HomeBusinessLogic {
///
var presenter: HomePresentationLogic?
func fetchUser(_ request: HomeModels.FetchUser.Request) {
//
//
let userPhone = "+7 (999) 111-22-33"
let userEmail = "im@alekseypleshkov.ru"
// ...
// Presentor'
let response = HomeModels.FetchUser.Response(userPhone: userPhone, userEmail: userEmail)
// Presentor'
presenter?.presentUser(response)
}
}

Présentateur


Il a un lien vers le protocole DisplayLogic , sous lequel ViewController est signé. Il ne contient aucune logique métier, mais formate uniquement les données reçues avant de les afficher. Dans l'exemple, nous avons formaté le numéro de téléphone, préparé une instance de la structure ViewModel et l' avons transmise au ViewController à l'aide de la méthode displayUser (viewModel :) du protocole DisplayLogic , où les données sont déjà affichées.

///
protocol HomePresentationLogic: class {
/// Interactor'a
func presentUser(_ response: HomeModels.FetchUser.Response)
}
final class HomePresenter: HomePresentationLogic {
/// View Controller'a
weak var viewController: HomeDisplayLogic?
func presentUser(_ response: HomeModels.FetchUser.Response) {
//
let formattedPhone = response.userPhone.replacingOccurrences(of: "-", with: " ")
// ViewModel View Controller
let viewModel = HomeModels.FetchUser.ViewModel(userPhone: formattedPhone, userEmail: response.userEmail)
// View Controller'a
viewController?.displayUser(viewModel)
}
}

Conclusion


Avec cette architecture, nous avons eu l'opportunité de répartir les responsabilités, d'améliorer la commodité du test de l'application, d'introduire la remplaçabilité des sections individuelles de l'implémentation et la norme d'écriture de code pour travailler en équipe.

Merci d'avoir lu jusqu'au bout.

Série d'articles


  1. Comprendre l'architecture Swift propre (vous êtes ici)
  2. Routeur et transmission de données dans une architecture propre et rapide
  3. Travailleurs de l'architecture propre et rapide
  4. Tests unitaires dans l'architecture Clean Swift
  5. Un exemple d'une architecture de boutique en ligne simple Clean Swift

Toutes les composantes de la scène: Lien
Aide à la rédaction d'un article: Bastien

Source: https://habr.com/ru/post/fr453986/


All Articles