mcpField
S
swift

swift

github.com/wendylabsinc开发6274 次安装5 周前更新
代码仓库

关于 swift

面向 Swift 开发者的编程 skill,提供配置、日志、遥测、测试、调试和语言实践的参考指南。按主题加载文档,涵盖从测试迁移到跨平台实现的具体问题。

主要功能

  1. 01配置与密钥处理指南
  2. 02Swift Log 结构化日志实践
  3. 03OpenTelemetry 设置与导出指南
  4. 04Swift Testing 模式及 XCTest 迁移
  5. 05内存安全与平台特定约定

适用场景

  1. 01设置配置和密钥处理
  2. 02为服务器应用添加日志或 OpenTelemetry
  3. 03编写 Swift 测试或从 XCTest 迁移
  4. 04检查跨平台及内存安全模式

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/wendylabsinc/claude-skills --skill swift

安装后在 Claude Code 中输入/swift确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
debugging.md
swift-configuration.md
swift-log.md
swift-otel.md
swift-testing.md

详细介绍

Swift

Swift 是一门现代通用编程语言。

参考文件

根据需要加载以下文件,了解特定主题:

  • references/swift-configuration.md - Swift 配置:从环境变量、文件和 CLI 参数读取配置;提供程序层级、命名空间、热重载、密钥处理
  • references/swift-log.md - Swift Log 日志 API:日志级别、结构化日志、库的最佳实践、元数据、自定义处理程序
  • references/swift-otel.md - Swift OTel:面向服务端应用的 OpenTelemetry 后端(Linux 上优先);日志、指标、追踪的 OTLP 导出;框架集成
  • references/swift-testing.md - Swift Testing 框架:@Test 宏、#expect/#require 断言、特征、参数化测试、测试套件、并行执行、从 XCTest 迁移
  • references/debugging.md - 调试技巧:Linux 上的终端 UI(备用屏幕缓冲区)、GitHub Actions 日志分析
Swift Testing 测试名称

优先使用反引号转义、句式风格的测试函数名称,让测试描述体现在函数名称中:

@Test
func `does something very special in a certain edge case`() async throws {
    // ...
}

仅在确实需要单独的显示名称时,才使用 @Test("...") 显示名称。

访问修饰符

除非类型和函数需要供外部使用,否则将其设为 internal。这样可避免意外暴露实现细节,也更容易修复访问级别错误。

成员组织

按以下可见性顺序排列成员:

  1. Public
  2. Internal
  3. Private

Public 成员应放在最前面,并按主题分组到 // MARK: - ... 部分中,形式应类似 Apple API 参考文档的分组。每个 // MARK: - ... 标题的上下都应留一个空行。优先使用描述功能的现在进行时动名词短语,例如:

// MARK: - Working with Child Items

public func addChild(_ child: Child) { ... }

// MARK: - Managing Life-Cycle

public func start() async throws { ... }

非 public 部分应使用可见性标记:

// MARK: - Internal

internal func prepareStorage() { ... }

// MARK: - Private

private func rebuildIndex() { ... }

不要将 internal 或 private 辅助项混入 public 主题部分。如果 public API 只有一个显而易见的主题,则无需在 internal/private 部分之前添加 // MARK: - <Topic>。

协议遵循的组织方式

根据协议的含义选择遵循声明的位置:

  • 如果协议表示“是一种(is-a)”关系,则将遵循声明写在类型声明中。
    • 示例:public enum BlahError: Error { ... }
  • 如果协议描述类型具备的一种能力(通常是以 -able 结尾的协议),则在同一文件底部的扩展中声明遵循。
    • 在扩展前紧接着使用 // MARK: - <ProtocolName> 标题。
public struct Blah { ... }

// MARK: - Codable

extension Blah: Codable {
    // Codable-specific implementation
}
避免使用 Foundation 的策略

在核心库代码中尽可能避免使用 Foundation:

  • 面向以下目标的库,其 public API 应避免使用 Foundation 类型(Data、Date、UUID 等):
    • Embedded Swift
    • 跨平台一致性
    • 减小二进制大小(FoundationEssentials 为 15-40MB)
  • 改用 Swift 标准库类型:
    • 使用 [UInt8] 代替 Data 作为字节缓冲区
    • 使用 ContinuousClock.Instant 或自定义类型代替 Date
    • 使用基于字节的初始化方法,而非 UUID 字符串
  • 始终使用 internal import Foundation 或 internal import FoundationEssentials,切勿使用 public import
#if canImport(FoundationEssentials)
    internal import FoundationEssentials
#else
    internal import Foundation
#endif
InternalImportsByDefault 功能

在 Package.swift 中使用 InternalImportsByDefault 时,除非显式标记为 public import,否则所有导入默认都是 internal。

