Search code examples
iosswiftcombine

Swift Combine: `sink` works with model.$name but not with model.name.publisher. Why?


I have the a model with a published name property:

@MainActor class MyModel:ObservableObject {
    
    @Published private(set) var name = ""

    ....

}

I am wondering why sink works when used on model.$name and not on model.name.publisher.

model.$name
    .receive(on: RunLoop.main)
    .sink { name in
        ....            <-- it's called. works.
    }
    .store(in: &subs)

This does not get called:

model.name.publisher
    .receive(on: RunLoop.main)
    .sink { name in
        ....            <-- does not get called.
    }
    .store(in: &subs)

Can someone explain exactly why I have to use the $ sign in front of name and why name.publisher does not work?


Solution

  • $name is how you access the underlying Publisher of the @Published property wrapper. This publisher will emit every time the name property mutates and probably that's what you want.

    With model.name.publisher you get a Publisher that will emit each character of the String and then complete (It's defined on a Sequence extension) If you have:

    @MainActor class MyModel:ObservableObject {
        
        @Published private(set) var name = "foo"
    
        ....
    
    }
    

    You will see f - o - o