AppCode News

Tutorial: Swift refactorings in AppCode

In this tutorial, you will get familiar with the AppCode refactoring tools available for Swift. On a simple project, we will show you how you can quickly and easily improve your code using refactorings. Along the way, you’ll also get familiar with some of the inspections and intention actions available in AppCode.

Before you start

To be able to repeat the steps of this tutorial, download the iOSConferences project. It is a simple SwiftUI application that loads a list of conferences from a local JSON file. You can look at how we created a similar application in the Create a SwiftUI application in AppCode tutorial. Now we will try to optimize its code and make it more concise and easier to read.

In the main menu, select View | Tool Windows | TODO. This will open the TODO tool window where you can see all the places in the project that we are going to refactor:

TODO comment

Read more on how to work with TODO comments in AppCode in TODO comments help article.

Rename

Renaming is one of the simplest and most frequently used operations that can significantly improve readability of your code. You may need it, for example, when you notice that a class, method, or variable name either does not follow the company’s guidelines, or its functionality was extended, and the name should be updated accordingly. The Rename refactoring in AppCode allows you to safely change the name of a file, class, method, variable throughout the whole project.

In the TODO tool window, double-click the Rename comment to navigate to the ContentView structure. ContentView is a SwiftUI view that displays the list of conferences. Though the application consists of two views only, the name of this one definitely could be more precise. So, let’s rename it to ConferencesList.

Place the caret at the structure name and press ⇧F6. Alternatively, select Refactor | Rename from the main menu. When the structure name gets highlighted, type the new name and press :

Rename refactoring

This action changes not only the structure name and its usages but also the name of the file. Nevertheless, let’s check if the structure name has been changed everywhere in the project. Press ⇧⌘F and in the dialog that opens type ContentView and make the search for the case-sensitive by clicking Case-sensivity. There are two occurrences left, let’s take a closer look at them:

Find not renamed

The first occurrence appears in comments. By default, text in comments is not changed when calling the Rename refactoring this way — you should perform it a little differently. Undo the refactoring by pressing ⌘Z and in the dialog that opens confirm this action. Place the caret at ContentView again and press ⇧F6 twice. This time the Rename dialog opens. Select the Search in comments and strings checkbox and enter the new name — ConferencesList:

Rename in comments

Click Preview to check all the occurrences that are going to be renamed:

Rename Preview

Now you see the usages in comments will also be affected. Click Do Refactor. Press ⇧⌘F. The only ContentView occurrence left is the ContentView_Previews class. It was not renamed by the refactoring since it is a separate symbol. Still, for the sake of consistency, rename it to ConferencesList_Preview. You can do it directly in the preview of the Find in File dialog:

Rename in Find dialog

To close the dialog, just click somewhere outside. Delete the //TODO: Rename comment. To delete the whole line at once, place the caret at it and press ⌘⌫.

Introduce a variable

The Introduce Variable refactoring may come in handy when you want to make your code more readable and clear, lighten up complex constructions, or avoid code repetition.

In the TODO tool window (View | Tool Windows | TODO), double-click the Introduce Variable comment. This will bring you to the List object with the loadFile() method passed as its parameter. You can extract the expression in brackets to a variable named conferencesData. This will make the code more clear and allow you to get rid of the type cast.

Place the caret at the loadFile() method call and press ⌥⌘V. Alternatively, press ⌃T and then 5 to select the refactoring from the list.

Select the expression in brackets and press :

Expressions

The variable declaration will appear right above the List object. Now you can choose if you want to declare the new variable with var and specify the variable type explicitly:

Extract Variable

Leave the both options unchecked. The variable name suggested by default is conferences. Rename it to conferencesData:

Extract Variable

With the caret placed at the variable declaration, press ⌥⇧↑ to put this line before the body structure and press ⌥⌘L to fix the code formatting:

Fix code and formatting

Let’s specify the variable type in the declaration and remove the type cast in the variable value. Press ⌥⏎ to invoke available intention actions and select Add explicit type:

Add explicit type

Remove as [Conference]. Look at the top right corner of the editor — the inspection widget has appeared there. This means that AppCode detected some code that can be improved. Click the widget to open the Problems tool window:

