A declarative Swift API to process values over time
“Uma API declarativa do Swift para processar valores ao longo do tempo. Esses valores podem representar muitos tipos de eventos assíncronos”
Publishers
Os publishers são tipos que podem emitir valores ao longo do tempo para uma ou mais partes interessadas, como subscribers.
protocol Publisher {
associatedtype Output // tipo de valores que o publisher emite
associatedtype Failure: Error // tipo de erro que o publisher pode emitir
func receive<S>(subscriber: S)
where S : Subscriber, Self.Failure == S.Failure, Self.Output == S.Input
}
Um publisher pode emitir três tipos de eventos:
Subscribers
Um Subscriber se inscreve em um Publisher para receber seus valores.
protocol Subscriber {
associatedtype Input // Tipo de valores que o subscriber recebe
associatedtype Failure: Error // Tipo de erro que o subscriber pode receber
// ...
}
Subscriptions
Subscriptions representam a conexão entre publishers e subscribers.
- Publishers fornecem uma Subscription para seus subscribers:
- Uma subscription possui o método .cancel() utilizado para desalocar uma assinatura.
Consumindo publishers
Combine nos fornece dois assinantes como operadores de um Publisher:
sink(receiveCompletion:receiveValue:)
let publisher = [0, 1, 2, 3, 4].publisher
let subscription = publisher
.sink(
receiveCompletion: { completion in
print("Completion: \(completion)")
},
receiveValue: { value in
print("Value: \(value)")
}
)
// Saída:
// Value: 0
// Value: 1
// Value: 2
// Value: 3
// Value: 4
// Completion: finished
assign(to:on:)
class MyClass {
var anInt: Int = 0 {
didSet {
print("anInt was set to: \(anInt)", terminator: "; ")
}
}
}
var myObject = MyClass()
let myRange = (0...2)
let subscription = myRange.publisher.assign(to: \.anInt, on: myObject)
// Saída:
// "anInt was set to: 0; anInt was set to: 1; anInt was set to: 2"
Subjects
Subject é um publisher usado para “injetar” valores em um fluxo.
PassthroughSubject<Output, Failure>
: permite que você publique novos valores sob demanda;- Útil para notificar interações do usuário
let subject = PassthroughSubject<String, Never>()
let subscription = subject
.sink(
receiveCompletion: { print("Completion: \($0)") },
receiveValue: { print("Value: \($0)") }
)
subject.send("Hello")
subject.send("World")
subject.send(completion: .finished)
subject.send("!")
// Saída:
// Value: Hello
// Value: World
// Completion: finished
CurrentValueSubject<Output, Failure>
: baseado no PassthroughSubject, porém permite saber qual o seu valor atual:
let subject = CurrentValueSubject<Int, Never>(0)
let subscription = subject
.sink(receiveValue: { print($0) })
subject.send(1)
subject.send(2)
print("current value \(subject.value)")
subject.value = 3
print("current value \(subject.value)")
// Saída:
// 0
// 1
// 2
// current value 2
// 3
// current value 3
Operators
Operators montam uma cadeia de republishers que processa elementos produzidos por publishers upstream.
let publisher = (1...3).publisher
let subscription = publisher
.map { $0 * 2 }
.sink(receiveValue: { print($0) })
// Saída:
// 2
// 4
// 6
Encadeamento de operadores
let publisher1 = [1, 1, 2, 2, 3, 3].publisher
let publisher2 = [4, 4, 5, 5, 6, 6].publisher
let formatter = NumberFormatter()
formatter.numberStyle = .spellOut
let subscription = publisher1
.merge(with: publisher2)
.removeDuplicates()
.map { formatter.string(for: NSNumber(integerLiteral: $0)) ?? "" }
.sink { print($0) }
// Saída:
// one
// two
// three
// four
// five
// six
Exemplo prático
Links úteis
Livros
Artigos
Vídeos
- try! Swift NYC 2019 – Getting Started with Combine
- Getting started with Combine + UIKit in Swift
- iOS 13 Swift Tutorial: Combine Framework – A Practical Introduction with UIKit
- Migrating to Combine