Difference between CurrentValueSubject and @Published
Asked Answered
P

5

38

So I'm digging into combine and this question came up.

Is there any real difference between using CurrentValueSubject (and setting its value using currentValueSubject.value) or using a @Published var and accessing its publisher with a $? I mean I know one returns a Subject instead of a Publisher, but the only real difference I could find is that CurrentValueSubject is way more useful because you can declare it on a protocol.

I really don't understand how @Published can be useful if we can just use PassthroughSubject, am I missing something here? Mind you, this is using UIKit, it may have other uses for SwiftUI.

Thank you.

Papillon answered 2/11, 2019 at 23:43 Comment(0)
A
27

@Published is just a quick way to use CurrentValueSubject a little neater. When I debug one of my apps and look at the type returned by $paramName , it's actually just a CurrentValueSubject:

po self.$books
▿ Publisher
  ▿ subject : <CurrentValueSubject<Array<Book>, Never>: 0x6000034b8910>

I guess one benefit of using CurrentValueSubject instead of @Published may be to allow you to use the error type?

Note: Despite being a CurrentValueSubject right now I'd never rely on that assumption.

Amazon answered 5/11, 2019 at 14:55 Comment(5)
Thanks! Another benefit is being able to declare it on a protocol :)Papillon
@Papillon Interesting, I hadn't considered protocols, yeah, wrappers like @Published aren't allowed there alright :DAmazon
It is possible but it is quite ugly... See my answer.Calen
They are closely similar, however, @Published fires on willSet of it's wrapped property, while CurrentValueSubject fires on didSet.Iosep
I'm not sure why this is the most accepted answer as this answer is not correct, and other answers offer more insight into the differences. As someone commented above, @Published fires on willSet of it's wrapped property, and CurrentValueSubject fires on didSet. So in practice they behave in different manners and can be used in and for very different purposes.Howey
O
39

I found myself coming back to this post so felt I'd add some extra insight in to the difference between @Published and CurrentValueSubject.

One main difference can be found in the documentation for @Published:

When the property changes, publishing occurs in the property’s willSet block, meaning subscribers receive the new value before it’s actually set on the property.

Additionally, conversation on the Swift Forums note that @Published is intended for use with SwiftUI.

With regards to @Published publishing in the willSet block of it's property, consider the following example:

class PublishedModel {
    @Published var number: Int = 0
}

let pModel = PublishedModel()

pModel.$number.sink { number in
    print("Closure: \(number)")
    print("Object:  \(pModel.number) [read via closure]")
}

pModel.number = 1
print("Object:  \(pModel.number) [read after assignment]")

This produces the following output:

Closure: 0
Object:  0 [read via closure]
Closure: 1
Object:  0 [read via closure]
Object:  1 [read after assignment]

Contrast this with another example where we keep everything the same, except replacing @Published with CurrentValueSubject:

class CurrentValueSubjectModel {
    var number: CurrentValueSubject<Int, Never> = .init(0)
}

let cvsModel = CurrentValueSubjectModel()

cvsModel.number.sink { number in
    print("Closure: \(number)")
    print("Object:  \(cvsModel.number.value) [read via closure]")
}

cvsModel.number.send(1)

print("Object:  \(cvsModel.number.value) [read after assignment]")

Output:

Closure: 0
Object:  0 [read via closure]
Closure: 1
Object:  1 [read via closure] // <— Here is the difference
Object:  1 [read after assignment]

After updating number to 1, reading the object's CurrentValueSubject's value property within the closure prints the new value instead of the old value as with @Published.

In summary, use @Published within your ObservableObjects for your SwiftUI views. If you're looking to create some sort of model object with an instance property that holds a current value and also publishes it's changes after they are set, use CurrentValueSubject.

Osteopath answered 13/2, 2021 at 9:14 Comment(1)
Ty for the clear examples! Any idea why they introduced this difference? What's a use case for wanting the value before it's set?Multiflorous
C
28

CurrentValueSubject is a value, a publisher and a subscriber all in one.

Sadly it doesn’t fire objectWillChange.send() when used inside an ObservableObject.

You can specify an error type.

@Published is a property wrapper, thus:

  • It is not yet supported in top-level code.
  • It is not supported in a protocol declaration.
  • It can only be used within a class.

@Published automatically fires objectWillChange.send() when used inside an ObservableObject.

Xcode will emit a warning if your try to publish to @Published wrapped property from a background queue. Probably because objectWillChange.send() must be called from the main thread.

The error type of its publisher is Never

My biggest beef against @Published is that it can’t behave as a subscriber and setting up Combine pipelines requires additional plumbing compared to a Current Value Subject.

We can declare a @Published property inside a protocol. Not very pretty...

protocol TestProtocol {
    var isEnabled: Bool { get }
    var isEnabledPublished: Published<Bool> { get }
    var isEnabledPublisher: Published<Bool>.Publisher { get }
}

class Test: ObservableObject, TestProtocol {
    @Published var isEnabled: Bool = false
    var isEnabledPublished: Published<Bool> { _isEnabled }
    var isEnabledPublisher: Published<Bool>.Publisher { $isEnabled }
}
Calen answered 29/11, 2019 at 15:1 Comment(1)
you don't have to use @Published you can redefine var objectWillChange to be whatever publisher you like.Johathan
A
27

@Published is just a quick way to use CurrentValueSubject a little neater. When I debug one of my apps and look at the type returned by $paramName , it's actually just a CurrentValueSubject:

po self.$books
▿ Publisher
  ▿ subject : <CurrentValueSubject<Array<Book>, Never>: 0x6000034b8910>

I guess one benefit of using CurrentValueSubject instead of @Published may be to allow you to use the error type?

Note: Despite being a CurrentValueSubject right now I'd never rely on that assumption.

Amazon answered 5/11, 2019 at 14:55 Comment(5)
Thanks! Another benefit is being able to declare it on a protocol :)Papillon
@Papillon Interesting, I hadn't considered protocols, yeah, wrappers like @Published aren't allowed there alright :DAmazon
It is possible but it is quite ugly... See my answer.Calen
They are closely similar, however, @Published fires on willSet of it's wrapped property, while CurrentValueSubject fires on didSet.Iosep
I'm not sure why this is the most accepted answer as this answer is not correct, and other answers offer more insight into the differences. As someone commented above, @Published fires on willSet of it's wrapped property, and CurrentValueSubject fires on didSet. So in practice they behave in different manners and can be used in and for very different purposes.Howey
E
11

One advantage on @Published is that it can act as a private-mutable, public-immutable CurrrentValueSubject.

Compare:

@Published private(set) var text = "someText"

with:

let text = CurrentValueSubject<String, Never>("someText")

When designing APIs you often want to allow clients to read the current value and subscribe to updates but prevent them from setting values directly.

Embolic answered 16/10, 2020 at 9:17 Comment(2)
I think that you can make same behaviour like this: ` private (set) var text: CurrentValueSubject<String, Never> = .init("someText") `Farlie
No, that only ensures you can't replace the publisher object itself. You can still set the value property from outside the class.Embolic
P
4

There is one limitation when using @Published.

You can only use @Published on properties of a Class whereas CurrentValueSubject can be used for struct as well

Penniepenniless answered 19/2, 2022 at 6:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.