How do I refactor Swift in Xcode?
AFAIK the refactoring is not working with Swift right now, however, at least in the same file(scope), you can do the following:
Xcode Refactor Convert to Switch
Update
As you've pointed out, Convert to Switch
!= Convert To Switch Statement
. In order to understand why, we need to know how Xcode refactoring feature works under the hood.
What I've found:
Here's a great article on the topic:
https://levelup.gitconnected.com/uncovering-xcode-indexing-8b3f3ff82551
It points us to a swift
open-source repo: https://github.com/apple/swift
Xcode refactoring is done with a SourceKit
framework, which is inside a swift repo.
Here's a SourceKit protocol for communication with Xcode: https://github.com/apple/swift/blob/main/tools/SourceKit/docs/Protocol.md
Internally, when we choose "Refactor", Xcode somehow (via XPC) sends something like source.request.workspace.refactoring
message to a SourceKit. And there's an "indexed file" somewhere, so SourceKit
can instantly (not in 5 minutes :)) propose you the options which are adequate/possible for a current snippet of code.
I've loaded swift
repo from github (it's about 30MB), navigated into SourceKit
code and tried to find stuff related to source.request.workspace.refactoring
.
Somewhere in the code is a reference to swift/IDE/RefactoringKinds.def
. I'll copy-paste the contents of this file:
#ifndef REFACTORING
#define REFACTORING(KIND, NAME, ID)
#endif
#ifndef SEMANTIC_REFACTORING
#define SEMANTIC_REFACTORING(KIND, NAME, ID) REFACTORING(KIND, NAME, ID)
#endif
#ifndef RANGE_REFACTORING
#define RANGE_REFACTORING(KIND, NAME, ID) SEMANTIC_REFACTORING(KIND, NAME, ID)
#endif
#ifndef INTERNAL_RANGE_REFACTORING
#define INTERNAL_RANGE_REFACTORING(KIND, NAME, ID) RANGE_REFACTORING(KIND, NAME, ID)
#endif
#ifndef CURSOR_REFACTORING
#define CURSOR_REFACTORING(KIND, NAME, ID) SEMANTIC_REFACTORING(KIND, NAME, ID)
#endif
/// Rename and categorise the symbol occurrences at provided locations
/// (syntactically).
REFACTORING(GlobalRename, "Global Rename", rename.global)
/// Categorize source ranges for symbol occurrences at provided locations
/// (syntactically).
REFACTORING(FindGlobalRenameRanges, "Find Global Rename Ranges", rename.global.find-ranges)
/// Find and categorize all occurences of the file-local symbol at a given
/// location.
REFACTORING(FindLocalRenameRanges, "Find Local Rename Ranges", rename.local.find-ranges)
/// Find and rename all occurences of the file-local symbol at a given
/// location.
CURSOR_REFACTORING(LocalRename, "Local Rename", rename.local)
CURSOR_REFACTORING(FillProtocolStub, "Add Missing Protocol Requirements", fillstub)
CURSOR_REFACTORING(ExpandDefault, "Expand Default", expand.default)
CURSOR_REFACTORING(ExpandSwitchCases, "Expand Switch Cases", expand.switch.cases)
CURSOR_REFACTORING(LocalizeString, "Localize String", localize.string)
CURSOR_REFACTORING(SimplifyNumberLiteral, "Simplify Long Number Literal", simplify.long.number.literal)
CURSOR_REFACTORING(CollapseNestedIfStmt, "Collapse Nested If Statements", collapse.nested.ifstmt)
CURSOR_REFACTORING(ConvertToDoCatch, "Convert To Do/Catch", convert.do.catch)
CURSOR_REFACTORING(TrailingClosure, "Convert To Trailing Closure", trailingclosure)
CURSOR_REFACTORING(MemberwiseInitLocalRefactoring, "Generate Memberwise Initializer", memberwise.init.local.refactoring)
CURSOR_REFACTORING(AddEquatableConformance, "Add Equatable Conformance", add.equatable.conformance)
CURSOR_REFACTORING(ConvertCallToAsyncAlternative, "Convert Call to Async Alternative", convert.call-to-async)
CURSOR_REFACTORING(ConvertToAsync, "Convert Function to Async", convert.func-to-async)
CURSOR_REFACTORING(AddAsyncAlternative, "Add Async Alternative", add.async-alternative)
RANGE_REFACTORING(ExtractExpr, "Extract Expression", extract.expr)
RANGE_REFACTORING(ExtractFunction, "Extract Method", extract.function)
RANGE_REFACTORING(ExtractRepeatedExpr, "Extract Repeated Expression", extract.expr.repeated)
RANGE_REFACTORING(MoveMembersToExtension, "Move To Extension", move.members.to.extension)
RANGE_REFACTORING(ConvertStringsConcatenationToInterpolation, "Convert to String Interpolation", convert.string-concatenation.interpolation)
RANGE_REFACTORING(ExpandTernaryExpr, "Expand Ternary Expression", expand.ternary.expr)
RANGE_REFACTORING(ConvertToTernaryExpr, "Convert To Ternary Expression", convert.ternary.expr)
RANGE_REFACTORING(ConvertIfLetExprToGuardExpr, "Convert To Guard Expression", convert.iflet.to.guard.expr)
RANGE_REFACTORING(ConvertGuardExprToIfLetExpr, "Convert To IfLet Expression", convert.to.iflet.expr)
RANGE_REFACTORING(ConvertToComputedProperty, "Convert To Computed Property", convert.to.computed.property)
RANGE_REFACTORING(ConvertToSwitchStmt, "Convert To Switch Statement", convert.switch.stmt)
// These internal refactorings are designed to be helpful for working on
// the compiler/standard library, etc., but are likely to be just confusing and
// noise for general development.
INTERNAL_RANGE_REFACTORING(ReplaceBodiesWithFatalError, "Replace Function Bodies With 'fatalError()'", replace.bodies.with.fatalError)
#undef CURSOR_REFACTORING
#undef INTERNAL_RANGE_REFACTORING
#undef RANGE_REFACTORING
#undef SEMANTIC_REFACTORING
#undef REFACTORING
As you see, there's Convert To Switch Statement
here, and Expand Switch Cases
, but not Convert To Switch
.
So my guess is that what Xcode shows us in UI, is either a bug (they forgot to delete an outdated stuff), or a part of some implementation which is not in a current version of a Swift repo.
Anyway, you may dig even deeper - I've just tried to investigate it in a short period of time. :)
If you really need this done, then except of exploring SourceKit
protocols, I guess you can open an issue on Github - maybe those who know it will explain a real reason.
Initial answer
Let's consider such an example:
enum SomeEnum {
case option1, option2, option3
}
func convertToSwitchExampleFunc(_ option: SomeEnum) {
if option == .option1 {
print("1")
}
if option == .option2 {
print("2")
}
if option == .option3 {
print("3")
}
}
func callingFunc() {
convertToSwitchExampleFunc(.option2)
}
If you select all code inside the body of convertToSwitchExampleFunc
, then choose Refactor
, Xcode won't show you "Convert to Switch".
But if you change the code such that it's more similar to an ugly "switch" implementation (if-else-if-else
chain):
func convertToSwitchExampleFunc(_ option: SomeEnum) {
if option == .option1 {
print("1")
}
else if option == .option2 {
print("2")
}
else if option == .option3 {
print("3")
}
}
Then Xcode will show you "Convert to Switch menu", and an auto-refactored code will look like this:
func convertToSwitchExampleFunc(_ option: SomeEnum) {
switch option {
case .option1:
print("1")
case .option2:
print("2")
case .option3:
print("3")
default:
break
}
}
Just to give more context:
Xcode 9.1 Refactoring not available
Try cleaning your Xcode. Also try clearing out derived data and restart Xcode.
Clean Build --> (Command-Option-Shift-K)
Delete DerivedData folder in ~/Library/Developer/Xcode/DerivedData
Code refactoring - Swift
You can use a single IBAction
instead of 4 separate methods. And make the sender type to UIButton
from Any
. Then you can do something like
@IBAction func toggleEnableDisable(_ sender: UIButton) {
let currentValue = sender.isEnabled
let buttonArray = [trainingButton, friendlyMatch, tournamentButton, competitiveMatch]
buttonArray.forEach { $0.isEnabled = false }
sender.isEnabled = !currentValue
}
Swift refactoring: How quickly change all access modifiers to public
You can make it in Find navigator. Choose Replace -> Text -> Matching Word and write something like this:
Choose your framework by clicking on "In Workspace". Than click "Replace all".
Do this with classes and structures. But be attentive with func
and var/let
because it can be inside some function.
Rename or refactor files in Xcode
The safest way to rename files is to refactor the class name using Xcode's "Refactor" command. Using that command makes sure that your code is also updated anywhere that references the file or the class (including NIBs).
Right-click on the class name in the interface (.h
) file, choose Refactor->Rename
, and Xcode will guide you through the process, allowing you to preview changes before you commit to them.
More details:
Right click on the class name (after @interface
) and choose Refactor->Rename. For example, right click on ViewController
in this code:
@interface ViewController : UIViewController
Enter the new name for the class:
Xcode will let you review the changes before you commit.
You can also highlight the class name and use Xcode's Edit->Refactor
menu, or even map it to a keyboard shortcut if you want.
If you actually want to rename the file without affecting any code, click on the name in the Navigator pane (left pane), wait a second, then click again. The name will be highlighted and you can type in a new one. You can also delete from the Navigator, rename the file in the Finder and re-add it to your project.
Related Topics
Realitykit as a Framework to Build 3D Nonar Apps
Flip Arfaceanchor from Left-Handed to Right-Handed Coordinate System
Nothing Prints Out in the Console in Command Line Tool Xcode
Get Fullpath or Convert to Fullpath
Save & Retrieve Tableviewcell Checkmark Using Nsuserdefaults in Swift
Executing Text-To-Speech in Order
Cannot Invoke 'Filter' with an Argument List of Type '((_) -> _)'
Swift Mutable Set: Duplicate Element Found
Swift- Variable Not Initialized Before Use (But It's Not Used)
What's the Best Way to Cut Swift String into 2-Letter-Strings
How to Manage Swiftui State with Nested Structs
Opposite of _Conversion in Swift to Assign to a Value of a Different Type
How to Say "If X == a or B or C" as Succinctly in Swift as Possible