This document outlines the standards and best practices for Swift development at Bayat.
- Follow the Swift API Design Guidelines
- Use SwiftLint to enforce consistent style
- Maximum line length should be 120 characters
- Use 4 spaces for indentation, not tabs
- Files should be encoded in UTF-8
- Files should end with a newline
- Avoid trailing whitespace
-
General Naming:
- Use clear, descriptive names that communicate purpose
- Favor clarity over brevity
- Avoid abbreviations and acronyms unless widely known
- Use camelCase for all names (except for type names)
-
Types and Protocols:
- Use PascalCase for type names (classes, structures, enumerations, and protocols)
- Name protocols to describe capabilities or behaviors (e.g.,
Equatable
,Codable
) - Name types as nouns
-
Methods and Functions:
- Use verbs for methods and functions that perform actions
- Name mutating methods using imperative verb phrases (e.g.,
sort()
,append()
) - Name non-mutating counterparts using past participles (e.g.,
sorted()
,appended()
)
-
Variables and Properties:
- Use nouns for properties and variables
- Use
is
prefix for Boolean properties (e.g.,isEnabled
,isValid
) - Name computed properties to express what they compute, not how
-
Enumerations:
- Use singular names for enumeration types
- Use lowerCamelCase for enumeration cases
- Consider omitting type name from case when context is clear
-
File Structure:
- One type per file unless types are closely related
- Name files after their main type (e.g.,
UserManager.swift
) - Group related types together in the file system
- Place extensions in the same file as the type they extend if small
-
Code Organization within a File:
- Follow a consistent order of section declarations:
- Type declaration
- Properties
- Initializers
- Public methods
- Private methods
- Protocol conformances
- Use
// MARK:
comments to organize code sections - Group related methods and properties together
- Follow a consistent order of section declarations:
-
Optional Handling:
- Avoid forced unwrapping (
!
) in production code - Use optional binding (
if let
,guard let
) to unwrap optionals - Use optional chaining (
?.
) when appropriate - Use
nil
coalescing operator (??
) for default values
- Avoid forced unwrapping (
-
Implicitly Unwrapped Optionals:
- Use sparingly, primarily for IBOutlets or properties initialized in
viewDidLoad
- Document why a property is implicitly unwrapped
- Consider refactoring to avoid implicitly unwrapped optionals
- Use sparingly, primarily for IBOutlets or properties initialized in
-
Visibility Modifiers:
- Use appropriate access control modifiers (
public
,internal
,fileprivate
,private
) - Favor minimal accessibility; start with
private
and increase visibility as needed - Document public API thoroughly
- Mark classes as
final
unless designed for inheritance
- Use appropriate access control modifiers (
-
Access Control for Extensions:
- Use consistent access level for extension methods as their types
- Use
fileprivate
for utility methods used only within the file
-
Strong Typing:
- Leverage Swift's type system for safety
- Use enumerations for states and options
- Avoid stringly-typed code
- Use custom types to clarify intent (e.g.,
typealias
or struct wrappers)
-
Type Inference:
- Let the compiler infer types when clear from context
- Include explicit types for public API and complex expressions
- Use type annotations for empty collections and ambiguous types
-
ARC and Retain Cycles:
- Be aware of potential retain cycles
- Use weak and unowned references appropriately
- Document closures that capture
self
strongly - Avoid memory leaks by properly breaking retain cycles
-
Value vs. Reference Types:
- Prefer structs over classes when possible
- Use classes when identity or inheritance is required
- Be aware of copy semantics for value types
- Document performance implications for large value types
-
Error Types:
- Define domain-specific error types (typically enumerations)
- Conform to
Error
protocol - Provide meaningful error messages
- Use appropriate error cases for different failure modes
-
Error Propagation:
- Use
throws
andrethrows
appropriately - Use
do-catch
blocks to handle errors - Avoid silently ignoring errors
- Provide context when converting errors between domains
- Use
-
Result Type:
- Use
Result<Success, Failure>
for asynchronous operations - Provide clear success and error types
- Handle both success and failure cases
- Use
Result
for operations that can fail even when using callbacks
- Use
-
Async/Await:
- Use Swift's structured concurrency model when available
- Properly mark
async
functions and methods - Use
Task
for launching async work - Handle cancellation appropriately
-
Dispatch Queues:
- Use appropriate queues for appropriate work (main queue for UI, background queues for processing)
- Be explicit about which queue work is performed on
- Avoid thread contention and deadlocks
- Consider thread safety for shared resources
-
Actor Model:
- Use actors to manage shared mutable state
- Respect actor isolation boundaries
- Be aware of potential deadlocks when actors call other actors
- Document actor usage patterns
-
Components:
- Model: Domain data and business logic
- View: UI elements and layout (UIViewController, UIView, SwiftUI View)
- ViewModel: Transforms model data for display and handles view logic
-
Best Practices:
- Keep views simple and focused on UI concerns
- Make view models testable with clear inputs and outputs
- Use data binding (Combine, RxSwift, or closures) to connect view and view model
- Keep models independent of UI concerns
-
Coordinator Pattern:
- Use coordinators for navigation logic
- Decouple view controllers from each other
- Handle deep linking and complex flows using coordinators
- Document the responsibility of each coordinator
-
Dependency Injection:
- Use constructor injection for required dependencies
- Consider property injection for optional dependencies
- Use dependency containers for complex dependency graphs
- Test with mock dependencies
-
View Composition:
- Break complex views into smaller, reusable components
- Use
ViewModifier
for reusable view modifications - Leverage
@ViewBuilder
for dynamic view construction - Keep view bodies readable and focused
-
State Management:
- Use appropriate property wrappers (
@State
,@Binding
,@ObservedObject
,@EnvironmentObject
) - Minimize state duplication
- Prefer value semantics for view models with
@ObservableObject
- Document state dependencies
- Use appropriate property wrappers (
- Bridging:
- Use
UIViewRepresentable
andUIViewControllerRepresentable
appropriately - Keep representable wrappers thin
- Handle updates and coordination properly
- Document UIKit dependencies
- Use
-
Test Coverage:
- Aim for high test coverage for business logic
- Test edge cases and error conditions
- Keep tests independent from each other
- Follow AAA pattern (Arrange, Act, Assert)
-
Mocking and Stubbing:
- Use protocols for easier mocking
- Create test-specific implementations for dependencies
- Consider using a mocking framework for complex cases
- Document mock behaviors
-
Test Organization:
- Name tests clearly to describe what they test
- Group related tests together
- Use descriptive assertions
- Test both success and failure paths
-
XCTest UI Testing:
- Make UI elements accessible for testing
- Create screen abstractions (Page Objects)
- Make tests robust against minor UI changes
- Test key user flows
-
Snapshot Testing:
- Use snapshot tests for UI verification
- Be aware of platform differences in rendering
- Document snapshot test requirements
- Update snapshots when UI intentionally changes
-
General Principles:
- Measure before optimizing
- Use Instruments for profiling
- Focus on hot paths and user-visible performance
- Document performance implications
-
Common Optimizations:
- Reduce memory allocations
- Minimize work on the main thread
- Batch UI updates
- Use appropriate data structures for operations
-
Package Structure:
- Organize code into modules with clear dependencies
- Document module boundaries and responsibilities
- Version packages according to semantic versioning
- Specify version constraints appropriately
-
Dependency Selection:
- Evaluate dependencies for maintenance and compatibility
- Consider license implications
- Prefer well-maintained, actively developed packages
- Document rationale for external dependencies
-
CocoaPods:
- Maintain an up-to-date Podfile
- Pin versions for stability
- Document pod-specific setup requirements
- Keep pods updated regularly
-
Carthage:
- Use Carthage for frameworks not available via SPM
- Document build requirements
- Include Carthage bootstrapping in documentation
-
Comments and Documentation:
- Use documentation comments (
///
) for public API - Document parameters, return values, and thrown errors
- Include examples for complex functionality
- Keep documentation up to date with code changes
- Use documentation comments (
-
README and Project Documentation:
- Maintain clear setup instructions
- Document architectural decisions
- Include visual diagrams for complex systems
- Document build and deployment processes
- Swift API Design Guidelines
- Swift.org Documentation
- Apple Human Interface Guidelines
- SwiftLint
- Swift Style Guide
disabled_rules:
# Customize based on project needs
- line_length
- trailing_whitespace
- todo
opt_in_rules:
- empty_count
- explicit_init
- closure_spacing
- overridden_super_call
- redundant_nil_coalescing
- private_outlet
- nimble_operator
- attributes
- operator_usage_whitespace
- closure_end_indentation
- first_where
- sorted_imports
- object_literal
- number_separator
- prohibited_super_call
- fatal_error_message
included:
- Sources
- Tests
excluded:
- Carthage
- Pods
- vendor
line_length:
warning: 120
error: 200
ignores_comments: true
ignores_urls: true
file_length:
warning: 400
error: 1000
type_name:
min_length: 3
max_length: 50
identifier_name:
min_length: 2
max_length: 50
excluded:
- id
- URL
- x
- y
reporter: "xcode"