关于 swift
面向 Swift 开发者的编程 skill,提供配置、日志、遥测、测试、调试和语言实践的参考指南。按主题加载文档,涵盖从测试迁移到跨平台实现的具体问题。
主要功能
- 01配置与密钥处理指南
- 02Swift Log 结构化日志实践
- 03OpenTelemetry 设置与导出指南
- 04Swift Testing 模式及 XCTest 迁移
- 05内存安全与平台特定约定
适用场景
- 01设置配置和密钥处理
- 02为服务器应用添加日志或 OpenTelemetry
- 03编写 Swift 测试或从 XCTest 迁移
- 04检查跨平台及内存安全模式
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ wendylabsinc/ claude-skills --skill swift安装后在 Claude Code 中输入/swift确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/debugging.mdswift-configuration.mdswift-log.mdswift-otel.mdswift-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。这样可避免意外暴露实现细节,也更容易修复访问级别错误。
成员组织
按以下可见性顺序排列成员:
- Public
- Internal
- 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 { ... }