
swift-language
热门应用现代 Swift 语言模式与惯用法,适用于非并发、非 SwiftUI 的代码。涵盖 if/switch 表达式(Swift 5.9+)、类型化 throws(Swift 6+)、结果构建器、属性包装器、不透明类型与存在类型(some vs any)、guard 模式、Never 类型、正则构建器(Swift 5.7+)、基本 Codable 塑形(CodingKeys、自定义解码、嵌套容器)、现代集合 API(count(where:)、contains(where:)、replacing())、基本 FormatStyle 用法以及字符串插值模式。在编写涉及泛型、协议、枚举、闭包或现代语言特性的核心 Swift 代码时使用;深度 Codable 路由至 swift-codable,详细格式化/本地化路由至 swift-formatstyle,API 命名路由至 swift-api-design-guidelines。
应用现代 Swift 语言模式与惯用法,适用于非并发、非 SwiftUI 的代码。涵盖 if/switch 表达式(Swift 5.9+)、类型化 throws(Swift 6+)、结果构建器、属性包装器、不透明类型与存在类型(some vs any)、guard 模式、Never 类型、正则构建器(Swift 5.7+)、基本 Codable 塑形(CodingKeys、自定义解码、嵌套容器)、现代集合 API(count(where:)、contains(where:)、replacing())、基本 FormatStyle 用法以及字符串插值模式。在编写涉及泛型、协议、枚举、闭包或现代语言特性的核心 Swift 代码时使用;深度 Codable 路由至 swift-codable,详细格式化/本地化路由至 swift-formatstyle,API 命名路由至 swift-api-design-guidelines。
Swift 语言模式
应用当前 Swift 语言语法,不改变行为或求值顺序。
深度解码路由至 swift-codable,格式化路由至 swift-formatstyle,命名路由至 swift-api-design-guidelines,并发路由至 swift-concurrency,SwiftUI 状态/视图工作路由至 swiftui-patterns。
目录
- If/Switch 表达式
- 类型化 Throws
- 结果构建器
- 属性包装器
- 不透明类型与存在类型
- Guard 模式
- Never 类型
- 正则构建器
- Codable 最佳实践
- 现代集合 API
- FormatStyle
- 字符串插值
- 常见错误
- 审查清单
- 参考资料
If/Switch 表达式
为现代化,固定当前行为和求值顺序,进行一次语义重写,编译受影响的模块,并运行聚焦的测试用例。修复任何变更后再继续;重复直到行为保持不变。
Swift 5.9+ 允许 if 和 switch 作为返回值的表达式。使用它们直接赋值、返回或初始化。
// 从 if 表达式赋值
let icon = if isComplete { "checkmark.circle.fill" } else { "circle" }
// 从 switch 表达式赋值
let label = switch status {
case .draft: "Draft"
case .published: "Published"
case .archived: "Archived"
}
// 在返回位置使用
func badgeText(for priority: Priority) -> String {
switch priority {
case .high: "High"
case .medium: "Medium"
case .low: "Low"
}
}
规则:
- 每个分支必须产生相同类型的值。
- 不允许多语句分支——每个分支是一个单一表达式。
- 用作函数参数时用括号包裹以避免歧义。
类型化 Throws
Swift 6+ 允许指定函数抛出的错误类型。
enum ValidationError: Error {
case tooShort, invalidCharacters, alreadyTaken
}
func validate(username: String) throws(ValidationError) -> String {
guard username.count >= 3 else { throw .tooShort }
guard username.allSatisfy(\.isLetterOrDigit) else { throw .invalidCharacters }
return username.lowercased()
}
// 调用者获得类型化错误——无需转换
do {
let name = try validate(username: input)
} catch {
// error 是 ValidationError,而非 any Error
switch error {
case .tooShort: print("Too short")
case .invalidCharacters: print("Invalid characters")
case .alreadyTaken: print("Taken")
}
}
规则:
- 仅当调用者受益于穷尽错误处理时使用
throws(SomeError)。对于混合错误源,使用无类型throws。 - 当现代化一个仅包含本地错误枚举的辅助函数时,优先使用
throws(ErrorEnum)并注明 Swift 6+。 throws(Never)标记一个语法上会抛出但实际从不抛出的函数——在泛型上下文中很有用。- 类型化 throws 会传播:调用
throws(A)和throws(B)的函数自身必须抛出覆盖两者的类型(或使用无类型throws)。
结果构建器
@resultBuilder 支持 DSL 风格的语法。SwiftUI 的 @ViewBuilder 是最常见的例子,但你可以为任何领域创建自定义构建器。
@resultBuilder
struct ArrayBuilder<Element> {
static func buildBlock(_ components: [Element]...) -> [Element] {
components.flatMap { $0 }
}
static func buildExpression(_ expression: Element) -> [Element] { [expression] }
static func buildOptional(_ component: [Element]?) -> [Element] { component ?? [] }
static func buildEither(first component: [Element]) -> [Element] { component }
static func buildEither(second component: [Element]) -> [Element] { component }
static func buildArray(_ components: [[Element]]) -> [Element] { components.flatMap { $0 } }
}
func makeItems(@ArrayBuilder<String> content: () -> [String]) -> [String] { content() }
let items = makeItems {
"Always included"
if showExtra { "Conditional" }
for name in names { name.uppercased() }
}
构建器方法: buildBlock(组合语句)、buildExpression(单一值)、buildOptional(不带 else 的 if)、buildEither(if/else)、buildArray(for..in)、buildFinalResult(可选后处理)。
属性包装器
自定义 @propertyWrapper 类型封装存储和访问模式。
@propertyWrapper
struct Clamped<Value: Comparable> {
private var value: Value
let range: ClosedRange<Value>
var wrappedValue: Value {
get { value }
set { value = min(max(newValue, range.lowerBound), range.upperBound) }
}
var projectedValue: ClosedRange<Value> { range }
init(wrappedValue: Value, _ range: ClosedRange<Value>) {
self.range = range
self.value = min(max(wrappedValue, range.lowerBound), range.upperBound)
}
}
// 使用
struct Volume {
@Clamped(0...100) var level: Int = 50
}
var v = Volume()
v.level = 150 // 限制为 100
print(v.$level) // 投影值:0...100
设计规则:
wrappedValue是主要的 getter/setter。projectedValue(通过$property访问)提供元数据或绑定。- 属性包装器可以组合:
@A @B var x先应用外层包装器。 - 当简单的计算属性足够时,不要使用属性包装器。
不透明类型与存在类型
some Protocol(不透明类型)
调用者不知道具体类型,但编译器知道。-> some P 返回在所有返回分支中具有一个固定的底层具体类型。
func makeCollection() -> some Collection<Int> {
[1, 2, 3] // 总是返回 Array<Int>——编译器知道具体类型
}
使用 some 的场景:
- 返回类型:当你希望隐藏实现但保留类型标识时。
- 参数类型(Swift 5.7+):
some P是未命名泛型参数(如<T: P>)的简写。
any Protocol(存在类型)
一个存在盒子,可以在运行时持有任何符合的类型。它使用动态派发,并且当值不适合内联缓冲区时可能会分配内存。
func process(items: [any StringProtocol]) {
for item in items {
print(item.uppercased())
}
}
何时选择
使用 some |
使用 any |
|---|---|
| 返回类型隐藏具体类型 | 异构集合 |
| 函数参数(替代简单泛型) | 需要动态类型擦除 |
| 更好的性能(静态派发) | 协议有 Self 或关联类型要求需要擦除 |
经验法则: 默认使用 some。仅当你需要异构集合或运行时类型灵活性时才使用 any。
Guard 模式
guard 强制执行前置条件并支持提前退出。它使快乐路径保持左对齐并减少嵌套。
func processOrder(_ order: Order?) throws -> Receipt {
// 解包可选值
guard let order else { throw OrderError.missing }
// 验证条件
guard order.items.isEmpty == false else { throw OrderError.empty }
guard order.total > 0 else { throw OrderError.invalidTotal }
// 布尔检查
guard order.isPaid else { throw OrderError.unpaid }
// 模式匹配
guard case .confirmed(let date) = order.status else {
throw OrderError.notConfirmed
}
return Receipt(order: order, confirmedAt: date)
}
最佳实践:
- 使用
guard处理前置条件,使用if处理分支逻辑。 - 合并相关 guard:
guard let a, let b else { return }。 else块必须退出作用域:return、throw、continue、break或fatalError()。- 使用简写解包:
guard let value else { ... }(Swift 5.7+)。
Never 类型
Never 是一个无人居住的类型,用于从不产生值的代码路径。它仅在值表达式可用或可推断时表现为 Swift 的底类型;它不是通用的类型见证,不会隐式符合任意协议,并且不能满足诸如 T: P 的泛型约束,除非该约束对 Never 本身有效。
// 终止程序的函数
func crashWithDiagnostics(_ message: String) -> Never {
let diagnostics = gatherDiagnostics()
logger.critical("\(message): \(diagnostics)")
fatalError(message)
}
enum Result<Success, Failure: Error> {
case success(Success)
case failure(Failure)
}
// Result<String, Never>——一个永远不会失败的结果
// 穷尽 switch:无需 default,因为 Never 没有 case
func handle(_ result: Result<String, Never>) {
switch result {
case .success(let value): print(value)
// 不需要 .failure case——编译器知道它不可能
}
}
正则构建器
Swift 5.7+ 的正则构建器 DSL 提供编译时检查、可读的模式。
import Foundation
import RegexBuilder
// 解析 "2024-03-15" 为组件
let dateRegex = Regex {
Capture { /\d{4}/ }; "-"; Capture { /\d{2}/ }; "-"; Capture { /\d{2}/ }
}
if let match = "2024-03-15".firstMatch(of: dateRegex) {
let (_, year, month, day) = match.output
_ = (year, month, day)
}
// TryCapture 带转换
let priceRegex = Regex {
"$"
TryCapture { OneOrMore(.digit); "."; Repeat(.digit, count: 2) }
transform: { Decimal(string: String($0)) }
}
何时使用构建器 vs. 字面量:
- 构建器:复杂模式、可复用组件、强类型捕获。
- 字面量(
/pattern/):简单模式、熟悉正则语法。 - 两者可以混合:在构建器块中嵌入
/.../字面量。
Codable 最佳实践
使用 CodingKeys 进行简单重命名,仅在有效载荷形状或转换不匹配时使用自定义解码。加载扩展 Swift 模式获取紧凑语言示例;使用 swift-codable 进行实现和验证。
现代集合 API
优先使用这些现代 API 而非手动循环:
let numbers = [1, 2, 3, 4, 5, 6, 7, 8]
// count(where:)——替代 .filter { }.count
let evenCount = numbers.count(where: { $0.isMultiple(of: 2) })
// contains(where:)——在第一个匹配时短路
let hasNegative = numbers.contains(where: { $0 < 0 })
// first(where:) / last(where:)
let firstEven = numbers.first(where: { $0.isMultiple(of: 2) })
// String replacing()——Swift 5.7+,返回新字符串
let cleaned = rawText.replacing(/\s+/, with: " ")
let snakeCase = name.replacing("_", with: " ")
// compactMap——从转换中解包可选值
let ids = strings.compactMap { Int($0) }
// flatMap——展平嵌套集合
let allTags = articles.flatMap(\.tags)
// Dictionary(grouping:by:)
let byCategory = Dictionary(grouping: items, by: \.category)
// reduce(into:)——高效累积
let freq = words.reduce(into: [:]) { counts, word in
counts[word, default: 0] += 1
}
FormatStyle
使用 .formatted() 和 Text(_:format:) 进行基本显示。样式选择、解析、本地化测试和可复用格式化器设计路由至 swift-formatstyle。
字符串插值
扩展 DefaultStringInterpolation 以实现领域特定格式化。使用 """ 表示多行字符串(缩进相对于结束的 """)。参见 references/swift-patterns-extended.md 获取自定义插值示例。
常见错误
- 在
some可行时使用any。 默认对返回类型和参数使用some,但每个-> some P分支必须返回相同的具体类型。 - 手动循环或
.filter { }.count而非集合 API。 使用count(where:)进行条件计数,以及contains(where:)、compactMap和flatMap替代额外迭代或数组。 - 使用
DateFormatter而非 FormatStyle。.formatted()更简单、类型安全,并自动处理本地化。 - 强制解包 Codable 解码。 对可选或缺失键使用
decodeIfPresent并带默认值。 - 现代化过程中重新排序前置条件。 使用
guard而不将规范化或转换移到验证之前。 - 无效的
@c签名。 说明UnsafeBufferPointer是 Swift 结构体/值包装器,然后拒绝String、Array、闭包和泛型占位符。 - 忽略类型化 throws。 当函数具有单一、清晰的错误类型时,类型化 throws 使调用者无需转换即可进行穷尽 switch。
- 过度使用属性包装器。 当不需要复用或投影值时,计算属性更简单。
- 对
Never说明不足。 对于Result<T, Never>或throws(Never),显式写出注意事项:Never 不会隐式符合任意协议,不能满足任意T: P约束,仅在有效的表达式/推断上下文中表现为底类型。 - 拥有兄弟技能的实现。 命名所有者技能并停止。避免
CodingKeys、解码器、格式化器、SwiftUI 或并发的代码片段。
审查清单
- [ ]
some仅当每个不透明返回分支具有一个具体类型时使用 - [ ]
guard用于前置条件;count(where:)替代手动计数或.filter { }.count - [ ] 使用
.formatted()而非DateFormatter/NumberFormatter - [ ] Codable 类型使用
CodingKeys进行 API 映射;对可选字段使用decodeIfPresent并带默认值 - [ ] if/switch 表达式用于条件赋值;属性包装器有明确的复用理由
- [ ] 复杂模式使用正则构建器(简单模式可使用字面量)
- [ ] 类型化 throws 用于单一本地错误域,并注明 Swift 6+ 兼容性
- [ ]
@c修正将UnsafeBufferPointer称为 Swift 结构体/值包装器,并枚举被拒绝的纯 Swift 类型名称 - [ ]
Never指导使用“无人居住”和“底类型类似”,并说明没有隐式任意协议/泛型符合 - [ ] 深度 Codable 路由至
swift-codable;FormatStyle API 路由至swift-formatstyle;市场/本地化显示 QA 路由至ios-localization;命名/并发/SwiftUI 路由至兄弟技能
参考资料
- 扩展模式和 Codable 示例:references/swift-patterns-extended.md
- 属性和 C 互操作:references/swift-attributes-interop.md





