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

Time:12-07

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?

CodePudding user response:

$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

CodePudding user response:

In this particular case, I understand why it's confusing.

$name refers to the publisher within the @Published property-wrapper. It is what you want.

name.publisher is something within String. Fabio Felici's answer explains it well. It will cycle through characters (it returns String.Element) of the current string, rather than new values of the string when it is swapped out. For that, use $name. (on which sink returns a String type for each change.)

  • Related