Best Practices
## Learning Objectives
- Follow Swift style guidelines
- Write clean, maintainable code
- Use Xcode effectively
- Master Swift idioms
## Naming Conventions
### Types and Protocols
```swift
// Use PascalCase for types and protocols
struct UserProfile {}
class NetworkManager {}
protocol DataSource {}
enum HttpMethod {}
// Protocol names describe behavior
protocol Reader {}
protocol Writer {}
```
### Variables and Functions
```swift
// Use camelCase for variables and functions
let userName = "Alice"
var isAuthenticated = false
func fetchUser() {}
func calculateTotal() {}
```
### Constants
```swift
// Constants at module level use camelCase
let maxRetryCount = 3
let defaultTimeout: TimeInterval = 30
// But enum cases use camelCase
enum Result {
case success
case failure
}
```
### Private Details
```swift
class User {
// Public
var name: String
// Private - underscore prefix is optional
private var _cachedData: String?
private var internalId: UUID?
}
```
## Code Organization
### File Structure
```swift
// 1. Imports
import Foundation
import UIKit
// 2. Class/Struct declaration
// 3. Properties
// 4. Initialization
// 5. Methods
// 6. Extensions
// 7. Private helpers
```
### Protocol Conformance
```swift
class MyViewController: UIViewController {
// Put protocol conformance in extension
}
extension MyViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 0
}
}
extension MyViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// Handle
}
}
```
## Swift Idioms
### Prefer let over var
```swift
// Good
let name = "Alice"
let count = items.count
let filtered = items.filter { $0 > 0 }
// When mutability needed
var mutable = [1, 2, 3]
mutable.append(4)
```
### Prefer guard over if let
```swift
// Good
func process(name: String?) {
guard let name = name else { return }
// Use name here
}
// Avoid nesting
func process(name: String?) {
if let name = name {
if name.count > 0 {
// Deep nesting - avoid this
}
}
}
```
### Use Trailing Closures
```swift
// Good
numbers.map { $0 * 2 }
URLSession.shared.dataTask(with: url) { data, _, _ in
// Handle
}.resume()
```
### Default Values
```swift
// Use nil coalescing
let displayName = user.name ?? "Anonymous"
// Or default parameter
func greet(_ name: String = "World") {
print("Hello, \(name)!")
}
```
## Error Handling
### Prefer Throws over Optionals
```swift
// Good - when failure has meaning
func parseJSON(_ data: Data) throws -> [String: Any] {
guard let json = try JSONSerialization.jsonObject(with: data) as? [String: Any] else {
throw ParseError.invalidFormat
}
return json
}
// Use try? when failure doesn't need details
if let _ = try? validate(email) {
// Valid
}
```
### Result Type for Callbacks
```swift
func fetchUser(completion: @escaping (Result) -> Void) {
// Success
completion(.success(user))
// Failure
completion(.failure(NetworkError.noConnection))
}
```
## Access Control
### Minimize Exposure
```swift
// Good - expose only what's needed
public class APIClient {
private let baseURL: URL
private var cachedResponses: [URL: Data] = [:]
}
// Avoid
class Person {
var name: String = "" // Public by default
var age: Int = 0 // Should be private?
var ssn: String = "" // Definitely private
}
```
### Prefer private to fileprivate
```swift
class MyClass {
// private - only accessible within this class
private var helper: Helper
// fileprivate - only within same file
fileprivate var sharedState: String = ""
}
```
## Optionals
### Safe Unwrapping
```swift
// Good
if let value = optional {
use(value)
}
// Better for early exit
func process() {
guard let value = optional else { return }
use(value)
}
// Nil coalescing for defaults
let displayName = user.name ?? "Anonymous"
```
### Avoid Force Unwrap
```swift
// Bad
let name = optional!
// Good alternatives
if let name = optional { }
guard let name = optional else { }
let name = optional ?? "default"
```
## Performance
### Value Types When Appropriate
```swift
// Use struct for simple data
struct Point {
var x: Double
var y: Double
}
// Use class for shared identity
class Person {
var name: String
// Shared across app
}
```
### Lazy Properties
```swift
class DataImporter {
// Heavy operation only when needed
lazy var data: Data = {
return loadHeavyData()
}()
}
```
### Avoid Retain Cycles
```swift
// Use weak for delegates
weak var delegate: MyDelegate?
// Use capture lists in closures
someCall { [weak self] result in
self?.handle(result)
}
```
## Testing
### Dependency Injection
```swift
// Good - testable
class NetworkService {
let session: URLSession
init(session: URLSession = .shared) {
self.session = session
}
}
// Testable with mock
let mockSession = MockURLSession()
let service = NetworkService(session: mockSession)
```
### Protocol-Oriented Design
```swift
protocol StorageProtocol {
func save(_ data: Data) throws
func load() throws -> Data
}
class FileStorage: StorageProtocol { }
class UserDefaultsStorage: StorageProtocol { }
```
## Documentation
### Comments
```swift
// Single line for brief explanation
/*
Multi-line for longer explanation
or detailed comments
*/
/**
Documentation comment (used by Xcode)
*/
```
### MARK Comments
```swift
class MyClass {
// MARK: - Properties
var name: String
// MARK: - Initialization
init() { }
// MARK: - Public Methods
func publicMethod() { }
// MARK: - Private Methods
private func privateMethod() { }
}
```
## Xcode Tips
### Keyboard Shortcuts
| Action | Shortcut |
|--------|----------|
| Run | Cmd+R |
| Build | Cmd+B |
| Clean | Cmd+Shift+K |
| Find | Cmd+F |
| Replace | Cmd+Alt+F |
| Go to Definition | Cmd+Click |
| Auto-indent | Ctrl+I |
| Comment | Cmd+/ |
### Navigation
| Action | Shortcut |
|--------|----------|
| Open Quickly | Cmd+Shift+O |
| Navigator | Cmd+0 |
| Assistant Editor | Cmd+Alt+Enter |
| Open in Tab | Cmd+Enter |
### Refactoring
- Cmd+Shift+E: Refactor
- Extract Method: Select code, right-click > Refactor
- Rename: Ctrl+Cmd+E
## Common Patterns
### Singleton
```swift
final class Singleton {
static let shared = Singleton()
private init() {
// Private initialization
}
}
```
### Factory Method
```swift
class Button {
func createButton(style: ButtonStyle) -> UIButton {
let button = UIButton(type: .system)
button.setTitle(style.title, for: .normal)
return button
}
}
```
### Builder Pattern
```swift
class UserBuilder {
private var name: String = ""
private var age: Int = 0
func setName(_ name: String) -> UserBuilder {
self.name = name
return self
}
func setAge(_ age: Int) -> UserBuilder {
self.age = age
return self
}
func build() -> User {
return User(name: name, age: age)
}
}
```
## Summary
- Use PascalCase for types, camelCase for variables
- Prefer `let` over `var`, `struct` over `class` when appropriate
- Use `guard` for early exit and optional unwrapping
- Minimize exposure with proper access control
- Avoid force unwrapping and force casts
- Use protocols for abstraction and testing
- Document with MARK comments and Swift documentation
- Learn Xcode shortcuts for productivity
- Write testable code with dependency injection
Comments
Comments powered by Giscus
To enable comments, add your Giscus embed code here.
Learn more about Giscus →