Files
obsidian-vault/work/wiki/apple-browsers/code-style.md
T

23 KiB

source, confidence, namespace, last_synced, alwaysApply
source confidence namespace last_synced alwaysApply
~/DuckDuckGo/apple-browsers.git/main/.cursor/rules/code-style.mdc 0.9 work 2026-04-28 true

Swift Code Style Guide

This style guide is based on the official iOS style guide and incorporates DuckDuckGo-specific patterns and requirements.

Correctness

Strive to make your code compile without warnings. This rule informs many style decisions such as using #selector types instead of string literals.

SwiftLint

We use SwiftLint for enforcing Swift style and conventions. See the SwiftLint configuration for specific rules.

Key SwiftLint settings:

  • Line length: 150 characters (not the default 100)
  • Force cast/try: warnings (not errors for pragmatic development)
  • Identifier naming: flexible for single-letter variables in closures

Naming Conventions

Follow the Swift API Design Guidelines with these key principles:

Core Principles

  • Clarity at the call site over brevity
  • Use camelCase (not snake_case)
  • UpperCamelCase for types and protocols
  • lowerCamelCase for everything else
  • Include all needed words while omitting needless words
  • Use names based on roles, not types

Type Names

// ✅ CORRECT: Descriptive, UpperCamelCase
class UserAuthenticationManager { }
struct BookmarkItem { }
enum NavigationState { }
protocol DataSourceProtocol { }

// ❌ INCORRECT: Too generic
class Manager { }
struct Data { }

Variable and Function Names

// ✅ CORRECT: Descriptive lowerCamelCase
let maximumRetryCount = 3
var isLoading = false
func fetchUserData() { }

// Boolean properties should read like assertions
var isEnabled: Bool
var hasCompleted: Bool
var canDelete: Bool

// ❌ INCORRECT: Abbreviations and unclear names
let usrMgr = UserManager()
func calcTotal() { }

Protocol Naming

// ✅ CORRECT: Capability protocols end in -able, -ible, -ing
protocol Loadable { }
protocol Refreshable { }
protocol UserAuthenticating { }

// ✅ CORRECT: Type protocols are nouns
protocol DataSource { }
protocol Delegate { }

Method Naming Patterns

// ✅ CORRECT: Method naming patterns
// Factory methods begin with "make"
func makeLocationManager() -> CLLocationManager

// Verb methods follow -ed, -ing rule for non-mutating
func sorted() -> [Element]  // non-mutating
func sort()                 // mutating

// Boolean methods read like assertions
func canDelete() -> Bool
func hasCompleted() -> Bool

Delegate Methods

When creating custom delegate methods, the unnamed first parameter should be the delegate source:

// ✅ CORRECT: Delegate pattern
func namePickerView(_ namePickerView: NamePickerView, didSelectName name: String)
func namePickerViewShouldReload(_ namePickerView: NamePickerView) -> Bool

// ❌ INCORRECT: Missing source parameter
func didSelectName(namePicker: NamePickerViewController, name: String)
func namePickerShouldReload() -> Bool

Use Type Inferred Context

Use compiler inferred context to write shorter, clear code:

// ✅ CORRECT: Type inferred context
let selector = #selector(viewDidLoad)
view.backgroundColor = .red
let toView = context.view(forKey: .to)
let view = UIView(frame: .zero)

// ❌ INCORRECT: Redundant type information
let selector = #selector(ViewController.viewDidLoad)
view.backgroundColor = UIColor.red
let toView = context.view(forKey: UITransitionContextViewKey.to)
let view = UIView(frame: CGRect.zero)

Generics

Generic type parameters should be descriptive, UpperCamelCase names:

// ✅ CORRECT: Descriptive generic names
struct Stack<Element> { ... }
func write<Target: OutputStream>(to target: inout Target)
func swap<T>(_ a: inout T, _ b: inout T)  // T is acceptable when no meaningful relationship

// ❌ INCORRECT: Non-descriptive or wrong case
struct Stack<T> { ... }
func write<target: OutputStream>(to target: inout target)

Language

Use US English spelling to match Apple's API:

// ✅ CORRECT: US English
let color = "red"

// ❌ INCORRECT: British English
let colour = "red"

Code Organization

File Structure

// 1. Import statements (minimal - only what's needed)
import UIKit
import Combine

// 2. Protocol definitions
protocol FeatureDelegate: AnyObject {
    func featureDidUpdate()
}

