Idiomatic Swift patterns, XCTest testing, SwiftLint, and build commands for iOS/macOS projects. Covers value types, optionals, concurrency (async/await, actors), error handling, and Swift Package Manager. Use when working on Swift projects.
Idiomatic Swift conventions, testing patterns, and toolchain reference for iOS/macOS/Swift CLI projects.
Prefer struct over class. Use class only when:
// ✓ Prefer
struct User { var name: String; var email: String }
// Use class only when needed
final class NetworkSession { /* reference semantics */ }
!) in production codeguard let / if let / instead??@unchecked Sendable and ! are code review flags// ✓
guard let user = getUser() else { return }
// ✗ in production
let user = getUser()!
// ✓ async/await
func fetchUser(id: UUID) async throws -> User {
let data = try await URLSession.shared.data(from: url).0
return try JSONDecoder().decode(User.self, from: data)
}
// ✓ actors for shared mutable state
actor UserCache {
private var cache: [UUID: User] = [:]
func get(_ id: UUID) -> User? { cache[id] }
func set(_ id: UUID, _ user: User) { cache[id] = user }
}
// ✓ typed errors
enum NetworkError: Error {
case notFound, unauthorized, serverError(Int)
}
// ✓ Result type for async boundaries
func loadUser() async -> Result<User, NetworkError> { ... }
import XCTest
@testable import MyApp
final class UserTests: XCTestCase {
func testUserCreation() throws {
let user = User(name: "Alice", email: "[email protected]")
XCTAssertEqual(user.name, "Alice")
}
func testAsyncFetch() async throws {
let user = try await UserService().fetch(id: testUUID)
XCTAssertNotNil(user)
}
}
Run tests:
swift test # all tests
swift test --filter UserTests # specific test class
xcodebuild test -scheme MyApp -destination 'platform=iOS Simulator,name=iPhone 15'
swift build # debug build
swift build -c release # release build
swift run MyTarget # run executable target
swift package resolve # resolve dependencies
swift package update # update dependencies
swiftlint # lint current directory
swiftlint --fix # auto-fix violations
swiftlint lint --reporter json # JSON report
# .swiftlint.yml (minimal config)
disabled_rules:
- trailing_whitespace
opt_in_rules:
- empty_count
- explicit_init
line_length: 120
| Pitfall | Fix |
|---|---|
| Force-unwrap in production | Use guard let or ?? |
| Capture list in closures | Use [weak self] to avoid retain cycles |
DispatchQueue + async/await mixed | Pick one concurrency model per module |
Unhandled try? swallowing errors | Log or propagate errors explicitly |
Large struct copied repeatedly | Consider class or @_disfavoredOverload |
rules/swift/coding-style.md