swiftcombineswift-playground

Sink.receive() Does Not Get called After Declaring an Initial Value in Combine Swift


Issue:

I am using a Publisher in a code example which I wrote based on the code example here, [https://developer.apple.com/documentation/combine/observableobject][1]. I expect the print statement inside the body of the sink.receive() to print "received value 30" when I run the code sample below followed by "received value 31". What is happening instead is that the console is printing "received value ()" then "asaad has birthday ()".

Why is this? How can I fix it?

Code:

import Combine

class Contact: ObservableObject {
    @Published var name: String
    @Published var age: Int
    
    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }
    
    func hasBirthday() {
        age += 1
    }
}

var cancellable: AnyCancellable!

let asaad = Contact(name: "Asaad Jaber", age: 30)

cancellable = asaad.objectWillChange
    .sink(receiveValue: { value in
        print("received value \(value)")
    })

let hasBirthday = asaad.hasBirthday()

print("asaad has birthday \(hasBirthday)")

Steps to Reproduce:

  1. I clicked on Xcode in my dock to launch Xcode.
  2. I clicked File > New > Playground to create a new playground.
  3. I copied the code in the code example and pasted it in the playground and I modified some of the values.
  4. I clicked the Play button to execute the code.

Solution

  • You are listening on the object itself and that it changed, not on the value that you want to observe changes on. You could change your code to the following:

    cancellable = asaad.$age //<-- listen to the publisher of age
        .sink(receiveValue: { value in
            print("received value \(value)")
        })
    

    Then you get updates whenever age changes and thus the value will also be the new value of age.

    Edit: Explanation why the solution in your question does not work as you expect.

    If you subscribe to changes of the entire object via objectWillChange, you need to consider following points.

    That's why it's better to listen to specific property publishers like $age.