Swift Tutorial

Swift Data Types

Swift Control Flow

Swift Functions

Swift Collections

Swift OOPs

Swift Additional Topics

Swift - Properties and their Different Types

In Swift, properties associate values with a particular class, structure, or enumeration. They allow you to store data within an object and can also provide a way to observe and manipulate that data. Swift has several types of properties:

1. Stored Properties

Stored properties store constant and variable values as part of an instance of a class or structure. Stored properties can be either variable (defined with var) or constant (defined with let).

struct Point {
    var x: Double
    let y: Double
}

In this example, x is a variable stored property, and y is a constant stored property.

2. Computed Properties

Instead of storing a value, computed properties provide a getter (and optionally a setter) to compute and return a value. Computed properties can be used in classes, structures, and enumerations.

struct Circle {
    var radius: Double
    var area: Double {
        get {
            return 3.14 * radius * radius
        }
        set(newArea) {
            radius = sqrt(newArea / 3.14)
        }
    }
}

Here, area is a computed property. You can get its value or set a new value (which recalculates the radius).

3. Property Observers

Property observers observe and respond to changes in property values. They are used with stored properties to execute code before or after a property's value changes. Swift provides two observers: willSet and didSet.

class Job {
    var salary: Double = 0.0 {
        willSet(newSalary) {
            print("About to set salary to \(newSalary)")
        }
        didSet {
            print("Salary has changed from \(oldValue) to \(salary)")
        }
    }
}

4. Type Properties

While instance properties are associated with an instance, type properties are associated with the type itself (like static variables in some other languages). They can be defined using the static keyword. For class types, you can also use the class keyword to allow subclasses to override the superclass's implementation.

struct MathConstants {
    static let pi = 3.14159265
}

To access a type property, you use the type's name instead of an instance name:

let piValue = MathConstants.pi

5. Lazy Stored Properties

A lazy stored property's initial value isn't calculated until the first time it's accessed. You declare a lazy property by adding the lazy modifier before its declaration. Lazy properties are useful when the initial value for a property is dependent on external factors or if its computation is expensive.

class ExpensiveDataLoader {
    // This data is not loaded until it's accessed for the first time
    lazy var expensiveData: String = {
        // Load and return the data
        print("Loading data...")
        return "Expensive Data"
    }()
}

6. Read-Only Computed Properties

These are computed properties that only have a getter without a setter. They return a value, and that value cannot be set directly.

struct Square {
    var sideLength: Double
    var area: Double {
        return sideLength * sideLength
    }
}

In the Square example above, area is a read-only computed property. It computes and returns the area of the square but cannot be set directly.

Properties are fundamental to Swift's approach to data encapsulation and provide a mechanism to establish clear interfaces for accessing and manipulating data within objects.

  1. Stored properties vs computed properties in Swift:

    Description: Stored properties store values, while computed properties compute a value on the fly.

    // Stored Property
    struct Rectangle {
        var width: Double
        var height: Double
    }
    
    // Computed Property
    struct Circle {
        var radius: Double
        var area: Double {
            return Double.pi * radius * radius
        }
    }
    
  2. Lazy properties in Swift:

    Description: Lazy properties are computed properties that are only calculated when first accessed.

    class DataDownloader {
        lazy var data: Data = {
            // Download data here
            return Data()
        }()
    }
    
  3. Swift read-only and read-write properties:

    Description: Read-only properties have a getter but no setter, while read-write properties have both.

    struct Temperature {
        var celsius: Double
    
        // Read-only property
        var fahrenheit: Double {
            return (celsius * 9/5) + 32
        }
    
        // Read-write property
        var kelvin: Double {
            get {
                return celsius + 273.15
            }
            set {
                celsius = newValue - 273.15
            }
        }
    }
    
  4. Property observers in Swift:

    Description: Property observers respond to changes in property values.

    class StepCounter {
        var totalSteps: Int = 0 {
            willSet(newTotalSteps) {
                print("About to set totalSteps to \(newTotalSteps)")
            }
            didSet {
                print("Added \(totalSteps - oldValue) steps")
            }
        }
    }
    
  5. Swift computed properties with getter and setter:

    Description: Computed properties can have custom getter and setter.

    struct Temperature {
        private var celsius: Double
    
        var fahrenheit: Double {
            get {
                return (celsius * 9/5) + 32
            }
            set {
                celsius = (newValue - 32) * 5/9
            }
        }
    }
    
  6. Static properties in Swift:

    Description: Static properties belong to the type itself rather than an instance.

    struct MathConstants {
        static let pi = 3.14159
        static var version = 1.0
    }
    
  7. Property wrappers in Swift:

    Description: Property wrappers add a layer of separation to property values.

    @propertyWrapper
    struct Clamped<Value: Comparable> {
        private var value: Value
        private let range: ClosedRange<Value>
    
        init(wrappedValue: Value, _ range: ClosedRange<Value>) {
            self.value = range.clamp(wrappedValue)
            self.range = range
        }
    
        var wrappedValue: Value {
            get { return value }
            set { value = range.clamp(newValue) }
        }
    }
    
    struct Size {
        @Clamped(1...10) var width: Int
        @Clamped(1...10) var height: Int
    }
    
  8. Swift instance and type properties:

    Description: Instance properties belong to instances, while type properties belong to the type itself.

    struct Car {
        static var totalCars = 0
        var brand: String
    
        init(brand: String) {
            self.brand = brand
            Car.totalCars += 1
        }
    }