Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
`RandomNumberViewModel` sketch.
final class RandomNumberViewModel: ObservableObject {
private var subscriptions = Set<AnyCancellable>()
// MARK: - Inputs
let randomNumberPing = PassthroughSubject<Void, Never>() /// (1) `randomNumberPing` would
/// receive `send()` calls from a `Button`’s `action` closure.
// MARK: - Outputs
@Published var randomNumber: Int? /// (2) On the output side, `randomNumber` could power a modal that
/// displays the returned value.
init() {
randomNumberPing
.setFailureType(to: RandomDotOrgError.self) /// (3) To line up our subject’s error with `randomNumberPublisher`’s.
/// I have some [more detailed writing](https://jasdev.me/set-output-type) on `.setFailureType(to:)`, for the curious.
.map(randomNumberPublisher)
.switchToLatest() /// (4) Switching to latest here cancels any in-flight requests.
.print() /// (5) To help with downstream debugging.
.receive(on: DispatchQueue.main) /// (6) Generally, we’ll need to dispatch to the main queue when exposing outputs to views.
.assign(to: \.randomNumber, onWeak: self)
.store(in: &subscriptions)
}
}
// MARK: - Helpers
extension Publisher where Failure == Never {
func assign<Root: AnyObject>( /// (7) A `weak` analog to
/// [`Publisher.assign(to:on:)`](https://developer.apple.com/documentation/combine/publisher/3235801-assign).
/// This operator is helpful in scenarios like the above where we’re piping a sequence to a `@Published`
/// property on the containing instance and need to avoid a retain cycle.
///
/// Additional notes: https://jasdev.me/notes/weak-assignment.
to keyPath: ReferenceWritableKeyPath<Root, Output>,
onWeak object: Root
) -> AnyCancellable {
sink { [weak object] value in
object?[keyPath: keyPath] = value
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment