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?
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 var
s 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 {
// ...
}