Swift 遗留代码重构:iOS/macOS 分步指南

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

引言:为什么遗留 Swift 代码需要重构

处理遗留代码是 iOS/macOS 开发人员生活中不可避免的一部分。即使项目最初是用 Swift 编写的,随着时间的推移,它也会积累技术债务:过时的模式、缺乏测试、单体架构以及 Swift 与 Objective-C 的混合。重构此类代码不仅能提高可读性,还能降低维护成本、加快新功能添加速度并减少错误数量。

在本文中,我们将介绍重构遗留 Swift 代码的具体技术:从消除强制解包到从 MVC 迁移到 MVVM。所有示例均可应用于 iOS 和 macOS 项目。



1. 评估与准备:重构从何处开始

在更改代码之前,了解其当前状态很重要。请使用 Xcode 内置的工具和指标。



1.1. 分析警告和错误

在 Xcode 中打开项目,然后转到报告导航器 (Cmd+9)。请注意:

  • 黄色警告——已弃用的 API、未使用的变量。
  • 红色错误——可能由不完整的 Swift 迁移导致的语法错误。
  • SwiftLint/SwiftFormat——连接一个 linter 以自动检测代码风格违规。


1.2. 在代码中搜索“红旗”

使用搜索 (Cmd+Shift+F) 扫描项目,查找以下模式:

  • ! — 强制解包(会导致应用崩溃的可选值)。
  • as! — 强制转换(未检查的类型转换)。
  • try! — 忽略错误。
  • NSObject — 不必要地继承自 NSObject(纯 Swift 不需要此操作)。
  • #selector — 字符串选择器,最好替换为带类型的 #selector。


// Bad (legacy)let name = user.name!  // force unwrap — crash on nil

// Good (refactoring)guard let name = user.name else { return}


1.3. 在更改前编写测试

没有测试的重构是有风险的。如果没有测试,请从为关键模块添加测试开始。使用 XCTest 进行单元测试,使用 XCUITest 进行 UI 测试。

遗留函数简单测试示例:

class LegacyCalculatorTests: XCTestCase {    func testCalculateTotal() {        let calculator = LegacyCalculator()        let result = calculator.calculateTotal(items: [10, 20, 30])        XCTAssertEqual(result, 60)    }}


2. 重构 Swift 代码的基本技术

让我们开始可以立即应用的实用步骤。



2.1. 用安全模式替换强制解包

遗留代码中最常见的问题是广泛使用 !。将它们替换为 guard let、if let 或 ??(空合并运算符)。

// Before refactoringfunc getUserName() -> String {    return user.name!}

// After refactoringfunc getUserName() -> String { return user.name ?? "Unknown"}


2.2. 消除 Massive View Controller (MVC → MVVM)

遗留项目通常存在“臃肿”的控制器问题。将业务逻辑提取到单独的服务或 ViewModel 中。

// Legacy ViewController (fragment)class ProfileViewController: UIViewController {    @IBOutlet weak var nameLabel: UILabel!        override func viewDidLoad() {        super.viewDidLoad()        // Business logic directly in the controller        let user = DataManager.shared.fetchUser()        nameLabel.text = user.name?.capitalized    }}

// After refactoringclass ProfileViewModel { private let userService: UserService var userName: String { return userService.currentUs

博客

书籍推荐