首页 > 解决方案 > 使用发布者过滤字符串字段中的数字

问题描述

我正在为用于引入数量的 textField 构建一个包装器。我正在尝试使用 Combine 来构建一切。其中一个用例在于,如果文本字段发送的 stringValue 有一个字母,我会过滤这些字母并将新值重新分配给同一个 var,因此文本字段会过滤这些值。还有一个代码可以将此值更改为 int,以便其他组件可以读取该 int 值。这是代码:

class QuantityPickerViewModel: ObservableObject {
    private var subscriptions: Set<AnyCancellable> = Set<AnyCancellable>()
    @Published var stringValue: String = ""
    @Published var value : Int? = nil
    
    init(initialValue: Int?) {
        $stringValue
            .removeDuplicates()
            .print("pre-filter")
            .map {
                $0.filter {$0.isNumber}
            }
            .print("post-filter")
            .map {
                Int($0)
            }
            .assign(to: \.value, on: self)
            .store(in: &subscriptions)

        $value.map {
            $0 != nil ? String($0!): ""
        }
        .print("Value")
        .assign(to: \.stringValue, on:self)
        .store(in: &subscriptions)
    
        value = initialValue
    }
}

我使用测试验证行为,我将只测试失败的测试:

class QuantityPickerViewModelTest: AppTestCase {
    var model: QuantityPickerViewModel!
    override func setUpWithError() throws {
        super.setUp()
        model = QuantityPickerViewModel(initialValue: 10)
    }
    
    func test_changeStringValueWithLetters_filtersLettersAndChangesValue() {
        model.stringValue = "30a"
        
        XCTAssertEqual(model.value, 30)
        XCTAssertEqual(model.stringValue, "30") // fails saying stringValue is still "30a"
    }
}

测试的输出是:

Test Case '-[SourdoughMasterTests.QuantityPickerViewModelTest test_changeStringValueWithLetters_filtersLettersAndChangesValue]' started.
pre-filter: receive subscription: (RemoveDuplicates)
post-filter: receive subscription: (Print)
post-filter: request unlimited
pre-filter: request unlimited
pre-filter: receive value: ()
post-filter: receive value: ()
Value: receive subscription: (PublishedSubject)
Value: request unlimited
Value: receive value: ()
Value: receive value: (10)
pre-filter: receive value: (10)
post-filter: receive value: (10)
Value: receive value: (10)
pre-filter: receive value: (30a)
post-filter: receive value: (30)
Value: receive value: (30)
pre-filter: receive value: (30)
post-filter: receive value: (30)
Value: receive value: (30)
/Users/jpellat/workspace/SourdoughMaster/SourdoughMasterTests/QuantityPickerViewModelTest.swift:54: error: -[SourdoughMasterTests.QuantityPickerViewModelTest test_changeStringValueWithLetters_filtersLettersAndChangesValue] : XCTAssertEqual failed: ("30a") is not equal to ("30")

有谁知道为什么没有分配价值?谢谢

标签: iosswiftcombine

解决方案


这不是导致此问题的组合问题本身,但似乎Published发布者在属性上实际设置值之前发出。所以,基本上"30a"是覆盖在assign.

无论如何,这个循环管道链似乎有点可疑。我也不认为你真的需要在这里结合 - 它可以通过两个计算属性和一个公共存储属性来解决:

@Published 
private var _value: Int? = nil

var value: Int? {
   get { _value }
   set { _value = newValue }
}

var stringValue: String {
   get { _value?.description ?? "" }
   set {
      _value = Int(newValue.filter { "0"..."9" ~= $0 })
   }
}

推荐阅读