I want to create a Swift Combine publisher that transmits a value and always gives the latest value when someone subscribes to it. However I want to only transmit a value once I have one - from an asynchronous call that’s triggered by the first subscriber. Pass through subject doesn’t work for that need, is there any good way to accomplish this?
You can use CurrentValueSubject for this:
let subject = CurrentValueSubject<T, E>(someValue)
func whatever() -> AnyPublisher<T, E> {
return subject.dropFirst().eraseToAnyPublisher()
}
Related
I have a function which receives a Publisher and creates a PassthroughSubject that is used to both:
subscribe to the source publisher
send values manually
E.g.:
class Adapter<T>{
let innerSubject=PassThroughSubject<T,Never>()
let scope = Scope() // custom type, Array<AnyCancellable>
func init(_ source:Publisher<T,Never>){
source.register(innerSubject).in(scope)
innerSubject.sink(receiveValue:{debugPrint($0)}).in(scope)
}
func adapt(_ T val){
innerSubject.send(val)
}
}
fn usage(){
let adapter=Adapter(Empty()) // change to Empty(completeImmediately:false) to fix
adapter.adapt(42) // should print 42,
}
The inner subject seems to be cancelled because Empty calls complete.
I would expect the inner subject's subscription to be cancelled, not the subject itself. In .Net at least that's the behaviour - am I missing something? Do I need to wrap this in a broadcasting subject? I thought that Publishers could receive multiple Receivers and multicast to each?
I would expect the inner subject's subscription to be cancelled, not the subject itself. In .Net at least that's the behaviour - am I missing something?
Just like with Observables, when a Publisher emits a completed event, it will not emit any more events. A Subject is a kind of Publisher. The behavior is exactly the same as in .Net so I can only assume you are missing something.
I thought that Publishers could receive multiple Receivers and multicast to each?
Yes they can. But they send the exact same values to each receiver, including the completion event. However, again just like with Observables and Rx Subjects, once it emits a completion event, it cannot emit anything else.
I'm creating a simple NewsApp. I want to create the best app architecture I can made. So my question is that if I want save really simple data like username and maybe 5-6 tags as strings, should I put userDefaults logic into my viewModel or should I create a layer between ViewModel and UserDefaultsAPI which will take care about saving data?
I mean I will create StoreData protocol which UserDefaultsAPI will implement. And if I should do it how I can achieve that? I am using RxSwift and I don't now how to subscribe changing data in UserDefaults by UserDefaultsAPI.
You should create a layer between, but given an Rx/functional architecture, it shouldn't be something heavy weight like a protocol.
Learn How to Control the World and do something like this instead:
struct Storage {
static var shared = Storage()
var saveProperty: (Property) -> Void = { property in
UserDefaults.standard.set(try? JSONEncoder().encode(property), forKey: "property")
}
var deleteProperty: () -> Void = {
UserDefaults.standard.removeObject(forKey: "property")
}
var currentProperty: () -> Observable<Property?> = {
UserDefaults.standard.rx.observe(Data.self, "property")
.map { $0.flatMap { try? JSONDecoder().decode(Property.self, from: $0) } }
.distinctUntilChanged()
}
}
struct Property: Codable, Equatable { }
It depends what your doing creating a seperate layer gives you, the opportunity to have different sources for data that implement the same protocol could be useful, and your data may be complex types than need to be encoded and decoded, so it makes sense to encapsulate that out, you may also want to provide some limit range to some of your values, but UserDefaults is just a service like NotificationCenter, you are not going to automatically wrap NotificationCenter in ever class, just do what is simplest, but doesn't run the risk of painting yourself in a corner later on. You are not going to get every issue decided at the get go right, the skill is to make sure you can quickly change if the need arises, and knowing about what possibility's you will need to take advantage of in the future and how can you avoid needing to make complex changes in the moment that don't add squat. There are lots of things you need to do, and being able to prioritise them is an important part of what you do, don't try make cleaver designs, be cleaver in making designs that can be easy for other to understand and modify.
Could you help me with a Combine issue?
I'd like to make a Publisher which sends multiple values during its lifetime. In more detail, I want to wrap a method with a completion handler into a Publisher, and the completion handler is supposed to be called multiple times. For example, it's a method used for receiving messages via WebSocket like this:
webSocketClient.receiveMessage { message, error in
// this closure is called multiple times, everytime a message comes
...
}
How can I wrap this into a Publisher? I want something like AnyPublisher<String, Error> in the end.
Wrapping these is super easy when I use other FRP libraries. For example, in ReactiveSwift, it can be achieved by using SignalProducer.init(_ startHandler:). In RxSwift, it's Observable.create method.
In Combine, I found Future can be used when there's only one value to emit, but it doesn't suit my current case. I couldn't find any initializer for Publisher for this use case.
Also, I found Effect.run in TCA (The Composable Architecture) which can be used for this case. It seems custom publishers are used in its implementation, which seems a bit complicated for simple usage, but is this the only way? Are there any other easy ways to achieve the similar behavior?
I feel this is a quite common scenario, so I'd like to know how Combine users are handling this case in practice.
Thank you in advance!
Basically there are two options.
In a SwiftUI environment make the class which contains the code conform to ObservableObject and add a #Published property
class ViewModel : ObservableObject {
#Published var message = ""
func loadData() {
webSocketClient.receiveMessage { message, error in
self.message = message
...
}
}
}
In the view create a #StateObject of the view model to be notified about new messages.
Or declare a subject to send values
class ViewModel {
let subject = PassthroughSubject<String,Never>()
func loadData() {
webSocketClient.receiveMessage { message, error in
self.subject.send(message)
...
}
}
}
To receive the notifications get an instance of the class, call sink on the subject and store the result into a strong reference.
I am new to RxSwift programming.
I am confused between the two while coding. Which one should be used to store datasource of table and how to decide that ?
A PublishSubject can emit an error or completed event while a PublishRelay cannot.
A PublishSubject conforms to the ObserverType protocol while the PublishRelay does not.
Another important point that was alluded to by #RobMayoff in his comment. Neither a PublishSubject nor a PublishRelay stores state, so neither of them are a good idea to "store datasource of table".
Fortunately, you don't need to store the state yourself because the DataSource object that the items operator creates internally stores it.
In other words, you don't need to use a Subject or Relay (of any sort) to feed a table view. Just use an Observable.
If you look at the interface to PublishRelay you can see that it wraps a PublishSubject but it hides this from its interface. So you can only send it accept(_ event: Element) which means you cannot send it error or completed Events only next elements.
public final class PublishRelay<Element>: ObservableType {
private let subject: PublishSubject<Element>
// Accepts `event` and emits it to subscribers
public func accept(_ event: Element) {
self.subject.onNext(event)
}
/// Initializes with internal empty subject.
public init() {
self.subject = PublishSubject()
}
//...
}
Anyhow, if you look at examples of tableview using RxCocoa they just wrap an array as an Observable usually using Just or create that you then pass to the tableview using RxCocoa's interface. You don't really want a Subject just a plain observable.
I'm trying to understand the purpose of this library by Jake Warthon:
https://github.com/JakeWharton/RxRelay
Basically: A Subject except without the ability to call onComplete or
onError. Subjects are stateful in a damaging way: when they receive an
onComplete or onError they no longer become usable for moving data.
I get idea, it's a valid use case, but the above seems easy to achieve just using the existing subjects.
1. Don't forward errors/completions events to the subject:
`observable.subscribe({ subject.onNext(it) }, { log error / throw exception },{ ... })`
2. Don't expose the subject, make your method signature return an observable instead.
fun(): Observable<> { return subject }
I'm obviously missing something here and I'm very curios on what it is!
class MyPublishRelay<I> : Consumer<I> {
private val subject: Subject<I> = PublishSubject.create<I>()
override fun accept(intent: I) = subject.onNext(intent)
fun subscribe(): Disposable = subject.subscribe()
fun subscribe(c: Consumer<in I>): Disposable = subject.subscribe(c)
//.. OTHER SUBSCRIBE OVERLOADS
}
subscribe has overloads and, usually, people get used to the subscribe(Consumer) overload. Then they use subjects and suddenly onComplete is also invoked. RxRelay saves the user from themselves who don't think about the difference between subscribe(Consumer) and subscribe(Observer).
Don't forward errors/completions events to the subject:
Indeed, but based on our experience with beginners, they often don't think about this or even know about the available methods to consider.
Don't expose the subject, make your method signature return an observable instead.
If you need a way to send items into the subject, this doesn't work. The purpose is to use the subject to perform item multicasting, sometimes from another Observable. If you are in full control of the emissions through the Subject, you should have the decency of not calling onComplete and not letting anything else do it either.
Subjects have far more overhead because they have to track and handle
terminal event states. Relays are stateless aside from subscription
management.
- Jake Wharton
(This is from the issue OP opened on GitHub and felt it was a more a correct answer and wanted to "relay" it here for others to see. https://github.com/JakeWharton/RxRelay/issues/30)
In addition to #akarnokd answer:
In some cases you can't control the flow of data inside the Observable, an example of this is when observing data changes from a database table using Room Database.
If you use Subjects, executing subjects.getValue will always throw error about null safety. So you have to put "? or !!" everywhere in your code even though you know that it will be not nullable.
public T getValue() {
Object o = value.get();
if (NotificationLite.isComplete(o) || NotificationLite.isError(o)) {
return null;
}
return NotificationLite.getValue(o);
}