使用 Combine 观察通用值

Observe generic values with Combine

以这种类型受限的情况为例 class Parameter,包装给定类型的值。

参数符合AnyParameter,因此可以在不知道类型的情况下在应用中的任何地方传递。参数可以显示在值单元格中 AnyValueCell

import UIKit
import Combine

print("Hello Playground")

protocol AnyParameter {
    var anyValue: Any { get }
    func set(value: Any)
}

protocol ParameterProtocol: AnyParameter {
    associatedtype ValueType
    
    var value: ValueType { get }
    func set(value: ValueType)

}

public class Parameter<T>: ParameterProtocol {
    
    typealias ValueType = T
    
    @Published var value: T
    
    var anyValue: Any { value }
    
    init(value: T) {
        self.value = value
    }
    
    func set(value: Any) {
        guard let value = value as? T else { return }
        set(value: value)
    }
    
    func set(value: T) {
        self.value = value
    }
}


public class AnyValueCell {
    
    var parameter: AnyParameter {
        didSet {
            updateObserver()
        }
    }
    
    var observer: AnyCancellable?
    
    init(parameter: AnyParameter) {
        self.parameter = parameter
        updateObserver()
    }
    
    func updateObserver() {
        observer?.cancel()
        
        // This is the point of the question - How to make this generic?
        // ---->
        if let p = parameter as? Parameter<Int> {
            observer = p.$value.sink() { value in
                print("Update Cell -> \(value)")
            }
            return
        }

        if let p = parameter as? Parameter<Double> {
            observer = p.$value.sink() { value in
                print("Update Cell -> \(value)")
            }
            return
        }

        if let p = parameter as? Parameter<Bool> {
            observer = p.$value.sink() { value in
                print("Update Cell -> \(value)")
            }
            return
        }
        // <----

        print("Wrong param type")
    }
}

let intParam = Parameter<Int>(value: 42)
let doubleParam = Parameter<Double>(value: 3.14)
let boolParam = Parameter<Bool>(value: false)

var params: [AnyParameter] = [intParam, doubleParam, boolParam]

print ("--> Init Cells")
let cells: [AnyValueCell] = params.map { AnyValueCell(parameter: [=10=]) }

print ("--> Change values")
intParam.set(value: 21)
doubleParam.set(value: 1.618)
boolParam.set(value: true)

结果如预期:

Hello Playground
--> Init Cells
Update Cell -> 42
Update Cell -> 3.14
Update Cell -> false
--> Change values
Update Cell -> 21
Update Cell -> 1.618
Update Cell -> true

添加一个anyValuePublisher 属性。您可以(也许应该)将它添加到 AnyParameter,或者您可以像这样在单独的协议中定义它:

protocol AnyParameterPublishing: AnyParameter {
    var anyValuePublisher: AnyPublisher<Any, Never> { get }
}

extension Parameter: AnyParameterPublishing {
    var anyValuePublisher: AnyPublisher<Any, Never> {
        return $value.map { [=10=] as Any }.eraseToAnyPublisher()
    }
}

然后你可以这样使用它:

class AnyValueCell {
    // ...

    func updateObserver() {
        guard let publishing = (parameter as? AnyParameterPublishing) else {
            print("Wrong param type")
            return
        }
        observer = publishing.anyValuePublisher
            .sink { print("Update Cell -> \([=11=])") }
    }
}