Skip to content

Instantly share code, notes, and snippets.

@kvdesa
Last active July 2, 2024 12:55
Show Gist options
  • Save kvdesa/97a65f5d47417aff62ba491656d64e73 to your computer and use it in GitHub Desktop.
Save kvdesa/97a65f5d47417aff62ba491656d64e73 to your computer and use it in GitHub Desktop.
Combine class to manage cancellable instances instead of using a Set.

CancelBag

Adapted from https://gist.github.com/filimo/6f826b352f75cd1640e4e705340c0301

Instead of using a Set, use a CancelBag instead (kind of like the DisposeBag in RxSwift).

No need to use references when storing cancellables and you can also collect multiple cancellable instances without calling .store(in:).

Collecting

Before:

let subscriptions = Set<AnyCancellable>()

optionalPublisher?
    .sink { value in
        print("received value: \(value)")
    }
    .store(in: &subscriptions)
publisher
    .assign(to: \.someVar, on: self)
    .store(in: &subscriptions)
subject
    .subscribe(publisher2)
    .store(in: &subscriptions)

Now:

let cancelBag = CancelBag()

optionalPublisher?
    .sink { value in
        print("received value: \(value)")
    }
    .store(in: cancelBag)
publisher
    .assign(to: \.someVar, on: self)
    .store(in: cancelBag)
subject
    .subscribe(publisher2)
    .store(in: cancelBag)

Or even better:

let cancelBag = CancelBag()

cancelBag.collect {
    optionalPublisher?
    .sink { value in
        print("received value: \(value)")
    }
    publisher
        .assign(to: \.someVar, on: self)
    subject
        .subscribe(publisher2)
}

Canceling

It's as simple as:

cancelBag.cancel()
import Combine
import SwiftUI
final class CancelBag {
// MARK: - Properties
var subscriptions = Set<AnyCancellable>()
// MARK: - Actions
/// Cancels and removes all cancellable instances stored within this `CancelBag`.
func cancel() {
subscriptions.forEach { $0.cancel() }
subscriptions.removeAll()
}
/// Collects the cancellable instances provided by the given block.
/// - Parameter cancellables:Block returning an array of cancellable instances.
func collect(@Builder _ cancellables: () -> [AnyCancellable?]) {
subscriptions.formUnion(cancellables().compactMap { $0 })
}
// MARK: - Builder
@_functionBuilder
struct Builder {
static func buildBlock(_ cancellables: AnyCancellable?...) -> [AnyCancellable?] {
cancellables
}
}
}
// MARK: - AnyCancellable
extension AnyCancellable {
/// Stores this cancellable instance in the specified `CancelBag`.
/// - Parameter cancelBag: `CancelBag` to store the cancellable instance.
func store(in cancelBag: CancelBag) {
cancelBag.subscriptions.insert(self)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment