Imagine un libro en el que no hay división en capítulos, y todo funciona sin un desglose lógico y semántico, un libro donde no hay párrafos, sin puntos y comas, un libro donde la primera línea habla sobre una cosa, la segunda sobre otra, la tercera nuevamente sobre Lo primero
Presentado?
¿Podrías entender de qué trata el libro?
¿Qué tan rápido podría encontrar el pasaje que le interesa?
Su código, así como los contenidos del libro, deben estructurarse para que el código sea fácil de leer y transmita el significado que contiene.
En este artículo mostraré ejemplos de código de organización en el que las clases tendrán la misma secuencia de bloques principales y su desglose.
Por conveniencia, usaré la palabra clase (clase), pero implicaré cualquier tipo de tipo (clase, estructura, enumeración).
Gracias a la aplicación de estos consejos, su código se volverá legible, lo que en el futuro proporcionará comodidad y velocidad de trabajo con él.
Por supuesto, los consejos descritos se pueden actualizar a su gusto, observando los principios básicos.
Primero, comparemos el mismo código de dos maneras.
Un ejemplo de una clase desordenada:
| final class MessyViewController: UIViewController { |
| |
| private let userService = UserService() |
| var userID: String? |
| private var userList: [User]? |
| |
| @IBOutlet private weak var searchBar: UISearchBar! |
| |
| weak var delegate: SomeDelegate? |
| |
| @IBAction private func cancelButtonPressed(_ sender: UIBarButtonItem) { |
| dismiss(animated: true, completion: nil) |
| } |
| |
| override func viewDidLoad() { |
| super.viewDidLoad() |
| // Do any additional setup after loading the view. |
| navigationController?.navigationBar.backgroundColor = .red |
| navigationItem.title = "Some" |
| } |
| @IBOutlet private weak var tableView: UITableView! |
| } |
Este código es similar a un volcado de métodos, variables y salidas, en el que todo se fusiona, es difícil entender a qué se refiere y en qué lugar buscar.
Un ejemplo de una clase pura:
| final class CleanViewController: UIViewController { |
| |
| // MARK: - IBOutlets |
| |
| @IBOutlet private weak var searchBar: UISearchBar! |
| @IBOutlet private weak var tableView: UITableView! |
| |
| // MARK: - Public Properties |
| |
| var userID: String? |
| weak var delegate: SomeDelegate? |
| |
| // MARK: - Private Properties |
| |
| private let userService = UserService() |
| private var userList: [User]? |
| |
| // MARK: - Lifecycle |
| |
| override func viewDidLoad() { |
| super.viewDidLoad() |
| |
| setupNavigationBar() |
| } |
| |
| // MARK: - Private Methods |
| |
| private func setupNavigationBar() { |
| navigationController?.navigationBar.backgroundColor = .red |
| navigationItem.title = "Some" |
| } |
| |
| // MARK: - IBActions |
| |
| @IBAction private func cancelButtonPressed(_ sender: UIBarButtonItem) { |
| dismiss(animated: true, completion: nil) |
| } |
| |
| } |
Línea vacía 38: sangra una línea del último método para que puedas ver dónde termina el último corchete de cierre del método y dónde termina la clase.
La misma funcionalidad se muestra en ambos ejemplos, pero la diferencia es que la segunda opción tiene una estructura clara, debido a que la lógica es más obvia, el código es fácil de leer, puede encontrar rápidamente lo que está buscando en él y, además, es agradable verlo.
Principios básicos para la formación de una estructura de clase pura:
- Siempre use // MARK: -
- Dé los nombres de las etiquetas y establezca su prioridad.
- Poner la lógica de los métodos del ciclo de vida en métodos separados
- Usamos extensión para implementar los protocolos
- Seleccionar elementos relacionados lógicamente
- Eliminamos sin usar
- Automatizar rutina
1. Utilice siempre // MARK: -
Para facilitar la lectura, el libro está dividido en capítulos, y nos resultará más cómodo trabajar si creamos una tabla de contenido de clase usando
// MARK: - .
Esta etiqueta no solo se destaca bien de todo el código, sino que también crea automáticamente una tabla de contenido: resalta las secciones del código en negrita en la lista de elementos de este archivo.
Puede ver la tabla de contenido de un archivo haciendo clic en el botón después de la flecha derecha (>) en la parte superior del archivo después del nombre de este archivo o ctr + 6 (menú de elementos del documento).2. Damos nombres de etiquetas y establecemos su secuencia
A continuación se encuentran las etiquetas principales para dividir el código en bloques conectados lógicamente y su secuencia:
| // MARK: - IBOutlets |
| |
| // MARK: - Public Properties |
| |
| // MARK: - Private Properties |
| |
| // MARK: - Initializers |
| |
| // MARK: - Lifecycle |
| |
| // MARK: - Public Methods |
| |
| // MARK: - Private Methods |
| |
| // MARK: - IBActions |
Al usar este método de agrupación, uno puede navegar fácilmente en el código de cualquier clase.
3. Extraiga la lógica de los métodos del ciclo de vida en métodos separados
La lógica dentro de los métodos del ciclo de vida de ViewController debe ponerse en métodos separados, incluso si tiene que crear un método con una línea de código. Hoy es uno y mañana son las diez.
| ❌ NOT Preferred |
| |
| override func viewDidLoad() { |
| super.viewDidLoad() |
| |
| navigationController?.navigationBar.backgroundColor = .red |
| someButton.layer.cornerRadius = 10 |
| someButton.layer.masksToBounds = true |
| navigationItem.title = "Some" |
| print("Some") |
| } |
| |
| |
| ✅ Preferred |
| |
| // MARK: - Lifecycle |
| |
| override func viewDidLoad() { |
| super.viewDidLoad() |
| |
| setupNavigationBar() |
| setupSomeButton() |
| printSome() |
| } |
| |
| |
| // MARK: - Private Methods |
| |
| private func setupNavigationBar() { |
| navigationController?.navigationBar.backgroundColor = .red |
| navigationItem.title = "Some" |
| } |
| |
| private func setupSomeButton() { |
| someButton.layer.cornerRadius = 10 |
| someButton.layer.masksToBounds = true |
| } |
| |
| private func printSome() { |
| print("Some") |
| } |
Debido al hecho de que los detalles de implementación se subcontratan a métodos de terceros, la lógica del ciclo de vida se vuelve más clara.
4. Use la extensión para implementar protocolos
Saque la implementación del protocolo en la extensión marcada
// MARK: - SomeProtocol :
| ❌ NOT Preferred |
| |
| final class CleanViewController: UIViewController, UITableViewDataSource, UITableViewDelegate { |
| |
| // all methods |
| } |
| |
| |
| ✅ Preferred |
| |
| final class CleanViewController: UIViewController { |
| |
| // class stuff here |
| |
| } |
| |
| |
| // MARK: - Table View Data Source |
| extension CleanViewController: UITableViewDataSource { |
| |
| func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { |
| |
| return userList?.count ?? 0 |
| } |
| |
| func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { |
| |
| let cell = UITableViewCell() |
| return cell |
| } |
| |
| } |
Esta etiqueta contendrá todo lo relacionado con este protocolo: todo lo que solo está aquí y no es necesario ir a ningún otro lado; de lo contrario, los métodos y propiedades del protocolo se distribuirán por toda la clase.
5. Seleccione los elementos relacionados lógicamente
Para aumentar la visibilidad, es necesario seleccionar elementos relacionados lógicamente usando una línea vacía:
| ❌ NOT Preferred |
| |
| private func showActivityIndicator(on viewController: UIViewController) { |
| activityIndicator.center = viewController.view.center |
| loadingView.backgroundColor = #colorLiteral(red: 0, green: 0, blue: 0, alpha: 1) |
| loadingView.alpha = 0.5 |
| activityIndicator.hidesWhenStopped = true |
| activityIndicator.style = .whiteLarge |
| loadingView.center = viewController.view.center |
| loadingView.clipsToBounds = true |
| loadingView.layer.cornerRadius = 15 |
| viewController.view.addSubview(loadingView) |
| viewController.view.addSubview(activityIndicator) |
| activityIndicator.startAnimating() |
| } |
| |
| |
| ✅ Preferred |
| |
| private func showActivityIndicator(on viewController: UIViewController) { |
| activityIndicator.center = viewController.view.center |
| activityIndicator.hidesWhenStopped = true |
| activityIndicator.style = .whiteLarge |
| |
| loadingView.center = viewController.view.center |
| loadingView.backgroundColor = #colorLiteral(red: 0, green: 0, blue: 0, alpha: 1) |
| loadingView.alpha = 0.5 |
| loadingView.clipsToBounds = true |
| loadingView.layer.cornerRadius = 15 |
| |
| viewController.view.addSubview(loadingView) |
| viewController.view.addSubview(activityIndicator) |
| |
| activityIndicator.startAnimating() |
| } |
6. Eliminamos sin usar
No deje comentarios innecesarios (por defecto), métodos vacíos o funcionalidad inactiva; esto obstruye el código. Presta atención a la clase AppDelegate, lo más probable es que encuentres métodos vacíos allí con comentarios dentro.
| ❌ NOT Preferred |
| |
| @UIApplicationMain |
| class AppDelegate: UIResponder, UIApplicationDelegate { |
| |
| var window: UIWindow? |
| |
| |
| func application( |
| _ application: UIApplication, |
| didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { |
| // Override point for customization after application launch. |
| return true |
| } |
| // |
| // func someFunc() { |
| // print("Some") |
| // } |
| |
| func applicationWillResignActive(_ application: UIApplication) { |
| // Sent when the application is about to move from active to inactive state. This can occur for certain |
| //types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits |
| //the application and it begins the transition to the background state. |
| // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. |
| } |
| |
| |
| ✅ Preferred |
| |
| @UIApplicationMain |
| class AppDelegate: UIResponder, UIApplicationDelegate { |
| |
| var window: UIWindow? |
| |
| |
| func application( |
| _ application: UIApplication, |
| didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { |
| |
| return true |
| } |
| |
| } |
7. Automatizar la rutina
Para evitar escribir manualmente en cada clase
// MARK: - SomeMark , use
Code Snippet .
Escribimos una etiqueta, la seleccionamos, luego Editor -> Crear fragmento de código, le damos un nombre y lo llamamos atajo.// MARCA: - Bonificación
- Marque la clase con la palabra clave final si esta clase no tiene hijos: el proyecto se compila más rápido y el código se ejecuta más rápido.
- Marque las propiedades, salidas y métodos con la palabra clave privada : estarán disponibles solo dentro de la clase y no estarán en la lista pública de propiedades y métodos si no se necesitan allí.
¡Le deseo mucho éxito en el desarrollo de aplicaciones y deje que su clase se vuelva más limpia!
// MARK: - Ayuda para escribir un artículoSergey Pchelyakov
Alexey Pleshkov
AlekseyPleshkov// MARK: - EnlacesEstilo de código Ray wenderlich