Major updates: - Added 35+ new skills from awesome-opencode-skills and antigravity repos - Merged SEO skills into seo-master - Merged architecture skills into architecture - Merged security skills into security-auditor and security-coder - Merged testing skills into testing-master and testing-patterns - Merged pentesting skills into pentesting - Renamed website-creator to thai-frontend-dev - Replaced skill-creator with github version - Removed Chutes references (use MiniMax API instead) - Added install-openclaw-skills.sh for cross-platform installation - Updated .env.example with MiniMax API credentials
15 KiB
Swift Coding Standards
Best practices for writing clean, safe, and idiomatic Swift code following Apple's guidelines and modern Swift conventions.
1. Optionals and Safety
Impact: CRITICAL
Swift's optional system eliminates null pointer exceptions through compile-time safety.
1.1 Safe Unwrapping with if let
if let name = optionalName {
print("Hello, \(name)")
}
// Multiple bindings
if let name = userName, let age = userAge, age >= 18 {
print("\(name) is an adult")
}
1.2 Guard for Early Exit
Use guard to exit early when preconditions aren't met:
func processUser(_ user: User?) {
guard let user = user else { return }
guard !user.name.isEmpty else { return }
print(user.name)
}
1.3 Nil Coalescing for Defaults
let displayName = name ?? "Anonymous"
let count = items?.count ?? 0
1.4 Optional Chaining
let count = user?.profile?.posts?.count
let uppercased = optionalString?.uppercased()
1.5 Optional map/flatMap
let uppercasedName = userName.map { $0.uppercased() }
let userID = userIDString.flatMap { Int($0) }
1.6 Never Force Unwrap
Avoid ! force unwrapping. Use safe alternatives:
| Instead of | Use |
|---|---|
value! |
if let value = value { } |
array[0] (unsafe) |
array.first |
dictionary["key"]! |
dictionary["key", default: defaultValue] |
2. Naming Conventions
Impact: HIGH
2.1 Types: PascalCase
class UserProfileViewController { }
struct NetworkRequest { }
protocol DataSource { }
enum LoadingState { }
2.2 Variables and Functions: camelCase
var userName: String
let maximumRetryCount = 3
func fetchUserProfile() { }
2.3 Boolean Naming
Use is, has, should, can prefixes:
var isLoading: Bool
var hasCompletedOnboarding: Bool
var shouldShowAlert: Bool
var canEditProfile: Bool
2.4 Function Naming
Use verb phrases, read like natural English:
// Good - clear actions
func fetchUsers() async throws -> [User]
func remove(_ item: Item, at index: Int)
func makeIterator() -> Iterator
// Avoid - unclear or redundant
func getUsersData() // "get" is redundant
func doRemove() // vague
2.5 Parameter Labels
First parameter label can be omitted when obvious:
func insert(_ element: Element, at index: Int)
func move(from source: Int, to destination: Int)
3. Protocol-Oriented Design
Impact: HIGH
Swift favors composition over inheritance through protocols.
3.1 Define Capabilities Through Protocols
protocol DataStore {
func save<T: Codable>(_ item: T, key: String) throws
func load<T: Codable>(key: String) throws -> T?
}
protocol Drawable {
var color: Color { get set }
func draw()
}
3.2 Protocol Extensions for Default Behavior
extension Drawable {
func draw() {
print("Drawing with \(color)")
}
}
extension Collection {
func chunked(into size: Int) -> [[Element]] {
guard size > 0 else { return [] }
var result: [[Element]] = []
var i = startIndex
while i != endIndex {
let j = index(i, offsetBy: size, limitedBy: endIndex) ?? endIndex
result.append(Array(self[i..<j]))
i = j
}
return result
}
}
3.3 Associated Types for Flexibility
protocol Repository {
associatedtype Item
func fetchAll() async throws -> [Item]
func save(_ item: Item) async throws
}
class UserRepository: Repository {
typealias Item = User
func fetchAll() async throws -> [User] { /* ... */ }
func save(_ item: User) async throws { /* ... */ }
}
3.4 Protocol Composition
protocol Named { var name: String { get } }
protocol Aged { var age: Int { get } }
func greet(_ person: Named & Aged) {
print("Hello, \(person.name), age \(person.age)")
}
4. Value Types vs Reference Types
Impact: HIGH
4.1 Prefer Structs (Value Types)
Use structs for simple data models, independent copies:
struct User {
var name: String
var email: String
}
struct Point {
var x: Double
var y: Double
}
4.2 Use Classes When Needed
Use classes when identity, shared ownership, or reference semantics matter. Prefer actors for mutable state shared across concurrent tasks:
class NetworkManager {
static let shared = NetworkManager()
private init() { }
}
class FileHandle {
// Wrapping system resource
}
4.3 Enums for Finite States
enum LoadingState {
case idle
case loading
case success(Data)
case failure(Error)
}
enum Result<Success, Failure: Error> {
case success(Success)
case failure(Failure)
}
| Type | Use When |
|---|---|
struct |
Data models, coordinates, independent values |
class |
Shared state, identity matters, inheritance needed |
enum |
Finite set of options, state machines |
5. Memory Management with ARC
Impact: CRITICAL
5.1 Breaking Retain Cycles with weak
class Apartment {
weak var tenant: Person?
}
class Person {
var apartment: Apartment?
}
5.2 Closure Capture Lists
Use capture lists intentionally to avoid retain cycles.
Choose weak or unowned based on lifetime guarantees:
// Weak capture for optional self
onComplete = { [weak self] in
self?.processResult()
}
// Capture specific values
let id = user.id
fetchData { [id] result in
print("Fetched for \(id)")
}
5.3 unowned for Guaranteed Lifetime
Use when reference should never be nil during object lifetime:
class CreditCard {
unowned let customer: Customer
init(customer: Customer) {
self.customer = customer
}
}
| Keyword | Use When |
|---|---|
weak |
Reference may become nil |
unowned |
Reference guaranteed to outlive |
| None | Strong ownership needed |
6. Error Handling
Impact: HIGH
6.1 Define Typed Errors
enum NetworkError: Error {
case invalidURL
case noConnection
case serverError(statusCode: Int)
case decodingFailed(underlying: Error)
}
enum ValidationError: LocalizedError {
case emptyField(name: String)
case invalidFormat(field: String, expected: String)
var errorDescription: String? {
switch self {
case .emptyField(let name):
return "\(name) cannot be empty"
case .invalidFormat(let field, let expected):
return "\(field) must be \(expected)"
}
}
}
6.2 Throwing Functions
func fetchUser(id: Int) throws -> User {
guard let url = URL(string: "https://api.example.com/users/\(id)") else {
throw NetworkError.invalidURL
}
// ... implementation
}
6.3 Do-Catch Handling
do {
let user = try fetchUser(id: 123)
print(user.name)
} catch NetworkError.serverError(let code) {
print("Server error: \(code)")
} catch NetworkError.noConnection {
print("Check your internet connection")
} catch {
print("Unknown error: \(error)")
}
6.4 try? and try!
// try? returns optional (nil on error)
let user = try? fetchUser(id: 123)
// try! crashes on error - use only when failure is programmer error
let config = try! loadBundledConfig()
6.5 Rethrows
func perform<T>(_ operation: () throws -> T) rethrows -> T {
return try operation()
}
7. Modern Concurrency (async/await)
Impact: CRITICAL
Use actor isolation and Sendable to prevent data races across concurrency domains.
7.1 Async Functions
func fetchUser(id: Int) async throws -> User {
guard let url = URL(string: "https://api.example.com/users/\(id)") else {
throw NetworkError.invalidURL
}
let (data, _) = try await URLSession.shared.data(from: url)
return try JSONDecoder().decode(User.self, from: data)
}
// Calling async functions
Task {
do {
let user = try await fetchUser(id: 123)
print(user.name)
} catch {
print("Failed: \(error)")
}
}
7.2 Parallel Execution with TaskGroup
func fetchAllUsers(ids: [Int]) async throws -> [User] {
try await withThrowingTaskGroup(of: User.self) { group in
for id in ids {
group.addTask {
try await fetchUser(id: id)
}
}
return try await group.reduce(into: []) { $0.append($1) }
}
}
7.3 async let for Concurrent Bindings
async let user = fetchUser(id: 1)
async let posts = fetchPosts(userId: 1)
async let followers = fetchFollowers(userId: 1)
let profile = try await ProfileData(
user: user,
posts: posts,
followers: followers
)
7.4 Actors for Thread-Safe State
actor BankAccount {
private var balance: Double = 0
func deposit(_ amount: Double) {
balance += amount
}
func withdraw(_ amount: Double) throws {
guard balance >= amount else {
throw BankError.insufficientFunds
}
balance -= amount
}
func getBalance() -> Double {
balance
}
}
// Usage
let account = BankAccount()
await account.deposit(100)
let balance = await account.getBalance()
7.5 MainActor for UI Updates
@MainActor
class ViewModel: ObservableObject {
@Published var isLoading = false
@Published var users: [User] = []
func loadUsers() async {
isLoading = true
defer { isLoading = false }
do {
users = try await fetchUsers()
} catch {
// Handle error
}
}
}
7.6 Task Cancellation
func fetchWithTimeout() async throws -> Data {
try await withThrowingTaskGroup(of: Data.self) { group in
group.addTask {
try await fetchData()
}
group.addTask {
try await Task.sleep(for: .seconds(10))
throw TimeoutError()
}
let result = try await group.next()!
group.cancelAll()
return result
}
}
// Check for cancellation
func longOperation() async throws {
for item in items {
try Task.checkCancellation()
await process(item)
}
}
8. Access Control
Impact: MEDIUM
8.1 Access Levels
| Level | Scope |
|---|---|
private |
Enclosing declaration only |
fileprivate |
Entire source file |
internal |
Module (default) |
public |
Other modules can access |
open |
Other modules can subclass/override |
8.2 Best Practices
public class UserService {
// Public API
public func fetchUser(id: Int) async throws -> User { }
// Internal helper
func buildRequest(for id: Int) -> URLRequest { }
// Private implementation detail
private let session: URLSession
private var cache: [Int: User] = [:]
}
8.3 Private Setters
public struct Counter {
public private(set) var count = 0
public mutating func increment() {
count += 1
}
}
9. Generics and Type Constraints
Impact: MEDIUM
9.1 Generic Functions
func swapValues<T>(_ a: inout T, _ b: inout T) {
let temp = a
a = b
b = temp
}
9.2 Type Constraints
func findIndex<T: Equatable>(of value: T, in array: [T]) -> Int? {
array.firstIndex(of: value)
}
func decode<T: Decodable>(_ type: T.Type, from data: Data) throws -> T {
try JSONDecoder().decode(type, from: data)
}
9.3 Where Clauses
func allMatch<C: Collection>(_ collection: C, predicate: (C.Element) -> Bool) -> Bool
where C.Element: Equatable {
collection.allSatisfy(predicate)
}
extension Array where Element: Numeric {
func sum() -> Element {
reduce(0, +)
}
}
9.4 Opaque Types (some)
func makeCollection() -> some Collection {
[1, 2, 3]
}
var body: some View {
Text("Hello")
}
10. Property Wrappers
Impact: MEDIUM
10.1 Common SwiftUI Property Wrappers
| Wrapper | Use Case |
|---|---|
@State |
View-local mutable state |
@Binding |
Two-way connection to parent state |
@StateObject |
View-owned observable object |
@ObservedObject |
Passed-in observable object |
@EnvironmentObject |
Shared object from ancestor |
@Environment |
System environment values |
@Published |
Observable property in class |
10.2 Custom Property Wrappers
@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) }
}
init(wrappedValue: Value, _ range: ClosedRange<Value>) {
self.range = range
self.value = min(max(wrappedValue, range.lowerBound), range.upperBound)
}
}
struct Settings {
@Clamped(0...100) var volume: Int = 50
}
Quick Reference
Optionals
if let x = optional { } // Safe unwrap
guard let x = optional else { return } // Early exit
let x = optional ?? default // Default value
optional?.method() // Optional chaining
optional.map { transform($0) } // Transform if present
Common Patterns
// Defer for cleanup
func process() {
let file = openFile()
defer { closeFile(file) }
// ... work with file
}
// Lazy initialization
lazy var expensive: ExpensiveObject = {
ExpensiveObject()
}()
// Type inference
let numbers = [1, 2, 3] // [Int]
let doubled = numbers.map { $0 * 2 } // [Int]
Closure Syntax
// Full syntax
let sorted = names.sorted(by: { (s1: String, s2: String) -> Bool in
return s1 < s2
})
// Shortened
let sorted = names.sorted { $0 < $1 }
// Trailing closure
UIView.animate(withDuration: 0.3) {
view.alpha = 0
}
Checklist
Safety
- No force unwrapping (
!) except for IB outlets and known-safe cases - All optionals handled with
if let,guard let, or?? - No implicitly unwrapped optionals (
!) in data models
Memory
- Escaping closures capture
selfintentionally; use[weak self]or[unowned self]to avoid retain cycles when needed - Delegate properties are
weak - No retain cycles between objects
Concurrency
- Async functions used instead of completion handlers
- Mutable state shared across concurrency domains is isolated, often with actors
- Types crossing concurrency domains use
Sendablewhen appropriate - UI updates on
@MainActor - Task cancellation checked in long operations
Access Control
privateused for implementation detailspublicAPI is minimal and intentional- No unnecessary
internalexposure
Naming
- Types use PascalCase
- Functions and variables use camelCase
- Booleans have
is/has/shouldprefix - Functions read like natural English
Swift and Apple are trademarks of Apple Inc.