Properties by Example - Swift Programming Language

  • Properties associate values with a particular class, structure, or enumeration.
  • Suppose if we need to change or access an iVar in your class using an object of your class, then there should be getter and setter methods assigned to the iVar.
  • A property is used mainly when other objects need to change or access the ivars in your object, without manually defining getters and setters, or using @property (in objective - c).
  • Properties can be further classified into Stored properties and Computed properties.
    • Computed properties are provided by classes, structures, and enumerations.
    • Stored properties are provided only by classes and structures.

1. Stored Properties

  • A Stored property is a constant or variable that is stored as part of an instance of a particular class or structure.
  • Stored properties can be either be:
    • variable stored properties (introduced by the var keyword).
    • constant stored properties (introduced by the let keyword).
  • You can provide a default value for a stored property as part of its definition.
  • You can also set and modify the initial value for a stored property during initialization.

1.1 Stored Property of a Variable Structure Instances

struct FixedLengthRange {
    var firstValue: Int
    let length: Int
}
var rangeOfThreeItems = FixedLengthRange(firstValue: 0, length: 3) // Setting Values 
print(rangeOfThreeItems.firstValue)
print(rangeOfThreeItems.length)
rangeOfThreeItems.firstValue = 6    //Modifying Values
print(rangeOfThreeItems.firstValue)  

/* prints:
0
3
6 */

1.2 Stored Properties of Constant Structure Instances

  • If you create an instance of a structure and assign that instance to a constant, you cannot modify the instance’s properties, even if they were declared as variable properties.
struct FixedLengthRange {
    var firstValue: Int
    let length: Int
}

let rangeOfThreeItems = FixedLengthRange(firstValue: 0, length: 3) // Setting Values 
print(rangeOfThreeItems.firstValue)
print(rangeOfThreeItems.length)

//Uncommenting next lines will give you error 
// rangeOfThreeItems.firstValue = 6    
// print(rangeOfThreeItems.firstValue)   

/* prints:
0
3*/

1.3 Lazy Stored Properties

  • A lazy stored property is a property whose initial value is not calculated until the first time it is used.
  • You indicate a lazy stored property by writing the lazy modifier before its declaration.
  • Lazy properties are useful when the initial value for a property is dependent on outside factors whose values are not known until after an instance’s initialization is complete.
  • Lazy properties are also useful when the initial value for a property requires complex or computationally expensive setup that should not be performed unless or until it is needed.
class DataImporter {
    var filename = "data.txt"
}

class DataManager {
    lazy var importer = DataImporter()
    var data = [String]()
  
}

let manager = DataManager()
manager.data.append("Some data")
manager.data.append("Some more data")

print(manager.importer.filename)
//  DataImporter instance for the importer property has now been created

// prints: data.txt

1.4 Stored Properties and Instance Variables

  • A Swift property does not have a corresponding instance variable, and the backing store for a property is not accessed directly.
  • This approach avoids confusion about how the value is accessed in different contexts and simplifies the property’s declaration into a single, definitive statement.
  • All information about the property—including its name, type, and memory management characteristics—is defined in a single location as part of the type’s definition.

2. Computed Properties

  • In addition to stored properties, classes, structures, and enumerations can define computed properties, which do not actually store a value. Instead, they provide a getter and an optional setter to retrieve and set other properties and values indirectly.
struct Point {
    var x = 0.0, y = 0.0
}
struct Size {
    var width = 0.0, height = 0.0
}
struct Rect {
    var origin = Point()
    var size = Size()
    var center: Point {
        get {
            let centerX = origin.x + (size.width / 2)
            let centerY = origin.y + (size.height / 2)
            return Point(x: centerX, y: centerY)
        }
        set(newCenter) {
            origin.x = newCenter.x - (size.width / 2)
            origin.y = newCenter.y - (size.height / 2)
        }
    }
}
var square = Rect(origin: Point(x: 0.0, y: 0.0),
                  size: Size(width: 10.0, height: 10.0))
let initialSquareCenter = square.center
square.center = Point(x: 15.0, y: 15.0)
print("square.origin is now at (\(square.origin.x), \(square.origin.y))")

// prints: square.origin is now at (10.0, 10.0)

2.1 Shorthand Setter Declaration

  • If a computed property’s setter does not define a name for the new value to be set, a default name of newValue is used.
