What's up with the Combine framework?

@anandabits That's essentially what a dispose bag is, except it's synchronized and properly encapsulated. You can see the implementation here: RxSwift/Disposables/DisposeBag.swift

You could say the same about URL. What String couldn't do? Nothing, so should we get rid of it and just use String everywhere? Of course not.

Combine tries to emulate it with

If you could find a synchronized Collection to pass in, then you can (at best) achieve feature-parity with what DisposeBag already does in a more simple way.

In any case, just because the feature doesn't have as prominent of a name as DisposeBag, doesn't mean that Apple discovered some magical way to absolve the need for something like DisposeBag. Pretty much any complaints one can have about the "manual" memory management involved with DisposeBag, are equally applicable to what Combine does.

Yes, I agree with this. I'm not a fan of the manual (and therefore error-prone) approach of DisposeBag. If we're going to add additional subscription management features to Combine I would prefer to see different directions explored.

1 Like

That was a really cool post, thanks for sharing. It took me hours to read, but I think I fully understand it now.

That's a really neat design, but I think I'm missing a piece that would be critical to adopting this for a reactive programming system like Rx or Combine. In the article, he discusses tasks that are called once, and either terminate or time out. On the other hand, Rx and Combine have a very different pattern. They set up a subscription, hold onto it (directly via a Disposable/Cancelable, or indirectly via a DisposeBag/Set<Cancelable>), and then eventually destroy it (e.g. when a VC deinitializes). Trio's approach seems perfectly applicable to subscriptions that are made and destroyed within a single function's body, but it isn't clear to me how this call-stack oriented design would help with subscriptions that outlive function lifetimes, dictated by the cancelation token held by a VC.

2 Likes

What I personally needed in Rx was some kind of a bag type which had disposables associated with some keys. DisposeBag is simply not enough. Therefore I personally like AnyCancelable because I can easily create a custom overload that stores a subscription in a dictionary, which is great because when you set a new subscription for the same key it will cancel the previous one.

extension AnyCancelable {
  func store<Key: Hashable>(
    in dictionary: inout [Key: AnyHashable],
    for key: Key
  ) {
    dictionary[key] = self
  }
}

You can already do that by extending RxSwift.Disposable :thinking:

Not quite because a pure disposable won‘t call dispose on deallocation, nor does Cancellable call cancel. And here is the point AnyCancellable is a lightweight DisposeBag because they call cancel/dispose on deallocation.

That‘s why I wrap now my disposables into a custom AnyDisposable which mimics AnyCancellable to get the same convenience.

2 Likes

Ah, I understand. I was wondering what AnyCancellable was really for, because it Cancellable doesn't have associated values, so there would be no need for a type eraser for it.

RxSwift.RefCountDisposable seems (by name) to be what you describe, but it's not. It's a uses a manual reference counting system not associated with the regular Swift ARC.

You should introduce your type as a pull request, it seems useful. It could be named something like ARCManagedDisposable to make it distinct.

In ReactiveSwift it is called ScopedDisposable

Can you please share situations, where setting disposable by key is needed? It is interesting to know, when it can be useful, especially in comparison with SerialDisposable, which has the following description:

/// Represents a disposable resource whose underlying disposable resource can be replaced by another disposable resource, causing automatic disposal of the previous underlying disposable resource.
public final class SerialDisposable {}

Combine today puts us in an awkward situation. Having SwiftUI in the project we are forced to use Combine. In comparison to mature RxSwift, it still needs many improvements. It is difficult to use Combine in real project because of lack of functionality. RxTest is great, and such tooling is necessary. At the same time, having both of them is painful. So, situation is strange:

  • using Combine only is painful, especially in the area of testing
  • using RxSwift without Combine keeps the door to SwiftUI closed
  • using both of them at the same time is painful

So, which variant to chose?:face_with_raised_eyebrow:
For new projects, first variant is chosen by default I suppose. With the hope that Test tooling will appear soon.

My own thoughts that making combine open sourced is a great idea.
There is separate thread about open sourcing SwiftUI, but this two frameworks are far from each other in this aspect.

Open sourcing will be useful for both Swift newcomers, apple ecosystem developers and developers that use Swift on other platforms.

Also, sometimes we need to explore the internal implementation of reactive internals. Reactive bugs can be very tricky, and even perfect documentation sometimes doesn't help to understand whats going on.

Sometimes exploring the internal implementation helps to write custom operators. For example, in our project we have 3 custom variations of 'debounce' operator. They differ a lot from each other, and none of them can be reproduced by combination of standard operators.