Imagine um livro no qual não haja divisão em capítulos, e tudo corra sem quebra lógica e semântica, um livro onde não há parágrafos, pontos e vírgulas, um livro em que a primeira linha fala de uma coisa, a segunda sobre outra, a terceira novamente sobre primeira coisa
Apresentado?
Você poderia entender sobre o que é o livro?
Quão rápido você conseguiu encontrar a passagem em que está interessado?
Seu código, assim como o conteúdo do livro, precisa ser estruturado para facilitar a leitura e transmitir o significado.
Neste artigo, mostrarei exemplos de código de organização em que as classes terão a mesma sequência de blocos principais e sua divisão.
Por conveniência, usarei a palavra classe (classe), mas implica qualquer tipo de tipo (classe, estrutura, enum).
Graças à aplicação dessas dicas, seu código ficará legível, o que no futuro fornecerá comodidade e rapidez no trabalho com ele.
Obviamente, as dicas descritas podem ser atualizadas ao seu gosto, observando os princípios básicos.
Primeiro, vamos comparar o mesmo código de duas maneiras.
Um exemplo de uma classe bagunçada:
| 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! |
| } |
Esse código é semelhante a um despejo de métodos, variáveis e saídas, no qual tudo se funde, é difícil entender a que se refere e em que lugar o que procurar.
Um exemplo de uma classe 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) |
| } |
| |
| } |
Linha vazia 38 - recue uma linha do último método para que você possa ver onde termina o último colchete do método e onde a classe termina.
A mesma funcionalidade é mostrada nos dois exemplos, mas a diferença é que a segunda opção tem uma estrutura clara, devido à qual a lógica é mais óbvia, o código é fácil de ler, você pode encontrar rapidamente o que está procurando nele e, além disso, é bom olhar para ele.
Princípios básicos para a formação de uma estrutura de classe pura:
- Sempre use // MARK: -
- Dê os nomes dos rótulos e defina sua prioridade
- Colocando a lógica fora dos métodos do ciclo de vida em métodos separados
- Usamos extensão para implementar os protocolos
- Selecione elementos logicamente relacionados
- Removemos não utilizados
- Automatizar rotina
1. Sempre use // MARK: -
Para facilitar a leitura, o livro é dividido em capítulos e será mais confortável trabalharmos se criarmos um índice de classe usando
// MARK: - .
Essa tag não apenas se destaca bem de todo o código, mas também cria automaticamente um sumário - destaca as seções do código em negrito na lista de elementos desse arquivo.
Você pode visualizar o índice de um arquivo clicando no botão após a seta direita (>) na parte superior do arquivo, após o nome desse arquivo ou ctr + 6 (menu de itens do documento).2. Damos nomes aos rótulos e estabelecemos sua sequência
Abaixo estão os principais rótulos para dividir o código em blocos conectados logicamente e sua sequência:
| // MARK: - IBOutlets |
| |
| // MARK: - Public Properties |
| |
| // MARK: - Private Properties |
| |
| // MARK: - Initializers |
| |
| // MARK: - Lifecycle |
| |
| // MARK: - Public Methods |
| |
| // MARK: - Private Methods |
| |
| // MARK: - IBActions |
Ao usar esse método de agrupamento, é possível navegar facilmente no código de qualquer classe.
3. Retire os métodos lógicos do ciclo de vida para métodos separados
A lógica dentro dos métodos do ciclo de vida do ViewController deve ser colocada em métodos separados, mesmo se você precisar criar um método com uma linha de código. Hoje é um, e amanhã é dez.
| ❌ 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") |
| } |
Devido ao fato de os detalhes da implementação serem terceirizados para métodos de terceiros, a lógica do ciclo de vida fica mais clara.
4. Use a extensão para implementar protocolos
Retire a implementação do protocolo na extensão 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 |
| } |
| |
| } |
Esse rótulo conterá tudo relacionado a este protocolo - tudo o que está apenas aqui e não há necessidade de ir para outro lugar; caso contrário, os métodos e propriedades do protocolo estarão espalhados por toda a classe.
5. Selecione os elementos logicamente relacionados
Para aumentar a visibilidade, é necessário selecionar elementos relacionados logicamente usando uma linha vazia:
| ❌ 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. Nós removemos não utilizados
Não deixe comentários desnecessários (padrão), métodos vazios ou funcionalidade inoperante - isso obstrui o código. Preste atenção à classe AppDelegate, provavelmente você encontrará métodos vazios lá com comentários 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. Automatize a rotina
Para evitar escrever manualmente em cada classe
// MARK: - SomeMark , use
Code Snippet .
Escrevemos um rótulo, selecionamos e, em seguida, Editor -> Criar trecho de código, atribuímos um nome a ele e chamamos de atalho.// MARCA: - Bônus
- Marque a classe com a palavra-chave final se essa classe não tiver filhos - o projeto é compilado mais rapidamente e o código é executado mais rapidamente.
- Marque propriedades, pontos de venda e métodos com a palavra-chave privada - eles estarão disponíveis apenas dentro da classe e não estarão na lista pública de propriedades e métodos se não forem necessários lá.
Desejo todo o sucesso no desenvolvimento de aplicativos e deixe sua classe mais limpa!
// MARK: - Ajuda na redação de um artigoSergey Pchelyakov
Alexey Pleshkov
AlekseyPleshkov// MARK: - LinksRay wenderlich código estilo