Swift 结合创建自定义主题
Swift Combine create a custom Subject
我想要一个 Subject
(类似于 CurrentValueSubject
),我可以将其发布到其中,但这会验证我发送的值。例如,我想验证输入是否强制介于一系列值之间,例如 1 和 10。如果高于最大值,则传递最大值,如果低于最小值,则传递最小值。
请不要告诉我在订阅代码上过滤结果,因为那是我试图避免的。那次重复。
伪代码为:
let intSubject = ValidatedValueSubject<Int>(value: 5, min: 1, max: 10)
intSubject.sink { value in print(value) }
intSubject.send(-10)
intSubject.send(5)
intSubject.send(15)
我想生成:
5
1
5
10
显然 CurrentValueSubject
我无法达到那种效果。
我试图创建自己的自定义主题,但我似乎无法让它发挥作用。
有些事情告诉我,我应该以不同的方式看待我的问题,因为我想这太容易了,不需要自定义 Subject
。
用例:
我有一个设置 class,它在设置屏幕和其他任何地方更新,当值发生变化时,我希望屏幕做出相应的反应。 ValidatedValueSubject
存在于这个 Settings 对象中。
设置需要公开 Subject
,这样任何屏幕都可以对 属性 的更改做出反应。
我的自定义方法Subject
如下:
final class QualitySubject: Subject {
public typealias Output = Int
public typealias Failure = Never
public private(set) var value: Output
private let max: Output
private let min: Output
init(value: Output, max: Output, min: Output) {
self.min = min
self.max = max
self.value = value
self.value = validated(value)
}
private func validated(_ value: Output) -> Int {
return max(min, min([=13=], max))
}
var subscription: [???? QualitySubscription ?????] = []
public func send(_ value: Output) {
self.value = validated(value)
subscription.subscriber.receive(value)
}
public func send(completion: Subscribers.Completion<Failure>) {
print("completion")
}
public func send(subscription: Subscription) {
print("send subscription")
}
public func receive<S>(subscriber: S) where S : Subscriber, S.Failure == Failure, S.Input == Output {
let qualitySubscription = QualitySubscription(value: value, subscriber: subscriber)
subscriber.receive(subscription: qualitySubscription)
// I think I should save a reference to the subscription so I could forward new values afterwards (on send method) but I can't because of generic constraints.
}
}
你可以包装一个 CurrentValueSubject
:
class MySubject<Output, Failure: Error>: Subject {
init(initialValue: Output, groom: @escaping (Output) -> Output) {
self.wrapped = .init(groom(initialValue))
self.groom = groom
}
func send(_ value: Output) {
wrapped.send(groom(value))
}
func send(completion: Subscribers.Completion<Failure>) {
wrapped.send(completion: completion)
}
func send(subscription: Subscription) {
wrapped.send(subscription: subscription)
}
func receive<Downstream: Subscriber>(subscriber: Downstream) where Failure == Downstream.Failure, Output == Downstream.Input {
wrapped.subscribe(subscriber)
}
private let wrapped: CurrentValueSubject<Output, Failure>
private let groom: (Output) -> Output
}
并像这样使用它:
let subject = MySubject<Int, Never>(initialValue: 5) { max(1, min([=11=], 10)) }
let ticket = subject.sink { print("value: \([=11=])") }
subject.send(-10)
subject.send(5)
subject.send(15)
输出:
value: 5
value: 1
value: 5
value: 10
欣赏包装主题的学术方面。然而,从 objective 的角度来看,我将提供一个替代方案 - 我将使用 map 来强制值进入定义的边界,而不是过滤器。
import Combine
let subject = CurrentValueSubject<Int, Never>(5)
subject.map { max(1, min([=10=], 10)) }.sink {
print("value: \([=10=])")
}
subject.send(-10)
subject.send(5)
subject.send(15)
与接受的答案相同的输出。
value: 5
value: 1
value: 5
value: 10
我想要一个 Subject
(类似于 CurrentValueSubject
),我可以将其发布到其中,但这会验证我发送的值。例如,我想验证输入是否强制介于一系列值之间,例如 1 和 10。如果高于最大值,则传递最大值,如果低于最小值,则传递最小值。
请不要告诉我在订阅代码上过滤结果,因为那是我试图避免的。那次重复。
伪代码为:
let intSubject = ValidatedValueSubject<Int>(value: 5, min: 1, max: 10)
intSubject.sink { value in print(value) }
intSubject.send(-10)
intSubject.send(5)
intSubject.send(15)
我想生成:
5
1
5
10
显然 CurrentValueSubject
我无法达到那种效果。
我试图创建自己的自定义主题,但我似乎无法让它发挥作用。
有些事情告诉我,我应该以不同的方式看待我的问题,因为我想这太容易了,不需要自定义 Subject
。
用例:
我有一个设置 class,它在设置屏幕和其他任何地方更新,当值发生变化时,我希望屏幕做出相应的反应。 ValidatedValueSubject
存在于这个 Settings 对象中。
设置需要公开 Subject
,这样任何屏幕都可以对 属性 的更改做出反应。
我的自定义方法Subject
如下:
final class QualitySubject: Subject {
public typealias Output = Int
public typealias Failure = Never
public private(set) var value: Output
private let max: Output
private let min: Output
init(value: Output, max: Output, min: Output) {
self.min = min
self.max = max
self.value = value
self.value = validated(value)
}
private func validated(_ value: Output) -> Int {
return max(min, min([=13=], max))
}
var subscription: [???? QualitySubscription ?????] = []
public func send(_ value: Output) {
self.value = validated(value)
subscription.subscriber.receive(value)
}
public func send(completion: Subscribers.Completion<Failure>) {
print("completion")
}
public func send(subscription: Subscription) {
print("send subscription")
}
public func receive<S>(subscriber: S) where S : Subscriber, S.Failure == Failure, S.Input == Output {
let qualitySubscription = QualitySubscription(value: value, subscriber: subscriber)
subscriber.receive(subscription: qualitySubscription)
// I think I should save a reference to the subscription so I could forward new values afterwards (on send method) but I can't because of generic constraints.
}
}
你可以包装一个 CurrentValueSubject
:
class MySubject<Output, Failure: Error>: Subject {
init(initialValue: Output, groom: @escaping (Output) -> Output) {
self.wrapped = .init(groom(initialValue))
self.groom = groom
}
func send(_ value: Output) {
wrapped.send(groom(value))
}
func send(completion: Subscribers.Completion<Failure>) {
wrapped.send(completion: completion)
}
func send(subscription: Subscription) {
wrapped.send(subscription: subscription)
}
func receive<Downstream: Subscriber>(subscriber: Downstream) where Failure == Downstream.Failure, Output == Downstream.Input {
wrapped.subscribe(subscriber)
}
private let wrapped: CurrentValueSubject<Output, Failure>
private let groom: (Output) -> Output
}
并像这样使用它:
let subject = MySubject<Int, Never>(initialValue: 5) { max(1, min([=11=], 10)) }
let ticket = subject.sink { print("value: \([=11=])") }
subject.send(-10)
subject.send(5)
subject.send(15)
输出:
value: 5
value: 1
value: 5
value: 10
欣赏包装主题的学术方面。然而,从 objective 的角度来看,我将提供一个替代方案 - 我将使用 map 来强制值进入定义的边界,而不是过滤器。
import Combine
let subject = CurrentValueSubject<Int, Never>(5)
subject.map { max(1, min([=10=], 10)) }.sink {
print("value: \([=10=])")
}
subject.send(-10)
subject.send(5)
subject.send(15)
与接受的答案相同的输出。
value: 5
value: 1
value: 5
value: 10