Lesson 59Structs

Struct Best Practices

Writing Better Structs

Follow these best practices to write clean, maintainable, and efficient Swift structs that your future self will thank you for!

Top 10 Best Practices

1. Use Structs by Default

Prefer structs over classes

2. Keep Structs Focused

Single responsibility

3. Meaningful Names

Clear, descriptive naming

4. Default Values

Easier initialization

5. Computed Properties

For derived values

6. Mark Mutating

Clear intent

7. Access Control

Hide implementation

8. Use Protocols

Add functionality

9. Use Extensions

Organize code

10. Document Code

Help future you

When to Use Class Instead

  • You need identity (reference semantics)
  • You need inheritance
  • You need deinitializers
  • Shared mutable state is required

Access Control Levels

private - Only within the type
fileprivate - Only within the file
internal - Within the module (default)
public - Anywhere, but can't subclass
open - Anywhere, can subclass
best_practices.swift
// STRUCT BEST PRACTICES
// Guidelines for writing clean, efficient structs

// ===== 1. USE STRUCTS BY DEFAULT =====
// Prefer structs over classes when possible

// GOOD: Simple data container
struct UserProfile {
    var name: String
    var email: String
    var age: Int
}

// ===== 2. KEEP STRUCTS SMALL & FOCUSED =====
// Single Responsibility Principle

// BAD: Too many responsibilities
struct BadUser {
    var name: String
    var email: String
    func sendEmail() { }
    func validatePassword() { }
    func saveToDatabase() { }
}

// GOOD: Separate concerns
struct User {
    var name: String
    var email: String
}

struct EmailService {
    func send(to user: User, message: String) { }
}

// ===== 3. USE MEANINGFUL NAMES =====
// Clear, descriptive property names

// BAD
struct P {
    var x: Double
    var y: Double
}

// GOOD
struct Coordinate {
    var latitude: Double
    var longitude: Double
}

// ===== 4. PROVIDE DEFAULT VALUES =====
// Make structs easier to initialize

struct Settings {
    var theme: String = "light"
    var fontSize: Int = 14
    var notificationsEnabled: Bool = true
}

let defaultSettings = Settings()
let customSettings = Settings(theme: "dark")

// ===== 5. USE COMPUTED PROPERTIES WISELY =====
// For derived values, not stored data

struct Rectangle {
    var width: Double
    var height: Double

    // GOOD: Derived from stored properties
    var area: Double {
        return width * height
    }

    var perimeter: Double {
        return 2 * (width + height)
    }

    var isSquare: Bool {
        return width == height
    }
}

// ===== 6. MARK MUTATING METHODS EXPLICITLY =====
// Clear intent about what modifies state

struct Counter {
    private(set) var value: Int = 0

    mutating func increment() {
        value += 1
    }

    mutating func reset() {
        value = 0
    }

    // Non-mutating returns new instance
    func adding(_ amount: Int) -> Counter {
        return Counter(value: value + amount)
    }
}

// ===== 7. USE ACCESS CONTROL =====
// Hide implementation details

struct BankAccount {
    private(set) var balance: Double = 0

    mutating func deposit(_ amount: Double) {
        guard amount > 0 else { return }
        balance += amount
    }

    mutating func withdraw(_ amount: Double) -> Bool {
        guard amount > 0 && balance >= amount else {
            return false
        }
        balance -= amount
        return true
    }
}

// ===== 8. CONFORM TO PROTOCOLS =====
// Add functionality through protocols

struct Task: Identifiable, Equatable, Codable {
    let id: UUID
    var title: String
    var isCompleted: Bool

    init(title: String) {
        self.id = UUID()
        self.title = title
        self.isCompleted = false
    }
}

// ===== 9. USE EXTENSIONS FOR ORGANIZATION =====
// Group related functionality

struct Product {
    var name: String
    var price: Double
}

extension Product: CustomStringConvertible {
    var description: String {
        return "\(name): $\(price)"
    }
}

extension Product {
    func withDiscount(_ percent: Double) -> Product {
        let newPrice = price * (1 - percent / 100)
        return Product(name: name, price: newPrice)
    }
}

// ===== 10. DOCUMENT YOUR CODE =====
/// A struct representing a geographic location
struct Location {
    /// Latitude in degrees (-90 to 90)
    var latitude: Double

    /// Longitude in degrees (-180 to 180)
    var longitude: Double

    /// Calculates distance to another location in kilometers
    func distance(to other: Location) -> Double {
        // Haversine formula implementation
        return 0.0  // Simplified
    }
}

Try It Yourself!

Review a struct you've written and apply at least 3 best practices from this lesson to improve it!