使用 Combine 观察通用值
Observe generic values with Combine
以这种类型受限的情况为例 class Parameter
,包装给定类型的值。
参数符合AnyParameter
,因此可以在不知道类型的情况下在应用中的任何地方传递。参数可以显示在值单元格中 AnyValueCell
- 在不知道底层值类型的情况下,您将如何观察变化?最好避免值单元格
updateObserver
函数 中的代码重复
- 这里可以使用
AnyPublisher
吗?如何使用?
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=])") }
}
}
以这种类型受限的情况为例 class Parameter
,包装给定类型的值。
参数符合AnyParameter
,因此可以在不知道类型的情况下在应用中的任何地方传递。参数可以显示在值单元格中 AnyValueCell
- 在不知道底层值类型的情况下,您将如何观察变化?最好避免值单元格
updateObserver
函数 中的代码重复
- 这里可以使用
AnyPublisher
吗?如何使用?
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=])") }
}
}