swiftclasspropertiesprotocolsmutability

Mutability of a class after conforming to protocol is in question


I was trying to implement a Networking Module for my personal project and I was following TDD. When I am writing the test cases I came across a compile error and I was not able to map the error with Swift concepts. I could have easily ignored it and done the implementation in a different way. But I was not feeling right to do it as I want to know the exact reason to the error.

This is the code snippet.

import UIKit

protocol HTTPClient {
    var url: String? {get set}
    func load()
}

class HTTPClientSpy: HTTPClient {
    var url: String?
    
    func load() {
        
    }
}

class Feed {
    let client: HTTPClient

    init(client: HTTPClient) {
        self.client = client
    }

    func loadFeed() {
        client.url = "" //Compile Error -> Cannot assign to property: 'client' is a 'let' constant
        client.load()
    }
}

When I do the following change the error goes away

class Feed {
    let client: HTTPClientSpy

    init(client: HTTPClientSpy) {
        self.client = client
    }

    func loadFeed() {
        client.url = "" //No compile errors
        client.load()
    }
}

Swift classes are mutable. which means we should be able to change the properties of the instance even if we create a constant reference to the instance. It seems there is something to do with the protocol conformance in this case which I cannot understand.

Can someone please explain the theory behind this?


Solution

  • Swift classes are mutable. which means we should be able to change the properties of the instance even if we create a constant reference to the instance.

    Indeed you are right. But structs can conform to your HTTPClient protocol too:

    struct SomeStruct: HTTPClient {
        var url: String?
        func load() {}
    }
    

    If Feed were passed an instance of SomeStruct, then client.url = "" would not work. Normally you cannot change the vars of a struct-typed variable, if you declared the variable with let.

    let client = SomeStruct()
    client.url = "" // error
    

    The compiler doesn't know whether client in Feed is storing a struct or a class, so it tries to be safe and doesn't let you change url.

    You can make sure that only classes can conform to the protocol by adding : AnyObject:

    protocol HTTPClient: AnyObject {
        // ...
    }