// 3. Main type declaration
class FeatureViewController: UIViewController {
    // Properties first
    private let viewModel: FeatureViewModel
    
    // Lifecycle methods
    override func viewDidLoad() {
        super.viewDidLoad()
        setupUI()
    }
    
    // Private methods
    private func setupUI() { }
}

// 4. Extensions for protocol conformance
// MARK: - UITableViewDataSource
extension FeatureViewController: UITableViewDataSource {
    // Protocol methods
}

Protocol Conformance

Prefer separate extensions for protocol conformance to keep related methods grouped:

// ✅ CORRECT: Separate extensions
class MyViewController: UIViewController {
    // class implementation
}

// MARK: - UITableViewDataSource
extension MyViewController: UITableViewDataSource {
    // table view data source methods
}

// MARK: - UIScrollViewDelegate
extension MyViewController: UIScrollViewDelegate {
    // scroll view delegate methods
}

// ❌ INCORRECT: All in main class declaration
class MyViewController: UIViewController, UITableViewDataSource, UIScrollViewDelegate {
    // all methods mixed together
}

Minimal Imports

Import only the modules a source file requires:

// ✅ CORRECT: Minimal imports
import UIKit
var view: UIView
var deviceModels: [String]

// ✅ CORRECT: Foundation when UIKit not needed
import Foundation
var deviceModels: [String]

// ❌ INCORRECT: Unnecessary imports
import UIKit
import Foundation  // UIKit already includes Foundation
var view: UIView
var deviceModels: [String]

Remove Unused Code

Remove unused (dead) code, including Xcode template code:

// ✅ CORRECT: Keep only implemented methods
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return Database.contacts.count
}

// ❌ INCORRECT: Template code and unused methods
override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}

override func numberOfSections(in tableView: UITableView) -> Int {
    // #warning Incomplete implementation, return the number of sections
    return 1
}

Formatting and Style

Line Breaks and Length

  • Line margin: 150 characters (not the default 100)
  • Long lines should be wrapped at around 150 characters
  • Avoid trailing whitespace at ends of lines
  • Add single newline at end of each file

Spacing

  • Indent using 4 spaces rather than tabs
  • Method braces open on same line, close on new line
  • One blank line between methods
  • No blank lines after opening brace or before closing brace
// ✅ CORRECT: Spacing and braces
if user.isHappy {
    // Do something
} else {
    // Do something else
}

// ❌ INCORRECT: Wrong brace placement
if user.isHappy
{
    // Do something
}
else {
    // Do something else
}

Colons

Colons have no space on left, one space on right. Exceptions: ternary operator ? :, empty dictionary [:], #selector syntax:

// ✅ CORRECT: Colon spacing
class TestDatabase: Database {
    var data: [String: CGFloat] = ["A": 1.2, "B": 3.2]
}

// ❌ INCORRECT: Wrong colon spacing
class TestDatabase : Database {
    var data :[String:CGFloat] = ["A" : 1.2, "B":3.2]
}

Function Parameters

Closing parentheses should not appear on line by themselves:

// ✅ CORRECT: Closing parenthesis placement
let user = try await getUser(
    for: userID,
    on: connection)

// ❌ INCORRECT: Closing parenthesis on own line
let user = try await getUser(
    for: userID,
    on: connection
)

Function Declarations

Short Functions

Keep short function declarations on one line:

// ✅ CORRECT: Short function on one line
func reticulateSplines(spline: [Double]) -> Bool {
    // implementation
}

Long Function Signatures

For functions with long signatures, put each parameter on new line:

// ✅ CORRECT: Long signature formatting
func reticulateSplines(spline: [Double],
                      adjustmentFactor: Double,
                      translateConstant: Int,
                      comment: String) -> Bool {
    // implementation
}

Return Types

Use Void for closure/function outputs, () for inputs:

// ✅ CORRECT: Return type formatting
func updateConstraints() -> Void {
    // implementation
}

typealias CompletionHandler = (result) -> Void

// ❌ INCORRECT: Wrong return type syntax
func updateConstraints() -> () {
    // implementation
}

typealias CompletionHandler = (result) -> ()

Function Calls

Mirror function declaration style at call sites:

// ✅ CORRECT: Single line when it fits
let success = reticulateSplines(splines)

// ✅ CORRECT: Multi-line when wrapped
let success = reticulateSplines(
    spline: splines,
    adjustmentFactor: 1.3,
    translateConstant: 2,
    comment: "normalize the display")

Closure Expressions

