引言:为什么遗留 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