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 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 TODO tool window where you can see all the places in the project that we are going to refactor:
. This will open theRename
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, or 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
from the main menu. When the structure name gets highlighted, type the new name:The gray icon next to the highlighted symbol means that the refactoring won't be performed in comments and string literals. Click this icon or press ⇥ and in the popup that appears, select the Comments and strings checkbox:
Press ⏎ to perform the refactoring. This will rename all occurrences of the symbol in source code, comments, and string literals. Moreover, the ContentsView.swift file will also be renamed.
Now let's make sure that AppCode has changed the structure name everywhere. Press ⌃ ⇧ F. In the dialog that opens, enable case sensitive search by clicking and type the old name —
ContentView
. The onlyContentView
occurrence found is theContentView_Previews
class. It was not renamed by the refactoring since it is a different symbol. Still, for the sake of consistency, rename it toConferencesList_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 ⌃ Y.
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 ( ), 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. To do this:
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 toconferencesData
:Press ⏎.
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 mean 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 (⌃ Y).
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 ( ), 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 thefilename
parameter within the method body. To do this, select one of the occurrences and press ⌃ ⌥ ⇧ J — the carets will be set at all the occurrences of the selected string, and you can typefilename
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 ( ), 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 ⇧ F10 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 namedresult
. You can use the Introduce 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 theConference
class and delete all the usages of theconference
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 replacetextDates()
withconference.textDates()
. ThetextDates()
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. 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.