Trailing Closure Syntax

Use trailing closure syntax only for single closure at end:

// ✅ CORRECT: Trailing closure usage
UIView.animate(withDuration: 1.0) {
    self.myView.alpha = 0
}

UIView.animate(withDuration: 1.0, animations: {
    self.myView.alpha = 0
}, completion: { finished in
    self.myView.removeFromSuperview()
})

// ❌ INCORRECT: Trailing closure with multiple closures
UIView.animate(withDuration: 1.0, animations: {
    self.myView.alpha = 0
}) { f in
    self.myView.removeFromSuperview()
}

Single-Expression Closures

Use implicit returns for single-expression closures:

// ✅ CORRECT: Implicit return
attendeeList.sort { a, b in
    a > b
}

Chained Methods

Format chained methods for clarity:

// ✅ CORRECT: Chained methods - compact when clear
let value = numbers.map { $0 * 2 }.filter { $0 % 3 == 0 }.index(of: 90)

// ✅ CORRECT: Chained methods - multi-line when complex
let value = numbers
    .map { $0 * 2 }
    .filter { $0 > 50 }
    .map { $0 + 10 }

Types and Constants

Native Types

Always use Swift's native types when available:

// ✅ CORRECT: Native Swift types
let width = 120.0                    // Double
let widthString = "\(width)"         // String

// ❌ INCORRECT: Objective-C types
let width: NSNumber = 120.0          // NSNumber
let widthString: NSString = width.stringValue  // NSString

Constants vs Variables

Use let by default, change to var only when compiler complains:

// ✅ CORRECT: Type properties for constants
enum Math {
    static let e = 2.718281828459045235360287
    static let root2 = 1.41421356237309504880168872
}

let hypotenuse = side * Math.root2

// ❌ INCORRECT: Global constants
let e = 2.718281828459045235360287  // pollutes global namespace
let root2 = 1.41421356237309504880168872

Type Inference

Prefer compact code and let compiler infer types:

// ✅ CORRECT: Type inference
let message = "Click the button"
let currentBounds = computeViewBounds()
var names = ["Mic", "Sam", "Christine"]
let maximumWidth: CGFloat = 106.5  // Specify when needed

// ❌ INCORRECT: Unnecessary type annotations
let message: String = "Click the button"
let currentBounds: CGRect = computeViewBounds()

Empty Collections

Use type annotation for empty arrays and dictionaries:

// ✅ CORRECT: Type annotation for empty collections
var names: [String] = []
var lookup: [String: Int] = [:]

// ❌ INCORRECT: Constructor syntax
var names = [String]()
var lookup = [String: Int]()

Syntactic Sugar

Prefer shortcut type declarations:

// ✅ CORRECT: Syntactic sugar
var deviceModels: [String]
var employees: [Int: String]
var faxNumber: Int?

// ❌ INCORRECT: Full generics syntax
var deviceModels: Array<String>
var employees: Dictionary<Int, String>
var faxNumber: Optional<Int>

Optionals

Optional Declarations

Use ? for optional types, ! only when you know initialization timing:

// ✅ CORRECT: Optional usage
var subview: UIView?
var volume: Double?

// Use ! only for outlets that initialize in viewDidLoad
@IBOutlet weak var tableView: UITableView!

Optional Binding

Shadow original names in optional binding:

// ✅ CORRECT: Shadow original name
if let subview = subview, let volume = volume {
    // do something with unwrapped subview and volume
}

// ❌ INCORRECT: Different names for unwrapped values
if let unwrappedSubview = optionalSubview {
    if let realVolume = volume {
        // do something with unwrappedSubview and realVolume
    }
}

Optional Chaining vs Binding

Use optional chaining for single access, binding for multiple operations:

// ✅ CORRECT: Optional chaining for single access
textContainer?.textLabel?.setNeedsDisplay()

// ✅ CORRECT: Optional binding for multiple operations
if let textContainer = textContainer {
    // do many things with textContainer
}

Memory Management

Reference Cycles

Prevent reference cycles with weak and unowned references.

Example: See memory-management.swift

Lazy Initialization

Use lazy initialization for fine-grained control:

// ✅ CORRECT: Lazy initialization
lazy var locationManager = makeLocationManager()

private func makeLocationManager() -> CLLocationManager {
    let manager = CLLocationManager()
    manager.desiredAccuracy = kCLLocationAccuracyBest
    manager.delegate = self
    manager.requestAlwaysAuthorization()
    return manager
}

