Swift Combine: turn a publisher into a read-only CurrentValueSubject
Why don't you simply keep the @Published
property public, while making its setter private? That should suit your requirements perfectly while also keeping your code minimal and clean.
class ViewModel {
@Published public private(set) var models = ["hello", "world"]
}
let viewModel = ViewModel()
viewModel.$models.sink { print($0) }
How to convert a publisher to a CurrentValueSubject?
You can't convert publishers to CurrentValueSubject
at will because it's a very specific type of publisher. Ask yourself whether you really need the type of myPublisher
to be CurrentValueSubject<Void, Never>
or if an AnyPublisher<Void, Never>
would do.
var myPublisher: AnyPublisher <Void, Never> {
Just(()).eraseToAnyPublisher()
}
Alternatively, if all you're trying to do is create an instance of CurrentValueSubject
that has ()
as its initial value you could use this:
var myPublisher: CurrentValueSubject<Void, Never> {
CurrentValueSubject<Void, Never>(())
}
How to use Combine's CurrentValueSubject and access it in a SwiftUI View?
I think your question shows that you need to learn a bit more the foundational principle behind SwiftUI, and behind Combine (that statement about "CurrentValueSubject vs regular Publisher" was quite wrong).
All you need here is to expose a @Published
property on your WorkoutManager
, and set it to the value that you want, when needed:
class WorkoutManager: NSObject, HKWorkoutSessionDelegate, HKLiveWorkoutBuilderDelegate, ObservableObject {
@Published var finishedWorkoutTotalEnergyBurned = 0.0
func stopWorkout() {
finishedWorkoutTotalEnergyBurned = unwrappedWorkout.totalEnergyBurned!.doubleValue(for: .kilocalorie())
}
}
struct SummaryView: View {
@StateObject var workoutManager = WorkoutManager()
var body: some View {
Text("\(workoutManager.finishedWorkoutTotalEnergyBurned)")
.navigationBarHidden(true)
}
}
@Published
does use a Combine publisher under the hood, which in combination with @StateObject
cause the view to update. But all you need to reason about here, is how and when to set these properties - and the view will update automatically. In the case you showed, there's likely no need to use a publisher directly.
Swift Combine - Multiple Observable (CurrentValueSubject or PassthroughSubject) into one but wait for update from all
Zip3
seems to be what you are looking for:
Publishers
.Zip3(var1, var2, var3)
.sink(receivedValue: { var1, var2, var3 in
print("Printed!")
})
.store(in: &subscriptions)
From the docs of zip
:
Use
zip(_:_:)
to return a new publisher that combines the elements from two additional publishers to publish a tuple to the downstream. The returned publisher waits until all three publishers have emitted an event, then delivers the oldest unconsumed event from each publisher as a tuple to the subscriber.
See a visualisation on RxMarbles!
Create a CurrentValueSubject from another CurrentValueSubject
I would just have the second publisher be a subscriber to the first publisher. Example:
let pub1 = CurrentValueSubject<String,Never>("howdy")
lazy var pub2 : AnyPublisher<String,Never> = {
self.pub1.eraseToAnyPublisher()
}()
Example of usage:
pub1.sink {print($0)}.store(in: &storage)
pub2.sink {print($0)}.store(in: &storage)
delay(1) {
self.pub1.send("hey")
delay(2) {
self.pub1.send("ho")
}
}
Output:
howdy
howdy
[delay]
hey
hey
[delay]
ho
ho
So both publishers are giving out the same value, which seems to be what you want.
Difference between CurrentValueSubject and @Published
@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.
Related Topics
Urlcomponents Queryitems Losing Percent Encoding When Mutated
Update UIapplicationshortcutitem from Extension
Realitykit - Keep Object Always in Front of Screen
Difference Between Any? and Any
Swiftui Concatenate Multiline Tappable Text
Optional Chaining and Array in Swift
I Am Loading Videos in Avplayer in Collection View But It Repeats Some Cells Data
Cannot Convert Value of Type 'Nsmutablearray' to Expected Argument Type '[Skaction]'
Swift Unsafemutablepointer & Unsafemutablepointer<Unsafepointer<Sometype>>
Swift 3: Convert a String to an Array
Trouble Calling a Method in an Init
Core Data with Swiftui Mvvm Feedback
List Inside Scrollview Is Not Displayed on Watchos
Swift: Convert Byte Array into Ciimage
What Do Detached and Assigncurrentcontext Meaning
Popping Noise Between Audioqueuebuffers