0

I have a code like this (play in a playground:

protocol ServiceProtocol {
    var enabled: Bool { get set }
}

class Service: ServiceProtocol {
    var enabled: Bool = false
}

class A {
    let service = Service()
}

class B {
    let service: ServiceProtocol = Service()
}

let a = A()
let b = B()

a.service.enabled = false
b.service.enabled = false // <-- the error is here: Cannot assign to property: 'service' is a 'let' constant

If I reference service as a Service (class A), everything works as expected. In class B, with service being a ServiceProtocol, I have the error:

Cannot assign to property: 'service' is a 'let' constant

It is a nonsense to me, as I am not trying to assign anything to service: I am only trying to assign a property of service. enabled being get set, I should be able to assign it, like in A.

If I declare service as a var in class B, the error disappears.

Any ideas?

Carl Sarkis
  • 119
  • 6
  • Possibly helpful: [Protocol Extension, Mutating Function](https://stackoverflow.com/q/32488726/1187415). – Martin R Jan 08 '21 at 14:21

1 Answers1

7

This is because classes, structs and enums can implement your protocol so the compiler doesn't know if it is a reference type or value type. You can restrict your protocol to be for only classes and your code will work

protocol ServiceProtocol: AnyObject {
    var enabled: Bool { get set }
}

And now

b.service.enabled = false

compiles fine

Joakim Danielson
  • 43,251
  • 5
  • 22
  • 52
  • Works fine, thank you!!! The error message could be maybe a little improved – Carl Sarkis Jan 08 '21 at 14:18
  • I would make a notice here that `AnyObject` yields an Objective-C object. While this is most of the times not a problem, I would say this is an important thing to know. To keep it as a "Swift-only" object you could simply mark it as `protocol ServiceProtocol: class {}`. – Vlad Rusu Jan 08 '21 at 14:19
  • Using `class` instead of `AnyObject` makes it much more clear that the protocol requires the conforming type to be a class IMHO, especially for people new to Swift – Dávid Pásztor Jan 08 '21 at 14:26
  • 1
    @VladRusu that's completely wrong. `class` is actually just a type alias for `AnyObject`. You are mistaking `AnyObject` for `NSObject`. `AnyObject` is simply a Swift protocol that all classes implicitly conform to, it doesn't mean Obj-C object. – Dávid Pásztor Jan 08 '21 at 14:29
  • Just mentioning that “class vs AnyObject protocol” has been discussed here https://stackoverflow.com/q/30176814/1187415 (with some links to the Swift forum). – Martin R Jan 08 '21 at 14:30
  • @CarlSarkis Yes I think you need to get used to that the compiler error messages can be confusing or misleading :) – Joakim Danielson Jan 08 '21 at 14:41
  • amazing misleading, amazing description. wow. – asilturk Jan 08 '21 at 14:44