Access Control

Access Control Order

Access control comes first, except for static and attributes:

// ✅ CORRECT: Access control ordering
private let message = "Great Scott!"

class TimeMachine {
    private dynamic lazy var fluxCapacitor = FluxCapacitor()
    @IBAction private func activate() { }
    static private let timeConstant = 88.0
}

// ❌ INCORRECT: Wrong ordering
fileprivate let message = "Great Scott!"

class TimeMachine {
    lazy dynamic private var fluxCapacitor = FluxCapacitor()
}

Private vs Fileprivate

Prefer private to fileprivate; use fileprivate only when compiler requires it.

Control Flow

Loop Style

Prefer for-in style over while-condition-increment:

// ✅ CORRECT: for-in style
for _ in 0..<3 {
    print("Hello three times")
}

for (index, person) in attendeeList.enumerated() {
    print("\(person) is at position #\(index)")
}

// ❌ INCORRECT: while style
var i = 0
while i < 3 {
    print("Hello three times")
    i += 1
}

Ternary Operator

Use ternary operator only when it increases clarity:

// ✅ CORRECT: Simple ternary usage
let value = 5
result = value != 0 ? x : y

let isHorizontal = true
result = isHorizontal ? x : y

// ❌ INCORRECT: Complex nested ternary
result = a > b ? x = c > d ? c : d : y

Golden Path

Use the "golden path" pattern - don't nest if statements:

// ✅ CORRECT: Golden path with guard
func computeFFT(context: Context?, inputData: InputData?) throws -> Frequencies {
    guard let context = context else {
        throw FFTError.noContext
    }
    guard let inputData = inputData else {
        throw FFTError.noInputData
    }
    
    // use context and input to compute the frequencies
    return frequencies
}

// ❌ INCORRECT: Nested if statements
func computeFFT(context: Context?, inputData: InputData?) throws -> Frequencies {
    if let context = context {
        if let inputData = inputData {
            // use context and input to compute the frequencies
            return frequencies
        } else {
            throw FFTError.noInputData
        }
    } else {
        throw FFTError.noContext
    }
}

Compound Guard Statements

Use compound guard for multiple optionals:

// ✅ CORRECT: Compound guard
guard 
    let number1 = number1,
    let number2 = number2,
    let number3 = number3 
else {
    fatalError("impossible")
}

// ❌ INCORRECT: Nested optional binding
if let number1 = number1 {
    if let number2 = number2 {
        if let number3 = number3 {
            // do something with numbers
        }
    }
}

Class and Struct Definitions

Example Well-Styled Class

final class Circle: Shape {
    var x: Int, y: Int
    var radius: Double
    var diameter: Double {
        get {
            radius * 2
        }
        set {
            radius = newValue / 2
        }
    }
    
    init(x: Int, y: Int, radius: Double) {
        self.x = x
        self.y = y
        self.radius = radius
    }
    
    convenience init(x: Int, y: Int, diameter: Double) {
        self.init(x: x, y: y, radius: diameter / 2)
    }
    
    override func area() -> Double {
        Double.pi * radius * radius
    }
}

extension Circle: CustomStringConvertible {
    var description: String {
        "center = \(centerString) area = \(area())"
    }
    
    private var centerString: String {
        "(\(x),\(y))"
    }
}

Use of Self

Avoid using self unless required by compiler:

// ✅ CORRECT: Self only when required
class PhotoViewController: UIViewController {
    var image: UIImage
    
    init(image: UIImage) {
        self.image = image  // Required to disambiguate
        super.init(nibName: nil, bundle: nil)
    }
    
    func setupImageView() {
        imageView.image = image  // self not needed
    }
}

Computed Properties

Omit get clause for read-only computed properties:

// ✅ CORRECT: Implicit get for read-only
var diameter: Double {
    radius * 2
}

// ❌ INCORRECT: Unnecessary get clause
var diameter: Double {
    get {
        return radius * 2
    }
}

Final

Use final when inheritance is not intended:

// ✅ CORRECT: Final for utility classes
final class Box<T> {
    let value: T
    init(_ value: T) {
        self.value = value
    }
}

DuckDuckGo-Specific Patterns

Design System Integration (MANDATORY)

ALWAYS use DesignResourcesKit for colors, typography, and icons.

Example: See design-system-integration.swift

Dependency Injection Pattern

Use AppDependencyProvider for dependency injection.

Example: See dependency-injection.swift

Async/Await Patterns

