How to modify protocol properties with private setters in default implementation extensions?

I want to do something like this:

public protocol Valued {
    var value: Int  { get }
    func changeValue(to: Int)
}

public extension Valued {
    func changeValue(to newValue: Int) {
        self.value = newValue // Error: Cannot assign to property: 'value' is a get-only property
    }
}

public class Thing: Valued {
    public private(set) var value: Int = 0
}

I think I have a feeling that this may never be made possible, but I need some help in understanding why.

In this case, the only thing that you know about Self is that it conforms to Valued, which means the only things you can do it with it are:

  • operations that are listed in the protocol
  • passing it to other functions that take generic parameters like T: Valued, or protocol objects (things with type Valued)
  • dynamic casts to query information about it

In this case, nothing about value says that it can be set, in Valued. A type like the following could conform to it:

public struct OtherThing: Valued {
    public var value: Int {
        return 10
    }
}

That computed property is get-only, so what would changeValue do?

If you want the protocol to support value being set, I recommend you use var value: Int { get set }, and types that need custom behaviour can customise their setter (or didSet or willSet).

Assuming you don't want the setter for value to be publicly exposed through the Valued protocol, you might consider refining Valued with an internal protocol that allows you to provide the default implementations you'd like.

public protocol Valued {
    var value: Int { get }
    mutating func changeValue(to: Int)
}

internal protocol MutableValued: Valued {
    var value: Int { get set }
}

extension MutableValued {
    public mutating func changeValue(to newValue: Int) {
        self.value = newValue
    }
}

public class Thing: MutableValued {
    public internal(set) var value: Int = 0
}

2 Likes