📜 ⬆️ ⬇️

Raise code readability in iOS development

Imagine a book in which there is no division into chapters, and everything goes without logical and semantic breakdown, a book with no paragraphs, no points and commas, a book where the first line tells about one thing, the second about the other, and the third again about the first.

Submitted?

Could you understand what the book is about?
')
How quickly could you find the passage you are interested in?

Your code, as well as the content of the book, needs to be structured so that the code is easily readable and conveys its meaning.

In this article, I will show examples on the organization of code in which classes will have the same sequence of basic blocks and their breakdown.

For convenience, I will use the word class (class), but imply any kind of type (class, struct, enum).

Thanks to the use of these tips, your code will become readable, which will further ensure the convenience and speed of working with it.

Of course, the described tips can be upgraded to your liking, observing the basic principles.

To begin with, let's compare the same code in two versions.

An example of a messy class:


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!
}

This code is similar to a dump of methods, variables and outlets, in which everything merges together, it is difficult to understand what is related to what and in what place.

An example of a pure class:


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)
}
}

Empty line 38 - indent one line from the last method so that you can see where the last closing method bracket ends and where the class itself ends.

In both examples, the same functionality is given, but the difference is that the second option has a clear structure, due to which the logic is more obvious, the code is easy to read, you can quickly find what you are looking for, and besides, it is just nice to look at it.

The basic principles for the formation of a pure class structure:


  1. Always use // MARK: -
  2. Give names of tags and set their order.
  3. Moving logic out of lifecycle methods to individual methods
  4. We use extension for implementation of protocols
  5. Select logically related elements.
  6. We remove the unused
  7. Automating routine

1. Always use // MARK: -


For readability, the book is divided into chapters, and so it will be more comfortable for us to work if we create a table of contents for the class using // MARK: - .

This label is not only well distinguished from the entire code, but also automatically creates a table of contents - it highlights the sections in the code in bold in the list of elements of this file.


You can view the file's contents by clicking on the button after the arrow to the right (>) at the very top of the file after the name of this file or ctr + 6 (document items menu).

2. We give the names of the tags and set their order.


Below are the main labels for breaking the code into logically related blocks and their sequence:

// MARK: - IBOutlets
// MARK: - Public Properties
// MARK: - Private Properties
// MARK: - Initializers
// MARK: - Lifecycle
// MARK: - Public Methods
// MARK: - Private Methods
// MARK: - IBActions
view raw MarkList.swift hosted with ❤ by GitHub

When using this method of grouping, you can easily navigate the code of any class.

3. We carry out the logic of the life cycle methods into separate methods


The logic inside the ViewController'a life cycle methods must be moved to separate methods, even if you have to create a method with one line of code. Today is one, and tomorrow is ten.

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")
}

Due to the fact that the implementation details are made to third-party methods, the logic of the life cycle becomes clearer.

4. Use extension to implement protocols


Take out the implementation of the protocols in the extension marked // 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
}
}
view raw extension.swift hosted with ❤ by GitHub

On this label everything that is connected with this protocol will lie - all that is only here and there is no need to go anywhere else, otherwise the methods and properties of the protocol will be scattered throughout the class.

5. Select the logically related elements.


To increase visibility, it is necessary to select logically related elements using an empty string:

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. We remove the unused


Do not leave unnecessary comments (default), empty methods or dead functionality - this is littering the code. Pay attention to the class AppDelegate, most likely you will find there empty methods with comments inside.

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
}
}
view raw AppDelegate.swift hosted with ❤ by GitHub


7. Automate the routine


In order not to write manually in each class // MARK: - SomeMark , use Code Snippet .


We write the label, select it, then Editor -> Create Code Snippet, give it a name and call it shortcuts.

// MARK: - Bonus


  1. Mark a class with the final keyword if the given class has no successors - the project compiles faster, and the code works faster.
  2. Mark properties, outlets and methods with the private keyword - they will be available only inside the class and will not be in the public list of properties and methods if they are not needed there.


I wish you all success in developing applications and let your class become cleaner!

// MARK: - Help in writing an article
Sergey Pchelyakov
Alexey Pleshkov medium/@AlekseyPleshkov

// MARK: - Links
Ray Wenderlich code style

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


All Articles