Example: See async-await-pattern.swift

Property Wrappers

Example: See property-wrappers.swift

Comments and Documentation

When to Comment

Use comments to explain WHY, not WHAT:

// ✅ CORRECT: Explains why
// We delay the animation to avoid conflicting with the previous transition
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
    self.animateTransition()
}

// ❌ INCORRECT: Explains what (obvious from code)
// Set the background color to red
view.backgroundColor = .red

Comment Style

Prefer double/triple-slash over C-style comments:

// ✅ CORRECT: Swift-style comments
// This is a comment
/// This is a documentation comment

// ❌ INCORRECT: C-style comments
/* This is a comment */

String Literals

Multi-line Strings

Use multi-line string syntax for long strings:

// ✅ CORRECT: Multi-line string formatting
let message = """
    You cannot charge the flux \
    capacitor with a 9V battery.
    You must use a super-charger \
    which costs 10 credits. You currently \
    have \(credits) credits available.
    """

// ❌ INCORRECT: Concatenation or inline text
let message = """You cannot charge the flux \
    capacitor with a 9V battery.
    You must use a super-charger \
    which costs 10 credits. You currently \
    have \(credits) credits available.
    """

Prohibited Patterns

No Emoji

Do not use emoji in code - it creates unnecessary friction:

// ❌ FORBIDDEN: Emoji in code
let isHappy = true 😀
func celebrate() 🎉 { }

// ✅ CORRECT: Clear, text-based names
let isHappy = true
func celebrate() { }

No Color/Image Literals

Do not use #colorLiteral or #imageLiteral - they're hard to read and maintain:

// ❌ FORBIDDEN: Literals
let color = #colorLiteral(red: 1, green: 0, blue: 0, alpha: 1)
let image = #imageLiteral(resourceName: "icon")

// ✅ CORRECT: Explicit constructors (but prefer DesignResourcesKit)
let color = UIColor(red: 1, green: 0, blue: 0, alpha: 1)
let image = UIImage(named: "icon")

// ✅ BEST: DesignResourcesKit
let color = UIColor(designSystemColor: .accent)
let image = DesignSystemImages.Color.Size24.bookmark

No Parentheses Around Conditionals

Don't use unnecessary parentheses:

// ✅ CORRECT: No parentheses needed
if name == "Hello" {
    print("World")
}

// ❌ INCORRECT: Unnecessary parentheses
if (name == "Hello") {
    print("World")
}

No Semicolons

Swift doesn't require semicolons - don't use them:

// ✅ CORRECT: No semicolons
let swift = "not a scripting language"

// ❌ INCORRECT: Unnecessary semicolons
let swift = "not a scripting language";

Error Handling and Assertions

Fatal Errors

Use fatalError() when app reaches unrecoverable state:

// ✅ CORRECT: Fatal error for impossible states
guard let viewController = storyboard.instantiateViewController(withIdentifier: "Main") as? MainViewController else {
    fatalError("Failed to instantiate MainViewController from storyboard")
}

Assertions

Use assert() and assertionFailure() for recoverable but unexpected states:

// ✅ CORRECT: Assert for development debugging
func processItems(_ items: [Item]) {
    assert(!items.isEmpty, "Items array should not be empty")
    
    // Handle empty array gracefully in release builds
    guard !items.isEmpty else { return }
    
    // Process items...
}

Logging

Use unified logging system for all logging.

Example: See logging-pattern.swift

See Logging Guidelines for comprehensive logging patterns.

Unit Test Naming

Use "when/then" convention for test names:

// ✅ CORRECT: When/then test naming
func testWhenUrlIsNotATrackerThenMatchesIsFalse() { }
func testWhenUserTapsBookmarkButtonThenBookmarkIsAdded() { }
func testWhenNetworkFailsThenErrorIsDisplayed() { }

// ❌ INCORRECT: Unclear test names
func testBookmarks() { }
func testNetworking() { }

Functions vs Methods

Prefer methods over free functions for discoverability:

// ✅ CORRECT: Methods are easily discoverable
let sorted = items.mergeSorted()
rocket.launch()

// ❌ INCORRECT: Free functions are hard to discover
let sorted = mergeSort(items)
launch(&rocket)

// ✅ ACCEPTABLE: Free functions that feel natural
let tuples = zip(a, b)
let value = max(x, y, z)

Remember: This style guide ensures consistency across the DuckDuckGo browser codebase. When in doubt, prioritize clarity and follow the patterns established in existing code.