何时使用 public import:

  • 导入模块中的类型会在 public API 中公开时(返回类型、参数、协议遵循)
  • 示例:在 public 类型中遵循 ServiceLifecycle.Service 时,使用 public import ServiceLifecycle
  • 切勿使用 public import Foundation——应将 Foundation 保持为 internal
特定平台的文件组织

使用 +platform 后缀约定来组织特定平台的实现:

  • PlatformDeviceDiscovery+macos.swift - macOS 实现
  • PlatformDeviceDiscovery+linux.swift - Linux 实现
  • PlatformDeviceDiscovery+default.swift - 其他平台的回退实现

向协议添加方法时,必须更新所有平台文件,以保持符合性。

Linux C 库支持

支持 Glibc 和 Musl,以兼容 Linux:

#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
import Darwin
#elseif canImport(Glibc)
import Glibc
#elseif canImport(Musl)
import Musl
#endif
避免在选择逻辑中编写重复代码

按优先级从多个选项中进行选择时,请使用排序,而不是编写多个条件代码块:

不佳 - 重复代码:

if !preferBluetooth {
    for interface in interfaces {
        if case .lan(let device) = interface {
            return .lan(device)
        }
    }
}
for interface in interfaces {
    if case .bluetooth(let device) = interface {
        return .bluetooth(device)
    }
}
if preferBluetooth {
    for interface in interfaces {
        if case .lan(let device) = interface {
            return .lan(device)
        }
    }
}

良好 - 排序一次,遍历一次:

let sorted = interfaces.sorted { a, b in
    if preferBluetooth {
        return a.type == "Bluetooth" && b.type != "Bluetooth"
    } else {
        return a.type == "LAN" && b.type != "LAN"
    }
}

for interface in sorted {
    switch interface {
    case .lan(let device): return .lan(device)
    case .bluetooth(let device): return .bluetooth(device)
    default: continue
    }
}
内存安全模式(Swift 6.2+)

Swift 6.2 引入了可选启用的严格内存安全检查,可在 Package.swift 中通过 .strictMemorySafety() 开启。

Span 生命周期约束:

  • Span<T> 依赖生命周期——它借用其底层存储的内存
  • 不能跨越异步边界
  • 不能逃逸闭包作用域
  • 不能传递给异步回调

解决方案:非对称 API 设计 使用 Span 进行解析(只读、同步、借用),使用 [UInt8] 进行写入(自有,可跨越边界):

public struct Characteristic<Value: Sendable>: Sendable {
    // Parsing uses Span - borrowed, synchronous access
    internal let parse: @Sendable (borrowing Span<UInt8>) throws -> Value

    // Writing uses [UInt8] - owned, can cross closure boundaries
    public typealias WithBytes = ([UInt8]) -> Void
    internal let write: @Sendable (Value) -> (WithBytes) -> Void
}

从字节安全地加载整数:

// UNSAFE: unsafeLoad
return span.bytes.unsafeLoad(as: UInt64.self)

// SAFE: Manual byte-by-byte assembly
var value: UInt64 = 0
for i in 0..<8 {
    value |= UInt64(span[i]) << (i * 8)
}
return value
基于 Span 的计算属性与 _read/_modify

借助实验性功能 LifetimeDependence,计算属性可以使用 _read 和 _modify 访问器返回 RawSpan 和 MutableRawSpan 等不可逃逸类型:

// Enable in Package.swift:
swiftSettings: [
    .enableExperimentalFeature("LifetimeDependence"),
]

// Read-only span access
public var bytes: RawSpan {
    _read {
        var mapInfo = GstMapInfo()
        guard mapBuffer(&mapInfo) else { fatalError("Failed to map") }
        defer { unmapBuffer(&mapInfo) }
        yield RawSpan(_unsafeStart: mapInfo.data, byteCount: Int(mapInfo.size))
    }
}

// Mutable span access with Copy-on-Write
public var mutableBytes: MutableRawSpan {
    _read {
        fatalError("Cannot read mutableBytes")
    }
    _modify {
        // Ensure unique ownership before write (CoW)
        if !isKnownUniquelyReferenced(&storage) {
            storage = storage.copy()!
        }
        var mapInfo = GstMapInfo()
        guard mapBuffer(&mapInfo) else { fatalError("Failed to map") }
        defer { unmapBuffer(&mapInfo) }
        var span = MutableRawSpan(_unsafeStart: mapInfo.data, byteCount: Int(mapInfo.size))
        yield &span
    }
}

已知编译器问题(Swift 6.2.3): 在某些上下文中使用 span.withUnsafeBytes 时,LifetimeDependenceScopeFixup pass 可能会崩溃。解决方法:为 C 互操作提供独立的基于闭包的方法,避免经过 span 访问器。

不可复制类型

对于不应复制的仅移动类型,请使用 ~Copyable:

public struct ResourceHandle: ~Copyable {
    // Can only be moved, not copied
}

public struct ServiceRegistration: @unchecked Sendable, ~Copyable { ... }

评论

支持 Markdown