--- source: ~/DuckDuckGo/apple-browsers.git/main/.cursor/rules/code-style.mdc confidence: 0.9 namespace: work last_synced: 2026-04-28 alwaysApply: true --- # Swift Code Style Guide *This style guide is based on the [official iOS style guide](iOS/styleguide/STYLEGUIDE.md) 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](https://github.com/realm/SwiftLint) for enforcing Swift style and conventions. See the [SwiftLint configuration](.swiftlint.yml) 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](https://swift.org/documentation/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 ```swift // ✅ CORRECT: Descriptive, UpperCamelCase class UserAuthenticationManager { } struct BookmarkItem { } enum NavigationState { } protocol DataSourceProtocol { } // ❌ INCORRECT: Too generic class Manager { } struct Data { } ``` ### Variable and Function Names ```swift // ✅ 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 ```swift // ✅ 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 ```swift // ✅ 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**: ```swift // ✅ 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: ```swift // ✅ 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**: ```swift // ✅ CORRECT: Descriptive generic names struct Stack { ... } func write(to target: inout Target) func swap(_ a: inout T, _ b: inout T) // T is acceptable when no meaningful relationship // ❌ INCORRECT: Non-descriptive or wrong case struct Stack { ... } func write(to target: inout target) ``` ### Language Use **US English spelling** to match Apple's API: ```swift // ✅ CORRECT: US English let color = "red" // ❌ INCORRECT: British English let colour = "red" ``` ## Code Organization ### File Structure ```swift // 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: ```swift // ✅ 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**: ```swift // ✅ 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: ```swift // ✅ 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** ```swift // ✅ 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: ```swift // ✅ 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**: ```swift // ✅ 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**: ```swift // ✅ 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**: ```swift // ✅ 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**: ```swift // ✅ 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**: ```swift // ✅ 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**: ```swift // ✅ 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**: ```swift // ✅ CORRECT: Implicit return attendeeList.sort { a, b in a > b } ``` ### Chained Methods **Format chained methods for clarity**: ```swift // ✅ 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: ```swift // ✅ 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**: ```swift // ✅ 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**: ```swift // ✅ 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**: ```swift // ✅ 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**: ```swift // ✅ CORRECT: Syntactic sugar var deviceModels: [String] var employees: [Int: String] var faxNumber: Int? // ❌ INCORRECT: Full generics syntax var deviceModels: Array var employees: Dictionary var faxNumber: Optional ``` ## Optionals ### Optional Declarations **Use `?` for optional types, `!` only when you know initialization timing**: ```swift // ✅ 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**: ```swift // ✅ 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**: ```swift // ✅ 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](code-style/memory-management.swift) ### Lazy Initialization **Use lazy initialization for fine-grained control**: ```swift // ✅ 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**: ```swift // ✅ 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`**: ```swift // ✅ 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**: ```swift // ✅ 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**: ```swift // ✅ 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**: ```swift // ✅ 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 ```swift 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**: ```swift // ✅ 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**: ```swift // ✅ 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**: ```swift // ✅ CORRECT: Final for utility classes final class Box { 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](code-style/design-system-integration.swift) ### Dependency Injection Pattern **Use AppDependencyProvider for dependency injection.** **Example:** See [dependency-injection.swift](code-style/dependency-injection.swift) ### Async/Await Patterns **Example:** See [async-await-pattern.swift](code-style/async-await-pattern.swift) ### Property Wrappers **Example:** See [property-wrappers.swift](code-style/property-wrappers.swift) ## Comments and Documentation ### When to Comment **Use comments to explain WHY, not WHAT**: ```swift // ✅ 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**: ```swift // ✅ 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**: ```swift // ✅ 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: ```swift // ❌ 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: ```swift // ❌ 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**: ```swift // ✅ 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: ```swift // ✅ 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**: ```swift // ✅ 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**: ```swift // ✅ 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](code-style/logging-pattern.swift) **See [Logging Guidelines](logging-guidelines.md) for comprehensive logging patterns.** ## Unit Test Naming **Use "when/then" convention for test names**: ```swift // ✅ 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: ```swift // ✅ 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.