struct AlternativeRect {
    var origin = Point()
    var size = Size()
    var center: Point {
        get {
            let centerX = origin.x + (size.width / 2)
            let centerY = origin.y + (size.height / 2)
            return Point(x: centerX, y: centerY)
        }
        set {
            origin.x = newValue.x - (size.width / 2)
            origin.y = newValue.y - (size.height / 2)
        }
    }
}
var square1 = AlternativeRect(origin: Point(x: 0.0, y: 0.0),
                  size: Size(width: 10.0, height: 10.0))
let initialSquareCenter1 = square.center
square.center = Point(x: 15.0, y: 15.0)
print("square.origin is now at (\(square.origin.x), \(square.origin.y))")

// prints: square.origin is now at (10.0, 10.0)

2.2 Read-Only Computed Properties

*A computed property with a getter but no setter is known as a read-only computed property. A read-only computed property always returns a value, and can be accessed through dot syntax, but cannot be set to a different value.

struct Cuboid {
    var width = 0.0, height = 0.0, depth = 0.0
    var volume: Double {
        return width * height * depth
    }
}
let fourByFiveByTwo = Cuboid(width: 4.0, height: 5.0, depth: 2.0)
print("the volume of fourByFiveByTwo is \(fourByFiveByTwo.volume)")

// prints: the volume of fourByFiveByTwo is 40.0

3. Property Observers

  • Property observers observe and respond to changes in a property’s value.
  • Property observers are called every time a property’s value is set, even if the new value is the same as the property’s current value.
  • You can add property observers to any stored properties you define, except for lazy stored properties.
  • You have the option to define either or both of these observers on a property:
    • willSet is called just before the value is stored.
    • didSet is called immediately after the new value is stored.
  • If you implement a willSet observer, it’s passed the new property value as a constant parameter. You can specify a name for this parameter as part of your willSet implementation. If you don’t write the parameter name and parentheses within your implementation, the parameter is made available with a default parameter name of newValue.
  • If you implement a didSet observer, it’s passed a constant parameter containing the old property value. You can name the parameter or use the default parameter name of oldValue. If you assign a value to a property within its own didSet observer, the new value that you assign replaces the one that was just set.
class StepCounter {
    var totalSteps: Int = 0 {
        willSet(newTotalSteps) {
            print("About to set totalSteps to \(newTotalSteps)")
        }
        didSet {
            if totalSteps > oldValue  {
                print("Added \(totalSteps - oldValue) steps")
            }
        }
    }
}
let stepCounter = StepCounter()
stepCounter.totalSteps = 200 

// About to set totalSteps to 200
// Added 200 steps 

stepCounter.totalSteps = 360 

// About to set totalSteps to 360
// Added 160 steps

stepCounter.totalSteps = 896

// About to set totalSteps to 896
// Added 536 steps

Global and Local Variables

  • Global variables are variables that are defined outside of any function, method, closure, or type context.
  • Local variables are variables that are defined within a function, method, or closure context.
  • The global and local variables you have encountered in previous chapters have all been stored variables. Stored variables, like stored properties, provide storage for a value of a certain type and allow that value to be set and retrieved.
  • you can also define computed variables and define observers for stored variables, in either a global or local scope. Computed variables calculate their value, rather than storing it, and they are written in the same way as computed properties.

4. Type Properties

  • Instance properties are properties that belong to an instance of a particular type.
  • Every time you create a new instance of that type, it has its own set of property values, separate from any other instance.
  • You can also define properties that belong to the type itself, not to any one instance of that type. There will only ever be one copy of these properties, no matter how many instances of that type you create. These kinds of properties are called type properties.
  • Type properties are useful for defining values that are universal to all instances of a particular type, such as a constant property that all instances can use (like a static constant in C), or a variable property that stores a value that is global to all instances of that type (like a static variable in C).
  • Stored type properties can be variables or constants. Computed type properties are always declared as variable properties, in the same way as computed instance properties.
struct SomeStructure {
    static var storedTypeProperty = "Some value."
    static var computedTypeProperty: Int {
        return 1
    }
}
print(SomeStructure.storedTypeProperty)

// Prints Some value.

SomeStructure.storedTypeProperty = "Another value."
print(SomeStructure.storedTypeProperty)

// Prints Another value.

enum SomeEnumeration {
    static var storedTypeProperty = "Some value."
    static var computedTypeProperty: Int {
        return 6
    }
}
print(SomeEnumeration.computedTypeProperty)

// Prints 6

class SomeClass {
    static var storedTypeProperty = "Some value."
    static var computedTypeProperty: Int {
        return 27
    }
    class var overrideableComputedTypeProperty: Int {
        return 107
    }
}
print(SomeClass.computedTypeProperty)

// Prints 27

Next - Methods by Example


You can download the swift playground of all above examples from Here




Related Article