Problems toolwindow

You see there is a redundant return keyword at line 16. You can fix it by pressing ⌥⏎ (either in the editor or in the Problems tool window) and selecting Remove redundant ‘return’.

Remove the TODO comment (⌘⌫).

Change signature

The Change Signature refactoring allows you to change a set of method parameters and their types, the return and throw types, and method visibility. The refactoring is performed in all method calls, implementations, and overrides.

In the TODO tool window (View | Tool Windows | TODO), double-click the Change Signature comment. This will open the loadFile() method in Data.swift. This method loads data from the local conferencesList.json file. The file name is specified in the method body, however, it would make more sense to pass it as a parameter. Let’s use the Change Signature refactoring to do this.

Place the caret at the loadFile() method name and press ⌘F6. Alternatively, press ⌃T and then 2 to select the Change Signature refactoring from the list.

In the Change Signature dialog that opens, click Add to add a new parameter and enter values in the following fields:

Internal Name: filename

Type: String

Default value: "conferencesData.json"

This will add an internal parameter of String type that you can see in the preview below:

Change Signature

Click the Preview button to see all the usages of the method that will be affected by the refactoring. In our case, there are two places — the method declaration and call:

Change Signature preview

Click Do Refactor. Now replace all occurrences of "conferencesData.json" with the filename parameter within the method body. To do this, select one of the occurrences and press ⌃⌘G — the carets will be set at all the occurrences of the selected string, and you can type filename instead:

Multiple cursors

Navigate to the method call to make sure the refactoring has been applied correctly. To do this, place the caret at the method name and press ⌘B. The loadFile() method is called with the "conferencesData.json" string passed as its parameter:

loadFile method

Go back to the method declaration ⌘B and remove the TODO comment.

Extract method

With the Extract Method refactoring, you can move a code fragment to a separate method and, therefore, make your code more readable, avoid duplications, and isolate independent parts of code if needed.

From the TODO tool window (View | Tool Windows | TODO), select the last comment — Extract Method. Here you see that the end and start dates are displayed in the Text element.

If you run the application ⌃R and select the dotSwift conference, the program will crash at this line. The point is that the value of the end property in this case equals nil, and the application will crash trying to unwrap the optional. Let’s extract the expression in the brackets into a separate method and add some code that will fix the bug. Place the caret at the expression in the brackets and press ⌥⌘M. Alternatively, press ⌃T and then 6.

In the popup that opens, select the expression that should be extracted and then press :

Expressions list

In the Extract Method dialog, type the method name — textDates — and click Refactor:

Extract Method dialog

The new method appears in the ConferenceDetails structure, and its call is added in place of the extracted expression:

Extracted method

Extract ⌥⌘V conference.start.dateToString() to a separate variable named result. You can use the Extract Variable refactoring ⌥⌘V to do it:

private func textDates() -> String {
    var result = conference.start.dateToString()
    return "\(result) - \((conference.end?.dateToString())!)"
}

In the if let block, add the code for displaying dates with non-nil end values:

private func textDates() -> String {
    var result = conference.start.dateToString()
    if let end = conference.end {
        result = "\(result) - \(end.dateToString())"
    }
    return result
}

Move the newly created method from ConferenceDetails to the Conference class and delete all the usages of the conference variable:

private func textDates() -> String {
    var result = start.dateToString()
    if let end = end {
        result = "\(result) - \(end.dateToString())"
    }
    return result
}

Go back to the ConferenceDetails view and replace textDates() with conference.textDates(). The textDates() method call gets highlighted. Hover the cursor overt it to see the error message:

Error

Apply a quick-fix to make the method internal: press ⌥⏎ and select the corresponding action from the list:

Quick fixes

Remove the TODO comment.

What’s next

In this tutorial, we covered the popular Swift refactorings and showed how you can use them in your project. You can find the description of all available refactorings for Swift and Objective-C in the Refactorings section in our help. Also, refer to the Code inspections and Intention actions sections to learn how code inspections and intention actions in AppCode can help you improve your code.