@Published Property Wrapper
@Published 适合于ObserabledObject配合使用,在存储属性上添加@Published,将会使得有值变化时,自动发出publisher
@Published的wrappedValue
@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
@propertyWrapper public struct Published<Value> {
/// Initialize the storage of the Published property as well as the corresponding `Publisher`.
public init(wrappedValue: Value)
public init(initialValue: Value)
/// A publisher for properties marked with the `@Published` attribute.
public struct Publisher : Publisher {
/// The kind of values published by this publisher.
public typealias Output = Value
/// The kind of errors this publisher might publish.
///
/// Use `Never` if this `Publisher` does not publish errors.
public typealias Failure = Never
/// This function is called to attach the specified `Subscriber` to this `Publisher` by `subscribe(_:)`
///
/// - SeeAlso: `subscribe(_:)`
/// - Parameters:
/// - subscriber: The subscriber to attach to this `Publisher`.
/// once attached it can begin to receive values.
public func receive<S>(subscriber: S) where Value == S.Input, S : Subscriber, S.Failure == Published<Value>.Publisher.Failure
}
/// The property that can be accessed with the `$` syntax and allows access to the `Publisher`
public var projectedValue: Published<Value>.Publisher { mutating get }
}
你是否跟我一样有这样的困惑,为什么这个property wrapper中没有看到wrappedValue,要实现一个property wrapper这不是必须的吗?
再来回顾一下State,同样的property wrapper,实现了wrappedValue
@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
@frozen @propertyWrapper public struct State<Value> : DynamicProperty {
/// Initialize with the provided initial value.
public init(wrappedValue value: Value)
/// Initialize with the provided initial value.
public init(initialValue value: Value)
/// The current state value.
public var wrappedValue: Value { get nonmutating set }
/// Produces the binding referencing this state value
public var projectedValue: Binding<Value> { get }
}
如果你把上面的Published再Xcode中自己实现,是会提示错误没有写wrappedValue实现的,所以说明系统的实现内部写了,但是为什么看不到?
唯一的可能是,wrappedValue的访问级别为internal,也就是说其实@Published的实际实现声明了类似于interval var wrappedValue:Value的内容,这满足了property wrapper的要求。 但是设计者不想暴露出来给外部使用,所以标记为internal,使其wrappedValue在框架之外是不可见的。
@Published的使用
既然wrappedValue不想暴露出来被外部使用,意图就是让使用projectedValue,它的类型是Published
Publisher是一个在@Published属性包装器内部的嵌套结构体类型,同时也遵守Publisher协议
这样的设计使得标记为@Published的属性投影($)都可以assign或sink订阅处理,使用各种Operator,如下面代码示例
class Person: ObservableObject{
@Published var email = ""
}
let remoteVerify = $email
.debounce(for: .milliseconds(500),scheduler: DispatchQueue.main)
.removeDuplicates()
.eraseToAnyPublisher()
创造Publisher的常见方式
- 定义一个struct
- 添加需要从外部传入的参数作为存储属性
- 添加一个计算属性publisher,遵循AnyPublisher<Output, Failure>,具体Output和Failure类型根据业务需要设计
- 使用初始化结构体,调用publisher,返回遵循Publisher协议对象,视情况后续Operator操作
struct EmailCheck {
let email: String
var publisher: AnyPublisher<Bool, Never> {
Future<Bool, Never> { promise in
DispatchQueue.global().asyncAfter(deadline: .now() + 0.5) {
if self.email.lowercased() !" "xxx@gmail.com" {
promise(.success(false))
} else {
promise(.success(true))
}
}
}
.receive(on: DispatchQueue.main)
.eraseToAnyPublisher()
}
}
Publishers.CombineLatest 这种大写struct直接使用的方式,有时更加高效