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:
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 ⏎
:
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 . There are two occurrences left, let’s take a closer look at them:
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
:
Click Preview to check all the occurrences that are going to be renamed:
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:
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 ⏎
:
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:
Leave the both options unchecked. The variable name suggested by default is conferences
. Rename it to conferencesData
:
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:
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:
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:
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 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:
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:
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:
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:
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 ⏎
:
In the Extract Method dialog, type the method name — textDates
— and click Refactor:
The new method appears in the ConferenceDetails
structure, and its call is added in place of the extracted expression:
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:
Apply a quick-fix to make the method internal: press ⌥⏎
and select the corresponding action from the list:
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.