我有以下情况,对于基于回调的代码pre-Combine / SwiftUI而言,这非常简单
UIAlert
,以选择一个分辨率选项,该值将产生新的值。如果验证通过-该值保持不变我认为这很自然地适合Combine框架。
我一直在努力寻找一个好的运营商组合,使我可以将结果从Combine中提取出来并进行检查,然后再决定要在没有订阅者和重新发布者的情况下接下来应用什么 的运营商在每个阶段。有条件地选择操作以及从Future
合并类似Alert
的结果的方法对我来说并不明显。
我有进行这种工作的代码-它遵循上述概念并带有整数值。但是它是具有多个发布者/主题的庞然大物-我不愿意向其他人解释此代码。还有一个问题,我如何在Alert
(即title
)的表示中使用该值,而又不将其取出并缓存(我请执行以下操作。
是否存在一种更清洁的方法来执行上述操作,而无需拆分/存储和重新发布相同的数据?当然不使用普通的旧回调-这可能是我在现实生活中的情况
import SwiftUI
import Combine
struct MultipleChainOfAlertsView: View {
@State var showFirst = false
@State var showSecond = false
@State var showThird = false
@State var initialValuePublisher: AnyPublisher<Int, Never>? = nil
@State var previousValuePublisher: AnyPublisher<Int, Never>? = nil
@State var currentValuePublisher: AnyPublisher<Int, Never>? = nil
@State var future1Publisher = CurrentValueSubject<Int?, Never>(nil)
@State var future2Publisher = CurrentValueSubject<Int?, Never>(nil)
@State var future3Publisher = CurrentValueSubject<Int?, Never>(nil)
@State var currentHandlerSubscriber: AnyCancellable? = nil
@State var currentAlertSubscriber: AnyCancellable? = nil
@State var finalSubscriber: AnyCancellable? = nil
@State var value: Int = 0
@State var finalValue: Int? = nil
func beginSeq() {
value = Int.random(in: 0...100)
initialValuePublisher = Just<Int>(value).eraseToAnyPublisher()
currentValuePublisher = initialValuePublisher
let setupStep3 = {
self.currentHandlerSubscriber?.cancel()
self.currentHandlerSubscriber = self.currentValuePublisher?.receive(on: DispatchQueue.main).sink(receiveValue: { val in
if val < 90 {
DispatchQueue.main.async {
self.showFirst = false
self.showSecond = false
self.showThird = true
}
self.previousValuePublisher = self.currentValuePublisher
self.currentValuePublisher = self.future3Publisher.compactMap { $0 }.eraseToAnyPublisher()
self.finalise()
}
else {
self.finalise()
}
})
}
let setupStep2 = {
self.currentHandlerSubscriber?.cancel()
self.currentHandlerSubscriber = self.currentValuePublisher?.receive(on: DispatchQueue.main).sink(receiveValue: { val in
if val > 2 {
DispatchQueue.main.async {
self.showFirst = false
self.showSecond = true
self.showThird = false
}
self.previousValuePublisher = self.currentValuePublisher
self.currentValuePublisher = self.future2Publisher.compactMap { $0 }.eraseToAnyPublisher()
setupStep3()
}
else {
setupStep3()
}
})
}
self.currentHandlerSubscriber?.cancel()
self.currentHandlerSubscriber = self.currentValuePublisher?.receive(on: DispatchQueue.main).sink(receiveValue: { val in
if val % 2 == 0 {
DispatchQueue.main.async {
self.showFirst = true
self.showSecond = false
self.showThird = false
}
self.previousValuePublisher = self.currentValuePublisher
self.currentValuePublisher = self.future1Publisher.compactMap { $0 }.eraseToAnyPublisher()
setupStep2()
}
else {
setupStep2()
}
})
}
func finalise() {
finalSubscriber = currentValuePublisher?.sink { val in
self.finalValue = val
// Reset everything
self.currentHandlerSubscriber?.cancel()
self.currentHandlerSubscriber = nil
self.currentAlertSubscriber?.cancel()
self.currentAlertSubscriber = nil
self.finalSubscriber?.cancel()
self.finalSubscriber = nil
self.initialValuePublisher = nil
self.previousValuePublisher = nil
self.currentValuePublisher = nil
self.future1Publisher.send(nil)
self.future2Publisher.send(nil)
self.future3Publisher.send(nil)
}
}
var body: some View {
return ZStack {
Button(action: { self.beginSeq() }) {
Text( finalValue != nil ? "Value is \(finalValue!)" : "Press to roll value")
}
Rectangle().frame(width: 0, height: 0)
.alert(isPresented: $showFirst) {
Alert(
title: Text("CB1"),
message: Text("Value is \(value)"),
primaryButton: .default(Text("Add 1")) {
self.showFirst = false
self.currentAlertSubscriber = self.previousValuePublisher?
.sink { val in
self.value = val + 1
self.future1Publisher.send(self.value)
}
},
secondaryButton: .default(Text("Make 0")) {
self.showFirst = false
self.currentAlertSubscriber = self.previousValuePublisher?
.sink { val in
self.value = 0
self.future1Publisher.send(self.value)
}
}
)
}
Rectangle().frame(width: 0, height: 0)
.alert(isPresented: $showSecond) {
Alert(
title: Text("CB2"),
message: Text("Value is \(value)"),
primaryButton: .default(Text("Add 3")) {
self.showSecond = false
self.currentAlertSubscriber = self.previousValuePublisher?
.sink { val in
self.value = val + 3
self.future2Publisher.send(self.value)
}
},
secondaryButton: .default(Text("Mult 2")) {
self.showSecond = false
self.currentAlertSubscriber = self.previousValuePublisher?
.sink { val in
self.value = val * 2
self.future2Publisher.send(self.value)
}
}
)
}
Rectangle().frame(width: 0, height: 0)
.alert(isPresented: $showThird) {
Alert(
title: Text("CB3"),
message: Text("Value is \(value)"),
primaryButton: .default(Text("Add 20")) {
self.showThird = false
self.currentAlertSubscriber = self.previousValuePublisher?
.sink { val in
self.value = val + 20
self.future3Publisher.send(self.value)
}
},
secondaryButton: .default(Text("Change Nothing")) {
// Nothing
self.showThird = false
self.currentAlertSubscriber = self.previousValuePublisher?
.sink { val in
self.future3Publisher.send(self.value)
}
}
)
}
}
}
}
代码上的一些警告
main.async
中完成,因为当两个布尔状态在同一渲染周期中处于活动状态时,警报将不会渲染编辑: 我还重写了上面的代码以仅使用回调-无需合并。它更容易阅读,变量更少,并且可以正常工作。它非常静态-但这是其他地方简化的权衡。我肯定在这里想念什么-这就是Combine擅长的地方
struct TestingStructure {
var originalValue: Int? = nil
var currentValue: Int? = nil
var checked1 = false
var checked2 = false
var checked3 = false
}
struct MultipleChainOfAlertsView: View {
@State var showFirst = false
@State var showSecond = false
@State var showThird = false
@State var testingStructure: TestingStructure? = nil
var value: Int {
testingStructure?.currentValue ?? 0
}
@State var finalValue: Int? = nil
func beginSeq() {
let value = Int.random(in: 0...100)
let testingStructure = TestingStructure(originalValue: value, currentValue: value, checked1: false, checked2: false, checked3: false)
testStructure(testingStructure)
}
func testStructure(_ testingStructure: TestingStructure) {
var structure = testingStructure
if let value = structure.currentValue
{
if structure.checked1 == false {
if value % 2 == 0 {
showFirst = true
self.testingStructure = structure
return
}
else {
structure.checked1 = true
}
}
if structure.checked2 == false {
if value > 2 {
showSecond = true
self.testingStructure = structure
return
}
else {
structure.checked2 = true
}
}
if structure.checked3 == false {
if value < 90 {
showThird = true
self.testingStructure = structure
return
}
else {
structure.checked3 = true
}
}
finalValue = structure.currentValue
}
}
var body: some View {
return ZStack {
Button(action: { self.beginSeq() }) {
Text( finalValue != nil ? "Value is \(finalValue!)" : "Press to roll value")
}
Rectangle().frame(width: 0, height: 0)
.alert(isPresented: $showFirst) {
Alert(
title: Text("CB1"),
message: Text("Value is \(value)"),
primaryButton: .default(Text("Add 1")) {
if var structure = self.testingStructure,
let value = structure.currentValue
{
structure.currentValue = value + 1
structure.checked1 = true
DispatchQueue.main.async{ self.testStructure(structure) }
}
},
secondaryButton: .default(Text("Make 0")) {
if var structure = self.testingStructure
{
structure.currentValue = 0
structure.checked1 = true
DispatchQueue.main.async{ self.testStructure(structure) }
}
}
)
}
Rectangle().frame(width: 0, height: 0)
.alert(isPresented: $showSecond) {
Alert(
title: Text("CB2"),
message: Text("Value is \(value)"),
primaryButton: .default(Text("Add 3")) {
if var structure = self.testingStructure,
let value = structure.currentValue
{
structure.currentValue = value + 3
structure.checked2 = true
DispatchQueue.main.async{ self.testStructure(structure) }
}
},
secondaryButton: .default(Text("Mult 2")) {
if var structure = self.testingStructure,
let value = structure.currentValue
{
structure.currentValue = value * 2
structure.checked2 = true
DispatchQueue.main.async{ self.testStructure(structure) }
}
}
)
}
Rectangle().frame(width: 0, height: 0)
.alert(isPresented: $showThird) {
Alert(
title: Text("CB3"),
message: Text("Value is \(value)"),
primaryButton: .default(Text("Add 20")) {
if var structure = self.testingStructure,
let value = structure.currentValue
{
structure.currentValue = value + 20
structure.checked3 = true
DispatchQueue.main.async{ self.testStructure(structure) }
}
},
secondaryButton: .default(Text("Change Nothing")) {
if var structure = self.testingStructure
{
structure.checked3 = true
DispatchQueue.main.async{ self.testStructure(structure) }
}
}
)
}
}
}
}