Combine is a responsive programming framework launched by Apple in 2019 for handling value streams that change over time. The following are the basic concepts and usage methods of Combine.
Source of value generation
Can send 0 or more values
May end with completion or error
Receive values from Publisher
Can control the demand for data flow
Convert, filter, and combine values from Publisher
import Combine
// 1. Just - Send a single value and then complete
let justPublisher = Just("Hello, World!")
// 2. Sequence - Sending values in the sequence
let sequencePublisher = [1, 2, 3, 4, 5].publisher
// 3. Future - The result of asynchronous operation
func fetchData() -> Future<String, Error> {
return Future { promise in
// Simulate asynchronous operations
DispatchQueue.global().asyncAfter(deadline: .now() + 1) {
promise(.success("Data fetched"))
}
}
}
// 4. @Published 属性包装器
class DataModel {
@Published var name: String = "Initial"
}
// Use Sink Subscription
var cancellables = Set<AnyCancellable>()
// Subscribe Just
justPublisher
.sink { value in
print("Received value: \(value)")
}
.store(in: &cancellables)
// Subscribe to Sequence
sequencePublisher
.sink(
receiveCompletion: { completion in
switch completion {
case .finished:
print("Finished successfully")
case .failure(let error):
print("Failed with error: \(error)")
}
},
receiveValue: { value in
print("Received: \(value)")
}
)
.store(in: &cancellables)
// Conversion Operators
sequencePublisher
.map { $0 * 2 } // Convert each value
.filter { $0 > 5 } // Filter value
.reduce(0, +) // Aggregate value
.sink { print("Result: \($0)") }
.store(in: &cancellables)
// Combination operator
let publisher1 = [1, 2, 3].publisher
let publisher2 = ["A", "B", "C"].publisher
Publishers.Zip(publisher1, publisher2)
.sink { print("Zipped: \($0), \($1)") }
.store(in: &cancellables)
// error handling
enum MyError: Error {
case testError
}
Fail(outputType: String.self, failure: MyError.testError)
.catch { error in
Just("Recovered from error")
}
.sink { print($0) }
.store(in: &cancellables)
import UIKit
import Combine
class ViewController: UIViewController {
@IBOutlet weak var label: UILabel!
@IBOutlet weak var textField: UITextField!
@IBOutlet weak var button: UIButton!
private var cancellables = Set<AnyCancellable>()
override func viewDidLoad() {
super.viewDidLoad()
setupBindings()
}
private func setupBindings() {
// Monitor text box changes
NotificationCenter.default
.publisher(for: UITextField.textDidChangeNotification, object: textField)
.compactMap { ($0.object as? UITextField)?.text }
.sink { [weak self] text in
self?.label.text = "You typed: \(text)"
}
.store(in: &cancellables)
// Button click event
button.publisher(for: .touchUpInside)
.sink { [weak self] _ in
self?.handleButtonTap()
}
.store(in: &cancellables)
}
private func handleButtonTap() {
print("Button tapped!")
}
}
struct User: Codable {
let id: Int
let name: String
let email: String
}
class UserService {
private var cancellables = Set<AnyCancellable>()
func fetchUsers() -> AnyPublisher<[User], Error> {
guard let url = URL(string: "https://jsonplaceholder.typicode.com/users") else {
return Fail(error: URLError(.badURL))
.eraseToAnyPublisher()
}
return URLSession.shared.dataTaskPublisher(for: url)
.map(\.data)
.decode(type: [User].self, decoder: JSONDecoder())
.eraseToAnyPublisher()
}
func loadUsers() {
fetchUsers()
.receive(on: DispatchQueue.main) // Switch to the main thread
.sink(
receiveCompletion: { completion in
switch completion {
case .finished:
print("Request completed")
case .failure(let error):
print("Error: \(error)")
}
},
receiveValue: { users in
print("Received users: \(users)")
}
)
.store(in: &cancellables)
}
}
class TimerExample {
private var cancellables = Set<AnyCancellable>()
func startTimer() {
Timer.publish(every: 1.0, on: .main, in: .common)
.autoconnect()
.sink { [weak self] date in
print("Timer fired at: \(date)")
self?.handleTimerTick()
}
.store(in: &cancellables)
}
private func handleTimerTick() {
// Process timer trigger
}
}
class TimerExample {
private var cancellables = Set<AnyCancellable>()
func startTimer() {
Timer.publish(every: 1.0, on: .main, in: .common)
.autoconnect()
.sink { [weak self] date in
print("Timer fired at: \(date)")
self?.handleTimerTick()
}
.store(in: &cancellables)
}
private func handleTimerTick() {
// Process timer trigger
}
}
class MyViewController: UIViewController {
private var cancellables = Set<AnyCancellable>()
deinit {
// Automatically cancel all subscriptions
cancellables.forEach { $0.cancel() }
}
}
These are the basic usage methods of Combine. Combine provides powerful responsive programming capabilities, particularly suitable for handling asynchronous event streams and data binding.