HuaTham HuaTham - 4 years ago 126
Swift Question

Read-only access for class property in Swift

I am aware of

as discussed here and here but it doesn't seem to work for my situation where I have instance variable with internal properties.

I have a instance property where I would like it to have read-only access. The following code shows some concrete example.

class Point {
var x: Int
var y: Int

init(_ x: Int, _ y: Int) {
self.x = x
self.y = y

class Sprite {
private(set) var origin = Point(0,0)

func incrementX() {
origin.x += 1

func incrementY() {
origin.y += 1

Here, my intent is to make
the sole owner of
, allowing it to be the only one who can modify
, otherwise making
read-only to the public. However:

var sprite = Sprite()
sprite.origin = Point(100, 200) // Error: setter is inaccessibie. Expected behavior.
sprite.origin.x = 150 // No error. The origin.x will be set to 150.

Apparently I can still modify
by modifying internal
directly, but this is not my intent.

How do I make
truly read-only? Am I missing something?

private var x: Int
fileprivate var x: Int
won't work for me because I want to be able to let an external class like

Edit: From comment below, this is not my actual scenario, since someone comments that I should be using
. The code serves as as example to make my question more concrete. I actually have a complex class as instance property in another class, not this simple
. I also can't use it as struct due to other design constraints.

Answer Source

Make origin completely private. The only public thing should be a read-only computed property. Your public incrementX and incrementY methods will still work as desired, because they are allowed to access origin.

If Point is a struct, then it is sufficient for this read-only property to return origin, because it will be a copy:

var currentOrigin : Point {
    return self.origin

But if you insist on making Point a class rather than a struct, then you are vending a reference to the same Point instance you are already retaining, and you cannot prevent it from being mutable. Therefore you will have to produce a copy of self.origin yourself, so that you are not vending a mutable reference to your own private instance:

var currentOrigin : Point {
    return Point(self.origin.x,self.origin.y)

(The way Objective-C Cocoa typically solves this sort of problem is by implementing a class cluster of immutable/mutable pairs; for example, we maintain an NSMutableString but we vend an NSString copy, so that our mutable string cannot be mutated behind our back. However, I think you are being very foolish to reject the use a struct, since this is one of Swift's huge advantages over Objective-C, i.e. it solves the very problem you are having.)

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download