Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Read-only and non-computed variable properties in Swift

People also ask

What is read-only property in Swift?

Read-Only Computed PropertiesA 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 can't be set to a different value.

How do you make a variable read-only in Swift?

In swift, we can create a read-only property by only defining a getter for a variable. Meaning no setter! Since the variable only has a getter, the compiler will throw an error when we try to assign a value to “sum”.

What are computed properties in Swift?

Computed properties are part of a family of property types in Swift. Stored properties are the most common which save and return a stored value whereas computed ones are a bit different. A computed property, it's all in the name, computes its property upon request.

What is willSet and didSet in Swift?

willSet is called before the data is actually changed and it has a default constant newValue which shows the value that is going to be set. didSet is called right after the data is stored and it has a default constant oldValue which shows the previous value that is overwritten.


Simply prefix the property declaration with private(set), like so:

public private(set) var hours:   UInt = 0
public private(set) var minutes: UInt = 0
public private(set) var seconds: UInt = 0

private keeps it local to a source file, while internal keeps it local to the module/project.

private(set) creates a read-only property, while private sets both, set and get to private.


There are two basic ways of doing what you want. First way is by having a private property and and a public computed property which returns that property:

public class Clock {

  private var _hours = 0

  public var hours: UInt {
    return _hours
  }

}

But this can be achieved in a different, shorter way, as stated in the section "Access Control" of the "The Swift Programming Language" book:

public class Clock {

    public private(set) var hours = 0

}

As a side note, you MUST provide a public initialiser when declaring a public type. Even if you provide default values to all properties, init() must be defined explicitly public:

public class Clock {

    public private(set) var hours = 0

    public init() {
      hours = 0
    }
    // or simply `public init() {}`

}

This is also explained in the same section of the book, when talking about default initialisers.


Since there are no access controls (meaning that you can't make an access contract that differs depending on who the caller is), here's what I would do for now:

class Clock {
    struct Counter {
        var hours = 0;
        var minutes = 0;
        var seconds = 0;
        mutating func inc () {
            if ++seconds >= 60 {
                seconds = 0
                if ++minutes >= 60 {
                    minutes = 0
                    ++hours
                }
            }
        }
    }
    var counter = Counter()
    var hours : Int { return counter.hours }
    var minutes : Int { return counter.minutes }
    var seconds : Int { return counter.seconds }
    func incrementTime () { self.counter.inc() }
}

This merely adds a level of indirection, as it were, to direct access to the counter; another class can make a Clock and then access its counter directly. But the idea — i.e., the contract we're trying to make — is that another class should use the Clock's top-level properties and methods only. We can't enforce that contract, but actually it was pretty much impossible to enforce in Objective-C too.


Actually access control (which does not exist yet in Swift) is not as enforced as you may think in Objective C. People can modify your readonly variables directly, if they really want to. They just do not do it with the public interface of the class.

You can do something similar in Swift (cut&paste of your code, plus some modifications, I did not test it):

class Clock : NSObject {

    var _hours:   UInt = 0
    var _minutes: UInt = 0
    var _seconds: UInt = 0

    var hours: UInt {
    get {
      return _hours
    }
    }

    var minutes: UInt {
    get {
      return _minutes
    }
    }

    var seconds: UInt  {
    get {
      return _seconds
    }
    }

    func incrementSeconds() {

        self._seconds++

        if self._seconds == 60 {

            self._seconds = 0
            self._minutes++

            if self._minutes == 60 {

                self._minutes = 0
                self._hours++
            }
        }
    }
}

which is the same as what you have in Objective C except that the actual stored properties are visible in the public interface.

In swift you can also do something more interesting, which you can also do in Objective C, but it's probably prettier in swift (edited in the browser, I did not test it):

class Clock : NSObject {

    var hours: UInt = 0

    var minutes: UInt {
    didSet {
      hours += minutes / 60
      minutes %= 60
    }
    }

    var seconds: UInt  {
    didSet {
      minutes += seconds / 60
      seconds %= 60
    }
    }

    // you do not really need this any more
    func incrementSeconds() {
        seconds++
    }
}