Skip to main content

Tools

Rockxy enforces code style with two tools. Configuration lives in the repository root:
  • SwiftLint.swiftlint.yml — linting rules and complexity limits
  • SwiftFormat.swiftformat — automatic formatting
# Run both before every commit
swiftlint lint --strict
swiftformat .

Formatting

RuleValue
Indentation4 spaces (never tabs, except Makefile/pbxproj)
Line length120 characters target (SwiftFormat); SwiftLint warns at 180, errors at 300
BracesK&R style (opening brace on same line)
Line endingsLF
SemicolonsNone
Trailing commasNone

Import Order

System frameworks first (alphabetically), then third-party packages, then local modules. Separate groups with a blank line:
import Foundation
import SwiftUI

import NIO
import SQLite

import RockxyCore

Access Control

Always specify access control explicitly — on the extension when all members share the same level, on individual members otherwise:
// Preferred — access control on the extension
private extension ProxyServer {
    func handleRequest(_ request: HTTPRequest) { ... }
    func forwardToUpstream(_ data: ByteBuffer) { ... }
}

// Also acceptable — explicit on each member
extension ProxyServer {
    internal func start() async throws { ... }
    private func configureBootstrap() { ... }
}
// Bad — missing access control
func processRequest() { ... }

Optionals

No force unwrapping (!) or force casting (as!) anywhere in the codebase. Use safe alternatives:
// Good
guard let response = transaction.response else {
    Self.logger.warning("Transaction has no response")
    return
}

if let contentType = headers["Content-Type"] as? String {
    detectFormat(contentType)
}

// Bad — will crash on nil
let response = transaction.response!
let contentType = headers["Content-Type"] as! String

Logging

Use OSLog through the Logger API. Every type that logs should declare a static logger:
import os

final class CertificateManager {
    private static let logger = Logger(
        subsystem: "com.amunx.Rockxy",
        category: "CertificateManager"
    )

    func generateCertificate(forHost host: String) async throws -> Certificate {
        Self.logger.info("Generating certificate for \(host)")
        // ...
        Self.logger.debug("Certificate cached, \(cacheSize) entries total")
    }
}
Never use print() in production code. All logging must go through OSLog so it appears in Console.app and can be filtered by subsystem and category.

Localization

Use String(localized:) for all user-facing strings in non-SwiftUI code:
let message = String(localized: "proxy.started.message")
SwiftUI view literals auto-localize — Text("Start Proxy") and Button("Clear Session") are automatically looked up in the strings catalog. Do not wrap these in String(localized:). Do not localize technical terms (HTTP, TLS, WebSocket, GraphQL, etc.).

Naming Conventions

ElementConventionExample
Types, protocols, enumsUpperCamelCaseProxyServer, InspectorPlugin
Functions, variableslowerCamelCasestartProxy(), transactionCount
Booleansis/has/should prefixisRecording, hasResponse, shouldIntercept
ConstantslowerCamelCasedefaultPort, maxBufferSize
Enum caseslowerCamelCase.connecting, .tlsHandshake

Comments

Do not add comments that restate what the code already says. Only comment to explain non-obvious reasoning:
// Bad — restates the code
// Check if the transaction has a response
guard let response = transaction.response else { return }

// Good — explains the "why"
// NIO delivers chunks out of order under back-pressure;
// reassemble by sequence number before forwarding
chunks.sort(by: \.sequenceNumber)

SwiftLint Limits

MetricWarningError
File length1,200 lines1,800 lines
Type body1,100 lines1,500 lines
Function body160 lines250 lines
Cyclomatic complexity4060
When approaching these limits, extract logic into extension files following the MainContentCoordinator pattern — e.g., TypeName+Category.swift. Group by domain logic, not arbitrary line counts.

Commits

Follow Conventional Commits. Single line, no description body:
feat: add WebSocket frame inspector
fix: prevent crash on empty GraphQL response
refactor: extract certificate cache into dedicated actor
docs: update keyboard shortcuts page
test: add proxy pipeline integration tests
Common prefixes: feat, fix, refactor, docs, test, chore, perf.

Branch Naming

Create branches from main with a prefix matching the work type:
feat/websocket-binary-frames
fix/proxy-crash-malformed-headers
docs/api-grouping-guide

Next Steps

Architecture

Understand the actor model, coordinator pattern, and data flow

Building

Build from source, run tests, and lint your changes