[SWIFT] Improving hard-to-read code with readable code ~ General

Hi, this is Nekokichi ( @ nekokichi1_yos2 ).

This is the 4th readable code improvement series.

(Please refer to the following for more information) Improving hard-to-read code with readable code ~ Preparation [Improving hard-to-read code with readable code ~ Name] (https://qiita.com/NEKOKICHI2/items/dc774bd79b1623032712) Improving hard-to-read code with readable code ~ Beauty Improving hard-to-read code with readable code ~ Comments

** This time, we will improve the whole code. ** **

Teaching of readable code

"Align conditional expressions" ・ Use affirmative form for conditions ・ Write easy-to-understand conditions first

"The ternary operator is nothing amazing" ・ The code can be shortened, but it is difficult to read.

"Return function early" ・ Prepare multiple exits ・ The code does not become redundant

"Nesting is simple and simple" ・ The deeper the nest, the burden on the reader ・ It is necessary to always remember conditional expressions and variables, and it is necessary to concentrate on understanding the inside of the nest.

"Explanatory variables, summary variables" -Explanatory variable: Indicates what kind of value the variable name is. -Summary variable: Collect huge values and expressions with variables

"Abuse of short-circuit evaluation" ・ Simply less code is not good ・ It is desirable that the same content is easy to understand.

"Split a huge formula" · Use summary variables if the same expression appears multiple times ・ Advantages: Phenomenon of typo, shorter code, easier to correct

"Delete variable" · Do I need to use variables? Consider ・ No need if you can understand easily without assigning to variables

"Reduce the scope of variables" ・ Wide scope makes it difficult to track where changes have been made. ・ Reduce the scope from global to local -Use access modifiers (private, public, static)

"Lower the position of the definition" -If you divide it into definition and processing, you always need to know the value of the variable. ・ If you set the definition and processing as a set, you can find the necessary variables and make them easier to read.

"Write variables only once" ・ If the number of changes is large, the current value will not be known. -Use let or private to prohibit permanent changes ・ Reduce the number of changes due to explanatory variables, etc.

Divide a huge formula

I am converting the attributedText of memoTextView to Data (), but it is dangerous to assign the UI value as it is, and the property name is long, so I put it in the variable inputAttrText in the guardlet statement.

AddMemo.swift


let attributedMemoData = try! NSKeyedArchiver.archivedData(withRootObject: memoTextView.attributedText!, requiringSecureCoding: false)

AddMemo.swift


guard let inputAttrText = memoTextView.attributedText else { return }
let attributedMemoData = try! NSKeyedArchiver.archivedData(withRootObject: inputAttrText, requiringSecureCoding: false)

The value is assigned to the transition destination variable in prepare ().

I am referring to the selected row th value of memoTableView, but since the property name is too long and difficult to see, I prepared a variable indexPathRow and shortened the length of the expression to be assigned.

ViewController.swift


let vc = segue.destination as! DisplayMemo
vc.selectedMemoObject   = memoListForRealm[memoTableView.indexPathForSelectedRow!.row]
vc.selectedMemoString   = memoList[memoTableView.indexPathForSelectedRow!.row]
vc.selectedIndexPathRow = memoTableView.indexPathForSelectedRow!.row

ViewController.swift


let vc = segue.destination as! DisplayMemo
guard let indexPathRow = memoTableView.indexPathForSelectedRow?.row else {
    return
}
vc.selectedMemoObject   = memoListForRealm[indexPathRow]
vc.selectedMemoString   = memoList[indexPathRow]
vc.selectedIndexPathRow = indexPathRow

Variables and readability

Delete unnecessary variables

Assigning a value to the property of the variable memoObject.

However, I assigned the inputAttrText converted to Data () to the variable attributedMemoData, but I deleted the variable attributedMemoData because passing it directly can reduce the code by one sentence rather than passing it through the variable.

AddMemo.swift


@IBAction func addMemo(_ sender: Any) {
    guard let inputAttrText = memoTextView.attributedText else { return }
    let memoObject             = MemoModel()
    // memoTextView.attributedText -> Data()
    let attributedMemoData     = try! NSKeyedArchiver.archivedData(withRootObject: inputAttrText, requiringSecureCoding: false)
    memoObject.data            = attributedMemoData
    memoObject.identifier      = String().randomString()

AddMemo.swift


@IBAction func addMemo(_ sender: Any) {
    guard let inputAttrText = memoTextView.attributedText else { return }
    let memoObject             = MemoModel()
    // memoTextView.attributedText -> Data()
    memoObject.data            = try! NSKeyedArchiver.archivedData(withRootObject: inputAttrText, requiringSecureCoding: false)
    memoObject.identifier      = String().randomString()

When I swipe to delete a cell, I delete the values of the array used for Realm and tableView.

However, Realm erased the unnecessary processing because the change is reflected by manipulating the variable specified in Results .

ViewController.swift


func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
    let selectedMemo = memoListForRealm[indexPath.row]

    //Realm-Delete
    try! realm.write() {
        realm.delete(selectedMemo)
        realm.delete(memoListForRealm[indexPath.row])
    }

ViewController.swift


func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {

    //Realm-Delete
    try! realm.write() {
        realm.delete(memoListForRealm[indexPath.row])
    }

Reduce the scope of variables

private is an access modifier that restricts references (gets) and changes (sets).

By giving private, you can prevent it from being used by other files and functions, and you will not have to worry about changes and references in other files.

Also private (This variable is not used in other files) Can be explicitly shown, eliminating the hassle of tracking the code.

So, whenever possible, I've added private to all variables and functions.

It is not necessary to add private to @IBAction, but if there is no qualifier, I think that it is executed somewhere, so I dared to add it.

ViewController.swift


@IBOutlet weak var memoTableView: UITableView!

//Memorandum for saving to Realm
var memoListForRealm:Results<MemoModel>!
// Realm
let realm               = try! Realm()
//Memo list
var memoList            = [NSAttributedString]()

ViewController.swift


@IBOutlet private weak var memoTableView: UITableView!

//Memorandum for saving to Realm
private var memoListForRealm:Results<MemoModel>!
// Realm
private let realm               = try! Realm()
//Memo list
private var memoList            = [NSAttributedString]()

AddMemo.swift


let realm       = try! Realm()
let imagePicker = UIImagePickerController()

@IBAction func addMemo(_ sender: Any) {
    
@IBAction func attachImageGesture(_ sender: UILongPressGestureRecognizer) {

AddMemo.swift


private let realm       = try! Realm()
private let imagePicker = UIImagePickerController()

@IBAction private func addMemo(_ sender: Any) {

@IBAction private func attachImageGesture(_ sender: UILongPressGestureRecognizer) {

EditMemo.swift


@IBOutlet weak var memoTextView: UITextView!

// Realm
let realm                       = try! Realm()
let imagePicker                 = UIImagePickerController()

@IBAction func attachImageGesture(_ sender: UILongPressGestureRecognizer) {

@IBAction func updateMemo(_ sender: Any) {

EditMemo.swift


@IBOutlet private weak var memoTextView: UITextView!

// Realm
private let realm                       = try! Realm()
private let imagePicker                 = UIImagePickerController()

@IBAction private func attachImageGesture(_ sender: UILongPressGestureRecognizer) {

@IBAction private func updateMemo(_ sender: Any) {

DisplayMemo.swift


@IBOutlet weak var memoTextView: UITextView!

DisplayMemo.swift


@IBOutlet private weak var memoTextView: UITextView!

final is a qualifier that limits inheritance and overrides.

Explicitly indicate that this class cannot be inherited or overridden.

class ViewController: UIViewController,UITableViewDelegate,UITableViewDataSource {
class AddMemo: UIViewController {
class DisplayMemo: UIViewController {
class EditMemo: UIViewController {

final class ViewController:UIViewController,UITableViewDelegate,UITableViewDataSource {
final class AddMemo: UIViewController {
final class EditMemo: UIViewController {
final class DisplayMemo: UIViewController {

Lower the position of the definition

Separating variable declaration and function execution makes it easier to see, but if you don't remember the existence of each variable, you won't know the order of processing.

Therefore, by lowering the position where the variable is defined, the required variable is defined near the processing to be executed, so the degree of understanding is improved.

In the code below, variable definition and function processing are separated because there are many codes for each.

However, there are many variables defined, and it was difficult to understand the later processing while remembering all at once.

Therefore, I organized the related variables and processes as a set.

ViewController.swift


func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
    if let pickerImage = info[.originalImage] as? UIImage {
        //Parameters required for conversion to NSAttributedString
        let width                 = pickerImage.size.width
        let padding               = self.view.frame.width / 2
        let scaleRate             = width / (memoTextView.frame.size.width - padding)
        // 10%Compressed image to
        let resizedImage          = pickerImage.resizeImage(withPercentage: 0.1)!
        let imageAttachment       = NSTextAttachment()
        var imageAttributedString = NSAttributedString()
        // memoTextView.attributedText -> NSMutableAttributedString
        let mutAttrMemoText       = NSMutableAttributedString(attributedString: memoTextView.attributedText)

        // resizedImage -> NSAttributedString()
        imageAttachment.image = UIImage(cgImage: resizedImage.cgImage!, scale: scaleRate, orientation: resizedImage.imageOrientation)
        imageAttributedString = NSAttributedString(attachment: imageAttachment)
        mutAttrMemoText.append(imageAttributedString)
        //Text after adding the image-> memoTextView.attributedText
        memoTextView.attributedText = mutAttrMemoText
    }
    dismiss(animated: true, completion: nil)
}

ViewController.swift


func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
    if let pickerImage = info[.originalImage] as? UIImage {
        let width                 = pickerImage.size.width
        let padding               = self.view.frame.width / 2
        let scaleRate             = width / (memoTextView.frame.size.width - padding)
        // 10%Compressed image to
        let resizedImage          = pickerImage.resizeImage(withPercentage: 0.1)!
        let imageAttachment       = NSTextAttachment()
        // resizedImage -> NSAttributedString()
        imageAttachment.image = UIImage(cgImage: resizedImage.cgImage!, scale: scaleRate, orientation: resizedImage.imageOrientation)

        var imageAttributedString = NSAttributedString()
        imageAttributedString = NSAttributedString(attachment: imageAttachment)

        // memoTextView.attributedText -> NSMutableAttributedString()
        let mutAttrMemoString     = NSMutableAttributedString(attributedString: memoTextView.attributedText)
        mutAttrMemoString.append(imageAttributedString)
        //Text after adding the image-> memoTextView.attributedText
        memoTextView.attributedText = mutAttrMemoString
    }
    dismiss(animated: true, completion: nil)
}

A set of definitions and implementations for two UIAlertActions used for UIAlertController.

@IBAction private func attachImageGesture(_ sender: UILongPressGestureRecognizer) {
    let alert = UIAlertController(title: "Attach image", message: nil, preferredStyle: .actionSheet)

    let okAction = UIAlertAction(title: "OK", style: .default) { (action) in
        self.present(self.imagePicker, animated: true, completion: nil)
    }
    let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)

    alert.addAction(okAction)
    alert.addAction(cancelAction)
    
    present(alert, animated: true, completion: nil)
}

@IBAction private func attachImageGesture(_ sender: UILongPressGestureRecognizer) {
    let alert = UIAlertController(title: "Attach image", message: nil, preferredStyle: .actionSheet)

    let okAction = UIAlertAction(title: "OK", style: .default) { (action) in
        self.present(self.imagePicker, animated: true, completion: nil)
    }
    alert.addAction(okAction)

    let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
    alert.addAction(cancelAction)
    
    present(alert, animated: true, completion: nil)
}

Summary

So far, I have improved the code by referring to the readable code, but I found that this book is a book where you can learn basic refactoring on the royal road.

There is no way to dramatically reduce the amount of code and write beautiful code that everyone can understand.

Simply ・ Change the names of variables and functions ・ Add easy-to-understand comments ・ Change the order of processing It was just an easy method.

However, refactoring = cleaning up the code is just a steady process.

Because it's an easy method, I think it's a natural method that you should never forget.

The content of this book is easy to read in the technical book, but it contains refactoring methods that are faithful to the basics.

If you want to live as an engineer in the future, it is recommended that you learn refactoring from this book.

Recommended Posts

Improving hard-to-read code with readable code ~ General
Lombok with VS Code
Docker management with VS Code
Format Ruby with VS Code
Hello World with VS Code!
Reduce verbose code with Lombok