swift-language

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。

932Star
47Fork
更新于 2026/7/15
SKILL.md
readonly只读
name
swift-language
description

应用现代 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 表达式

为现代化,固定当前行为和求值顺序,进行一次语义重写,编译受影响的模块,并运行聚焦的测试用例。修复任何变更后再继续;重复直到行为保持不变。

Swift 5.9+ 允许 ifswitch 作为返回值的表达式。使用它们直接赋值、返回或初始化。

// 从 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(不带 elseif)、buildEitherif/else)、buildArrayfor..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 块必须退出作用域:returnthrowcontinuebreakfatalError()
  • 使用简写解包: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 获取自定义插值示例。

常见错误

  1. some 可行时使用 any 默认对返回类型和参数使用 some,但每个 -> some P 分支必须返回相同的具体类型。
  2. 手动循环或 .filter { }.count 而非集合 API。 使用 count(where:) 进行条件计数,以及 contains(where:)compactMapflatMap 替代额外迭代或数组。
  3. 使用 DateFormatter 而非 FormatStyle。 .formatted() 更简单、类型安全,并自动处理本地化。
  4. 强制解包 Codable 解码。 对可选或缺失键使用 decodeIfPresent 并带默认值。
  5. 现代化过程中重新排序前置条件。 使用 guard 而不将规范化或转换移到验证之前。
  6. 无效的 @c 签名。 说明 UnsafeBufferPointer 是 Swift 结构体/值包装器,然后拒绝 StringArray、闭包和泛型占位符。
  7. 忽略类型化 throws。 当函数具有单一、清晰的错误类型时,类型化 throws 使调用者无需转换即可进行穷尽 switch。
  8. 过度使用属性包装器。 当不需要复用或投影值时,计算属性更简单。
  9. Never 说明不足。 对于 Result<T, Never>throws(Never),显式写出注意事项:Never 不会隐式符合任意协议,不能满足任意 T: P 约束,仅在有效的表达式/推断上下文中表现为底类型。
  10. 拥有兄弟技能的实现。 命名所有者技能并停止。避免 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 路由至兄弟技能

参考资料