Search code examples
swiftoverridingcomputed-properties

Cannot override mutable property with read-only property that is a var


I am trying to mock CMMotionManager for unit testing. Here is my abbreviated code:

protocol XMotionManager {
    var isDeviceMotionAvailable: Bool { get }
    var deviceMotionUpdateInterval: TimeInterval { get set }
}

extension CMMotionManager: XMotionManager {}

final class MockCMMotionManager: CMMotionManager {
    private (set) var mockedIsDeviceMotionAvailable = true
    override var isDeviceMotionAvailable: Bool {
        mockedIsDeviceMotionAvailable
    }
    
    private var mockedDeviceMotionUpdateInterval: TimeInterval = 1.0
    override var deviceMotionUpdateInterval: TimeInterval {
        mockedDeviceMotionUpdateInterval
    }
}  

The protocol content is taken from the API. MockCMMotionManager is supposed to override the two properties so that they can be set as required.
While this works for isDeviceMotionAvailable, I get the following compiler error for deviceMotionUpdateInterval:

Cannot override mutable property with read-only property 'deviceMotionUpdateInterval'

I don't understand this error because deviceMotionUpdateInterval is not a read-only property, but declared as a var.
Is something wrong with my code?


Solution

  • deviceMotionUpdateInterval is a read-only property as it's a computed property without a setter.

    Computed properties that don't have an explicit setter declared only have an implicit getter.

    Your declaration of

    override var deviceMotionUpdateInterval: TimeInterval {
      mockedDeviceMotionUpdateInterval
    }
    

    is equivalent to this:

    override var deviceMotionUpdateInterval: TimeInterval {
      get {
        mockedDeviceMotionUpdateInterval
      }
    }
    

    To fix the issue, simply declare both a getter and setter explicitly for the computed property.

    override var deviceMotionUpdateInterval: TimeInterval {
      get {
        mockedDeviceMotionUpdateInterval
      }
      set {
        mockedDeviceMotionUpdateInterval = newValue
      }
    }