I’ve encountered unexpected behavior in Swift when a class conforms to Publisher. It appears that just conforming to the protocol causes property getters and setters to stop being called.
This version works as expected:
import Foundation
import Combine
struct Box {
let root = Root()
var nested: Nested {
get { root.nested }
set { root.nested = newValue }
}
}
class Root {
private var _nested = Nested()
var nested: Nested {
get {
print("Accessing nested value")
return _nested
}
set {
print("Will Set nested value")
defer { print("Did set nested value") }
self._nested = newValue
}
}
}
struct Nested {
private var _value: Int = 0
var value: Int {
get {
print("Accessing value")
return _value
}
set {
print("Will modify value")
defer { print("Did modify value") }
_value = newValue
}
}
}
var box = Box()
box.nested.value = 42
box.nested = Nested()
Output:
Accessing nested value
Will modify value
Did modify value
Will Set nested value
Did set nested value
Will Set nested value
Did set nested value
But simply making Root conform to Publisher like this:
class Root: Publisher {
// same property as above
typealias Output = Void
typealias Failure = Never
func receive<S>(subscriber: S) where S : Subscriber, Void == S.Input, Never == S.Failure {}
}
Produces only:
Will modify value
Did modify value
No getter or setter logs are printed anymore.
Why does adding Publisher conformance cause property accessors on the class to be skipped entirely? Is this a Swift compiler optimization artifact, a bug, or some unintended side effect of protocol witness table synthesis?
Tested in Xcode 16.2.
swift --version
swift-driver version: 1.115.1 Apple Swift version 6.0.3 (swiftlang-6.0.3.1.10 clang-1600.0.30.1)
Target: arm64-apple-macosx15.0
Any insights appreciated.
The getters and setters are still being called. The problem is that Publisher
declares its own print
method, so you end up calling this print
instead.
Publisher.print
doesn't print anything to the console. It returns a new publisher that logs info about what the publisher is publishing.
If you replace print
with Swift.print
, so that the global function print
is called, you can see the same messages being printed.