组合中的 RxSwift `ActivityIndicator` 功能
RxSwift `ActivityIndicator` Functionality in Combine
我已经使用 RxSwift 几年了,我开始探索 Combine with SwiftUI 并且在尝试从 Combine 中复制 RxSwift 的一些功能时遇到了一些麻烦。
在 RxSwift GitHub 上有一个名为 ActivityIndicator.swift
.
的文件中的示例
基本用法如下:
class Foo {
let activityIndicator = ActivityIndicator()
lazy var activity = activityIndicator.asDriver()
var disposeBag = DisposeBag()
func doSomething() {
Observable
.just("this is something")
.trackActivity(activityIndicator)
.subscribe()
.disposed(by: disposeBag)
}
}
这样做的目的是让您可以离开 activity
驱动程序,并且它会在每次订阅或订阅完成时发出布尔值。
然后您可以使用 RxCocoa 直接驱动类似 UIActivityIndicatorView 的 isAnimating 属性 的东西。
我一直在尝试弄清楚如何在 Combine 中创建与此类似的东西,但没有任何运气。
假设我有一个如下所示的 viewModel:
class ViewModel: ObservableObject {
@Published var isActive = false
func doSomething() -> AnyPublisher<Void, Never> {
Just(())
.delay(for: 2.0, scheduler: RunLoop.main)
.eraseToAnyPublisher()
}
}
我想做的是为 Publisher
创建一个运算符,其功能类似于 Rx 运算符的工作方式,我可以通过链转发来自订阅的事件,但更改 isActive
每次值 subscribes/completes/cancels.
在 SwiftUI 视图中,我将启动 doSomething
函数并沉入其中,同时还能够使用已发布的 isActive
属性 到 show/hide ProgressView
与此类似的内容:
struct SomeView: View {
let viewModel = ViewModel()
var body: some View {
var cancelBag = Set<AnyCancellable>()
VStack {
Text("This is text")
if viewModel.isActive {
ProgressView()
}
}
.onAppear(perform: {
viewModel
.doSomething()
.sink()
.store(in: &cancelBag)
})
}
}
有没有我完全想念的像这样工作的东西?
如果没有,我该如何着手在 Combine 中复制 RxSwift 功能?
提前感谢您的帮助。
嗯... ActivityIndicator
class 的关键是 Observable.using(_:observableFactory:)
运算符。不幸的是,我认为 Combine 中没有等效的运算符。
using 运算符在订阅 Observable 时创建资源,然后在 Observable 发送停止事件(完成或错误)时处置资源。这确保了资源的生命周期。在这种特殊情况下,资源只是在创建时增加 Int 值并在处置时减少它。
我想你可以用这样的东西来模仿这种行为:
extension Publisher {
func trackActivity(_ activityIndicator: CombineActivityIndicator) -> some Publisher {
return activityIndicator.trackActivity(of: self)
}
}
final class CombineActivityIndicator {
var counter = CurrentValueSubject<Int, Never>(0)
var cancelables = Set<AnyCancellable>()
func trackActivity<Source: Publisher>(of source: Source) -> some Publisher {
let sharedSource = source.share()
counter.value += 1
sharedSource
.sink(
receiveCompletion: { [unowned self] _ in
self.counter.value -= 1
},
receiveValue: { _ in }
)
.store(in: &cancelables)
return sharedSource
}
var asPublisher: AnyPublisher<Bool, Never> {
counter
.map { [=10=] > 0 }
.eraseToAnyPublisher()
}
}
但是,上面的 class 会使 Publisher 升温,您可能会因此错过发出的值。使用风险自负,除非你绝望,否则我不推荐以上内容。
也许有人已经为 Publisher 写了一个 using
运算符并且愿意分享。
看起来有人创建了 Combine 版本。我不知道它是否与@Daniel T. 讨论的问题相同,但看起来很有希望。
https://github.com/duyquang91/ActivityIndicator
我已经使用 RxSwift 几年了,我开始探索 Combine with SwiftUI 并且在尝试从 Combine 中复制 RxSwift 的一些功能时遇到了一些麻烦。
在 RxSwift GitHub 上有一个名为 ActivityIndicator.swift
.
基本用法如下:
class Foo {
let activityIndicator = ActivityIndicator()
lazy var activity = activityIndicator.asDriver()
var disposeBag = DisposeBag()
func doSomething() {
Observable
.just("this is something")
.trackActivity(activityIndicator)
.subscribe()
.disposed(by: disposeBag)
}
}
这样做的目的是让您可以离开 activity
驱动程序,并且它会在每次订阅或订阅完成时发出布尔值。
然后您可以使用 RxCocoa 直接驱动类似 UIActivityIndicatorView 的 isAnimating 属性 的东西。
我一直在尝试弄清楚如何在 Combine 中创建与此类似的东西,但没有任何运气。
假设我有一个如下所示的 viewModel:
class ViewModel: ObservableObject {
@Published var isActive = false
func doSomething() -> AnyPublisher<Void, Never> {
Just(())
.delay(for: 2.0, scheduler: RunLoop.main)
.eraseToAnyPublisher()
}
}
我想做的是为 Publisher
创建一个运算符,其功能类似于 Rx 运算符的工作方式,我可以通过链转发来自订阅的事件,但更改 isActive
每次值 subscribes/completes/cancels.
在 SwiftUI 视图中,我将启动 doSomething
函数并沉入其中,同时还能够使用已发布的 isActive
属性 到 show/hide ProgressView
与此类似的内容:
struct SomeView: View {
let viewModel = ViewModel()
var body: some View {
var cancelBag = Set<AnyCancellable>()
VStack {
Text("This is text")
if viewModel.isActive {
ProgressView()
}
}
.onAppear(perform: {
viewModel
.doSomething()
.sink()
.store(in: &cancelBag)
})
}
}
有没有我完全想念的像这样工作的东西?
如果没有,我该如何着手在 Combine 中复制 RxSwift 功能?
提前感谢您的帮助。
嗯... ActivityIndicator
class 的关键是 Observable.using(_:observableFactory:)
运算符。不幸的是,我认为 Combine 中没有等效的运算符。
using 运算符在订阅 Observable 时创建资源,然后在 Observable 发送停止事件(完成或错误)时处置资源。这确保了资源的生命周期。在这种特殊情况下,资源只是在创建时增加 Int 值并在处置时减少它。
我想你可以用这样的东西来模仿这种行为:
extension Publisher {
func trackActivity(_ activityIndicator: CombineActivityIndicator) -> some Publisher {
return activityIndicator.trackActivity(of: self)
}
}
final class CombineActivityIndicator {
var counter = CurrentValueSubject<Int, Never>(0)
var cancelables = Set<AnyCancellable>()
func trackActivity<Source: Publisher>(of source: Source) -> some Publisher {
let sharedSource = source.share()
counter.value += 1
sharedSource
.sink(
receiveCompletion: { [unowned self] _ in
self.counter.value -= 1
},
receiveValue: { _ in }
)
.store(in: &cancelables)
return sharedSource
}
var asPublisher: AnyPublisher<Bool, Never> {
counter
.map { [=10=] > 0 }
.eraseToAnyPublisher()
}
}
但是,上面的 class 会使 Publisher 升温,您可能会因此错过发出的值。使用风险自负,除非你绝望,否则我不推荐以上内容。
也许有人已经为 Publisher 写了一个 using
运算符并且愿意分享。
看起来有人创建了 Combine 版本。我不知道它是否与@Daniel T. 讨论的问题相同,但看起来很有希望。 https://github.com/duyquang91/ActivityIndicator