swiftcombine

Adding Publisher conformance to a class breaks expected computed property getter calls


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.

Reproducible example

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.

Question

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.


Solution

  • 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.