Read-only and non-computed variable properties in Swift
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++
}
}