Github.com/agelessman/…
Subscribers’ role in Combine is that of a subscriber and is dedicated to receiving data.
assign
class AssignViewObservableObject: ObservableObject {
var cancellable: AnyCancellable?
@Published var text = ""
init(a) {
let publisher = PassthroughSubject<String.Never>()
cancellable = publisher
.assign(to: \.text, on: self)
publisher.send("Hello, world")}}Copy the code
public func assign<Root> (to keyPath: ReferenceWritableKeyPath<Root.Self.Output>, on object: Root) -> AnyCancellable
Copy the code
The first parameter of assign is of type ReferenceWritableKeyPath. ReferenceWritableKeyPath Requires that the parameter be a reference type and writable keypath.
Keypath is a very powerful technique in Swift. Swift is known to be strongly typed, and it is only keypath that allows us to access properties of a certain type.
Keypath is not the main content of this article, but its usage is discussed briefly. There are three main types of keypath:
KeyPath
: Can access only dataWritableKeyPath
: Can access and write dataReferenceWritableKeyPath
: Can only access and write data of reference type
For example, we have a student model:
struct Student {
let name: String
let age: Int
let score: Int
}
Copy the code
If we want to filter out the name,age, or score code, it looks something like this:
let students = [Student(name: "Zhang", age: 20, score: 80)]
print(students.map { $0.name })
print(students.map { $0.age })
print(students.map { $0.score })
Copy the code
Next, we can use KeyPath’s dark magic as follows:
extension Sequence {
func map<T> (_ keyPath: KeyPath<Element.T>)- > [T] {
map { $0[keyPath: keyPath] }
}
}
Copy the code
print(students.map(\.name))
print(students.map(\.age))
print(students.map(\.score))
Copy the code
This is just a simple example. The real power of KeyPath is in the ability to generalize. I won’t explain more about KeyPath here.
In short, Assign uses the power of KeyPath, but it can’t accept errors.
sink
public func sink(receiveValue: @escaping ((Self.Output) - >Void)) -> AnyCancellable
Copy the code
public func sink(receiveCompletion: @escaping ((Subscribers.Completion<Self.Failure- > >)Void), receiveValue: @escaping ((Self.Output) - >Void)) -> AnyCancellable
Copy the code
Compared with assign, sink is an all-powerful subscriber, which can be used in two ways:
- When publisher’s error type is
Never
, you can use the simple form, you can pass only onereceiveValue
Closure to receive data sent by Publisher - When publisher is of another type of error, you need to use a complex situation, passing in two closure parameters,
receiveCompletion
Used to receive completion events including.finished
and.failure
.receiveValue
Used to receive data
class SinkViewObservableObject: ObservableObject {
var cancellable: AnyCancellable?
@Published var text = ""
init(a) {
let publisher = PassthroughSubject<String.Never>()
cancellable = publisher
.sink(receiveValue: { value in
self.text = value
})
publisher.send("Hello, world")}}Copy the code
The code above is equivalent to the code below:
class SinkViewObservableObject: ObservableObject {
var cancellable: AnyCancellable?
@Published var text = ""
init(a) {
let publisher = PassthroughSubject<String.Never>()
cancellable = publisher
.assign(to: \.text, on: self)
publisher.send("Hello, world")}}Copy the code
When publisher calls sink, it returns an AnyCancellable type. The core method of this protocol is:
final public func cancel(a)
Copy the code
Calling this method cancels the pipline that is currently executing. In short, sink is the subscriber we usually use most in development.
onReceive
struct OnReceiveView: View {
private var timer = Timer.TimerPublisher(interval: 1.0,
runLoop: RunLoop.main,
mode: .common).autoconnect()
@State private var count = 0
var body: some View {
Text("\(count)")
.onReceive(timer) { _ in
self.count + = 1
}
.navigationBarTitle("onReceive")}}Copy the code
OnReceive is a subscriber used in SwiftUI, which is a method of View protocol. That is to say, as long as the View protocol is implemented, this method can be called:
extension View {
/// Adds an action to perform when this view detects data emitted by the
/// given publisher.
///
/// - Parameters:
/// - publisher: The publisher to subscribe to.
/// - action: The action to perform when an event is emitted by
/// `publisher`. The event emitted by publisher is passed as a
/// parameter to `action`.
///
/// - Returns: A view that triggers `action` when `publisher` emits an
/// event.
@inlinable public func onReceive<P> (_ publisher: P.perform action: @escaping (P.Output) - >Void) -> some View where P : Publisher.P.Failure = = Never
}
Copy the code
The above example is an example of timer, since it is not used much in SwiftUI, we will not explain in detail.
AnyCancellable
public func assign<Root> (to keyPath: ReferenceWritableKeyPath<Root.Self.Output>, on object: Root) -> AnyCancellable
Copy the code
public func sink(receiveCompletion: @escaping ((Subscribers.Completion<Self.Failure- > >)Void), receiveValue: @escaping ((Self.Output) - >Void)) -> AnyCancellable
Copy the code
Assign and sink, both of which return types are AnyCancellable, allow us to call.cancel() to cancel the current pipline, so we usually use an attribute to refer to this return value so that we can cancel the pipline if necessary.
class AnyCancellableViewObservableObject: ObservableObject {
var cancellable1: AnyCancellable
init(a) {
let publisher = PassthroughSubject<String.Never>()
cancellable1 = publisher
.sink(receiveValue: { print($0)}}}Copy the code
If multiple pipelines are used in an implementation of ObservableObject, they can be saved into a collection:
class AnyCancellableViewObservableObject: ObservableObject {
var cancellables: Set<AnyCancellable> = []
init(a) {
let publisher = PassthroughSubject<String.Never>()
publisher
.sink(receiveValue: { print($0) })
.store(in: &cancellables)
}
}
Copy the code