Routeur et données passant une architecture propre et rapide

Bonjour lecteur!

Dans un article précédent , j'ai parlé du cycle VIP de l'architecture Clean Swift . Nous allons maintenant aborder l'un des sujets les plus importants - la transition et le transfert de données entre les scènes.



Théorie


Le composant Router est responsable de la logique de navigation et de transfert de données, qui fait partie de la scène (facultatif bien sûr). Il est initialisé dans le ViewController , avec l' Interactor et le Presenter .

Le routeur implémente deux protocoles - RoutingLogic et DataPassing , que nous remplirons de nos fonctionnalités. RoutingLogic doit contenir les méthodes responsables de la transition vers une scène spécifique. DataPassing contient la variable dataStore , qui fait référence au Protocol DataStore . Interactor Scenes implémente le protocole DataStore et fonctionne avec les variables qui y sont stockées. Le routeur lui-même contient un lien vers le ViewController de sa scène.

En utilisant le lien vers le ViewController , le routeur saute entre les scènes. Pour ce faire, vous pouvez utiliser Segue ou créer une scène vers laquelle vous souhaitez effectuer une transition, par programme. La méthode utilisée n'est pas importante, l'essentiel pour nous est d'avoir un lien vers une instance de la classe ViewController vers laquelle nous basculons .

En utilisant le lien vers le DataStore, nous transférerons les données de l' interacteur d' une scène vers l' interacteur de la scène vers laquelle nous basculons. Et, comme mentionné précédemment, c'est le routeur qui doit savoir comment procéder.

Pratique


Par exemple, nous allons transférer le texte de TextField vers Label d'une autre scène. Considérons deux façons de transition entre les scènes - selon Segue et par programme.

La classe Router contient 3 groupes sémantiques de méthodes:

  1. Méthodes de l' implémentation de RoutingLogic (routeTo)
  2. Méthodes responsables de la navigation (naviguer vers, transition sans Segue)
  3. Méthodes de transfert de données (passDataTo, s'il existe des données à transférer)



Si nous effectuons une transition via Segue , par exemple, lorsqu'un bouton est cliqué, alors dans ViewController, nous devons remplacer la méthode prepare (for: sender :). Cette extension vous permettra d'appeler automatiquement des méthodes depuis le routeur sous le nom de Segue .

Remplacer préparer (pour: expéditeur :) est facultatif lorsque vous travaillez avec Segue. Vous pouvez l'exclure du code et appeler performSegue (withIdentifier: sender :) dans la méthode Router . La préparation n'est nécessaire que si vous devez utiliser Segue avec le transfert de données.

final class HomeViewController: UIViewController {
// ...
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Segue
if let scene = segue.identifier {
// , Router
// router?.routeToNAME(segue:)
let selector = NSSelectorFromString("routeTo\(scene)WithSegue:")
// ,
// Segue
if let router = router, router.responds(to: selector) {
router.perform(selector, with: segue)
}
}
}
// ...
}


Maintenant, nous arrivons enfin à la chose la plus intéressante - le code du routeur . L'exemple contient des commentaires, nous ne considérerons donc que les points clés.

Dans ce routeur, nous travaillons avec deux scènes - Home et Detail . La transition de la scène d' accueil est également gérée de deux manières - par Segue et par programme . Les données sont transférées de la scène d' accueil à la scène de détail .

Toutes les méthodes du protocole RoutingLogic doivent être nommées selon le principe routeToNAME , où NAME est le nom du Segue (identifiant) que nous spécifions lorsque nous travaillons avec le Storyboard . Cela est nécessaire non seulement pour la commodité et la beauté de l'utilisation, mais aussi pour notre sélecteur de méthode dans prepare (pour: sender :) ViewController , que nous avons redéfini plus tôt.

Dans la classe HomeRouter , il existe également des méthodes commençant par naviguerTo et passDataTo . Les premiers sont responsables de la logique de transition, tandis que les seconds sont responsables du transfert des données. Les méthodes naviguer vers ne sont créées que si la transition est effectuée par programme.

Dans l'exemple, nous avons une méthode routeToDetail (segue :) . Le paramètre segue est facultatif car la méthode contient une implémentation qui vous permet de l'appeler sans utiliser Segue . Dans les deux cas de transition, nous obtenons des valeurs non facultatives de notre scène HomeViewController et HomeDataStore'a Home , ainsi que des liens vers la scène ViewController et DataStore Detail . Ici, il convient de prêter attention au fait que detailDS est une variable et est passée à la méthode passDataToDetail en utilisant le paramètre pass -through (inout). Ceci est important car sans inout, nous devrons marquer tous les protocoles DataStore comme «possible à implémenter uniquement avec des classes» (protocole DetailDataStore: classe), ce qui entraîne de nombreuses difficultés, notamment la capture de liens forts.

import UIKit
/// @objc
/// prepare View Controller'e
@objc protocol HomeRoutingLogic {
/// Detail View Controller
func routeToDetail(segue: UIStoryboardSegue?)
}
protocol HomeDataPassing {
var dataStore: HomeDataStore? { get }
}
final class HomeRouter: NSObject, HomeRoutingLogic, HomeDataPassing {
// MARK: - Private
// MARK: - Public
weak var viewController: HomeViewController?
var dataStore: HomeDataStore?
// MARK: - HomeRoutingLogic
func routeToDetail(segue: UIStoryboardSegue?) {
if let segue = segue {
//
// Segue
// Detail View Controller
// Data Store Router'e
guard
let homeDS = dataStore,
let detailVC = segue.destination as? DetailViewController,
var detailDS = detailVC.router?.dataStore
else { fatalError("Fail route to detail") }
// , , ,
// ""
passDataToDetail(source: homeDS, destination: &detailDS)
} else {
// ,
// Segue
// Detail View Controller Storyboard'a
// Data Store Router'e
guard
let viewController = viewController,
let homeDS = dataStore,
let storyboard = viewController.storyboard,
let detailVC = storyboard.instantiateViewController(withIdentifier: "Detail") as? DetailViewController,
var detailDS = detailVC.router?.dataStore
else { fatalError("Fail route to detail") }
passDataToDetail(source: homeDS, destination: &detailDS)
// , ""
navigateToDetail(source: viewController, destination: detailVC)
}
}
// MARK: - Navigation
private func navigateToDetail(source: HomeViewController, destination: DetailViewController) {
source.navigationController?.pushViewController(destination, animated: true)
}
// MARK: - Passing data
/// destination inout,
/// Data Store
private func passDataToDetail(source: HomeDataStore, destination: inout DetailDataStore) {
// HomeDataStore DetailDataStore
destination.message = source.message
}
}


Conclusion


C’est tout. Merci d'avoir lu jusqu'au bout! Ci-dessous, je laisserai un lien vers le projet si vous souhaitez essayer l'article en action.

Série d'articles


  1. Présentation de Clean Swift Architecture
  2. Routeur et passage de données dans une architecture Clean Swift (vous êtes ici)
  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

Lien vers le projet
Aide à la rédaction d'un article: Bastien

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


All Articles