首页 > 解决方案 > Xcode 重构“转换为 Switch”

问题描述

Xcode Refactor 有一个名为“Convert to Switch”的功能。我正在寻找可以由函数使用并转换为 switch 流语句的代码示例。

标签: iosxcode

解决方案


更新

正如您所指出的,Convert to Switch!= Convert To Switch Statement。为了理解为什么,我们需要知道 Xcode 重构特性是如何在底层工作的。

我发现了什么:

这是一篇关于该主题的精彩文章: https ://levelup.gitconnected.com/uncovering-xcode-indexing-8b3f3ff82551

它指向一个swift开源仓库:https ://github.com/apple/swift

Xcode 重构是通过一个SourceKit框架完成的,该框架位于一个 swift repo 中。

这是与 Xcode 通信的 SourceKit 协议:https ://github.com/apple/swift/blob/main/tools/SourceKit/docs/Protocol.md

在内部,当我们选择“重构”时,Xcode以某种方式(通过 XPC)向SourceKit 发送消息之类的东西。source.request.workspace.refactoring并且在某处有一个“索引文件”,因此SourceKit可以立即(而不是在 5 分钟内 :))向您建议对于当前代码片段来说足够/可能的选项。

我已经swift从 github 加载了 repo(大约 30MB),导航到SourceKit代码并试图找到与source.request.workspace.refactoring.

代码中的某处是对swift/IDE/RefactoringKinds.def. 我将复制粘贴此文件的内容:

#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

如您所见,Convert To Switch Statement这里有 和Expand Switch Cases,但没有Convert To Switch

所以我的猜测是,Xcode 在 UI 中向我们展示的要么是一个错误(他们忘记删除过时的东西),要么是某些实现的一部分,它不在当前版本的 Swift 存储库中。

无论如何,你可能会更深入地挖掘——我只是试图在短时间内调查它。:) 如果你真的需要这样做,那么除了探索SourceKit协议之外,我想你可以在 Github 上打开一个问题——也许知道的人会解释一个真正的原因。

初步答案

让我们考虑这样一个例子:

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)
}

如果您选择 的正文中的所有代码convertToSwitchExampleFunc,然后选择Refactor,Xcode 将不会显示“转换为开关”。

但是,如果您更改代码使其更类似于丑陋的“开关”实现(if-else-if-else链):

func convertToSwitchExampleFunc(_ option: SomeEnum) {
    if option == .option1 {
        print("1")
    }
    else if option == .option2 {
        print("2")
    }
    else if option == .option3 {
        print("3")
    }
}

然后 Xcode 将显示“转换为切换菜单”,自动重构的代码将如下所示:

func convertToSwitchExampleFunc(_ option: SomeEnum) {
    switch option {
    case .option1:
        print("1")
    case .option2:
        print("2")
    case .option3:
        print("3")
    default:
        break
    }
}

只是为了提供更多上下文: 在此处输入图像描述


推荐阅读