Trong khung Kết hợp, nhà xuất bản là loại tuân theo giao thức Publisher
. Nó chịu trách nhiệm cung cấp một luồng giá trị cho người đăng ký. Giao thức Publisher
xác định hai loại liên quan: Output
và Failure
, cho biết loại giá trị mà nhà xuất bản có thể phát ra và loại lỗi mà nhà xuất bản có thể đưa ra tương ứng.
Nhà xuất bản có thể phát ra một hoặc nhiều giá trị theo thời gian và nó cũng có thể hoàn thành hoặc không thành công. Khi một người đăng ký đăng ký với một nhà xuất bản, nhà xuất bản gọi phương thức receive(subscription:)
của người đăng ký và chuyển cho nó một đối tượng Subscription
mà người đăng ký có thể sử dụng để kiểm soát luồng giá trị. Người đăng ký cũng có thể gọi phương thức receive(_:)
trên nhà xuất bản để nhận các giá trị mới.
Khung Kết hợp cung cấp một số nhà xuất bản tích hợp, chẳng hạn như Just
, Fail
, Empty
, Deferred
và Sequence
, có thể được sử dụng để tạo nhiều loại nhà xuất bản khác nhau. Ngoài ra, bạn có thể tạo nhà xuất bản tùy chỉnh của riêng mình bằng cách tuân thủ giao thức Publisher
và triển khai các phương pháp bắt buộc.
Các nhà xuất bản cũng có thể được kết hợp với nhau để tạo ra các quy trình phức tạp hơn. Khung Kết hợp cung cấp một số toán tử tích hợp có thể được sử dụng để sửa đổi và kết hợp các nhà xuất bản, chẳng hạn như map
, filter
, reduce
, flatMap
, zip
và merge
. Các toán tử này được cung cấp bởi phần mở rộng giao thức của Publisher
và có thể được gọi trên bất kỳ nhà xuất bản nào.
Bây giờ tôi muốn cung cấp cho bạn một số nhà xuất bản hữu ích mà tôi sử dụng trong các dự án của mình.
Để triển khai một nhà xuất bản sử dụng bộ hẹn giờ lặp lại với khoảng thời gian tùy chỉnh trong Swift, bạn có thể sử dụng lớp Bộ Timer
từ Foundation framework. Đây là một ví dụ về cách bạn có thể làm điều đó:
RepeatingTimeSubscription
phù hợp với giao thức Subscription
:
private class RepeatingTimerSubscription<S: Subscriber>: Subscription where S.Input == Void { private let interval: TimeInterval private let queue: DispatchQueue private var subscriber: S? private var timer: Timer? init(interval: TimeInterval, queue: DispatchQueue, subscriber: S) { self.interval = interval self.queue = queue self.subscriber = subscriber } func request(_ demand: Subscribers.Demand) { timer?.invalidate() timer = Timer.scheduledTimer( withTimeInterval: interval, repeats: true ) { [weak self] _ in self?.queue.async { _ = self?.subscriber?.receive() } } } func cancel() { timer?.invalidate() timer = nil subscriber = nil } }
RepeatingTimePublisher
tuân thủ giao thức của Publisher
:
import Foundation import Combine final class RepeatingTimerPublisher: Publisher { typealias Output = Void typealias Failure = Never private let interval: TimeInterval private let queue: DispatchQueue init(interval: TimeInterval, queue: DispatchQueue = .main) { self.interval = interval self.queue = queue } func receive<S>(subscriber: S) where S: Subscriber, Failure == S.Failure, Output == S.Input { let subscription = RepeatingTimerSubscription( interval: interval, queue: queue, subscriber: subscriber ) subscriber.receive(subscription: subscription) } }
Để sử dụng nhà xuất bản này, bạn có thể tạo một phiên bản của nó và đăng ký nó bằng cách sử dụng phương pháp sink
của giao thức Publisher
.
Ví dụ:
private var cancellable: AnyCancellable? func subscribeOnTimer(interval: TimeInterval) { let publisher = RepeatingTimerPublisher(interval: interval) cancellable = publisher.sink { print("Timer fired!") } } //TEST THE METHOD subscribeOnTimer(interval: 5.0)
Thao tác này sẽ in "Hẹn giờ đã kích hoạt!" cứ sau 5 giây. Bạn có thể hủy đăng ký bằng cách gọi phương thức cancel
trên đối tượng AnyCancellable
được trả về bởi phương thức sink
.
Ví dụ:
deinit { cancellable?.cancel() }
Để triển khai tính năng bỏ phiếu dài bằng cách sử dụng khung Kết hợp trong Swift, bạn có thể tạo một nhà xuất bản đưa ra yêu cầu mạng ở một khoảng thời gian xác định và trả về phản hồi làm đầu ra. Đây là một ví dụ về cách bạn có thể làm điều đó:
Enum lỗi tùy chỉnh cho các trường hợp không thành công.
enum CustomError: Error { case invalidResponse case invalidDecoding case error }
LongPollingSubscription
phù hợp với giao thức Subscription
:
private class LongPollingSubscription<S: Subscriber, Output: Decodable>: Subscription where S.Input == Output, S.Failure == CustomError { private let url: URL private let interval: TimeInterval private let decoder: JSONDecoder private var subscriber: S? private var timer: Timer? private var task: URLSessionDataTask? init( url: URL, interval: TimeInterval, subscriber: S, decoder: JSONDecoder = JSONDecoder() ) { self.url = url self.interval = interval self.subscriber = subscriber self.decoder = decoder } func request(_ demand: Subscribers.Demand) { timer?.invalidate() timer = Timer.scheduledTimer( withTimeInterval: interval, repeats: true ) { [weak self] _ in self?.makeRequest() } makeRequest() } func cancel() { timer?.invalidate() timer = nil task?.cancel() task = nil subscriber = nil } private func makeRequest() { task = URLSession.shared.dataTask(with: url) { [weak self] data, response, error in guard let self else { return } if let error = error as? S.Failure { self.subscriber?.receive( completion: .failure(error) ) return } guard let data else { self.subscriber?.receive( completion: .failure(.invalidResponse) ) return } do { let output = try self.decoder.decode( Output.self, from: data ) _ = self.subscriber?.receive(output) } catch { self.subscriber?.receive( completion: .failure(.invalidDecoding) ) } } task?.resume() } }
LongPollingPublisher
tuân thủ giao thức của Publisher
:
final class LongPollingPublisher<Output: Decodable>: Publisher { typealias Failure = CustomError private let url: URL private let interval: TimeInterval init(url: URL, interval: TimeInterval) { self.url = url self.interval = interval } func receive<S>(subscriber: S) where S: Subscriber, Failure == S.Failure, Output == S.Input { let subscription = LongPollingSubscription( url: url, interval: interval, subscriber: subscriber ) subscriber.receive(subscription: subscription) } }
<Output: Decodable>
có nghĩa là bạn có thể sử dụng bất kỳ loại phản hồi chung nào phù hợp với Decodable
thức Có thể giải mã.
Để thử nghiệm, bạn cần tạo một mô hình phù hợp với Decodable
. Tôi sử dụng API công khai của https://pixabay.com/api .
Hãy để nó là cấu trúc PhotoResponse:
struct PhotoResponse: Decodable { struct Photo: Decodable { let user: String let id: Int let largeImageURL: String } let hits: [Photo] let total: Int }
Để sử dụng nhà xuất bản này, bạn có thể tạo một phiên bản của nó và đăng ký nó bằng cách sử dụng phương pháp sink
của giao thức Publisher
. Ví dụ:
private var cancellable: AnyCancellable? private func pollingTest() { let url = URL(string: "https://pixabay.com/api/?key={your_key}")! let publisher = LongPollingPublisher<PhotoResponse>( url: url, interval: 5.0 ) cancellable = publisher.sink(receiveCompletion: { completion in switch completion { case .finished: print("Completed") case .failure(let error): print("Error: \(error)") } }, receiveValue: { response in print("Received response: \(response)") }) } //TEST THE METHOD pollingTest()
Có một số nhà xuất bản tùy chỉnh hữu ích có thể được tạo bằng cách sử dụng khung Kết hợp trong Swift. Đây là vài ví dụ:
NotificationCenterPublisher
: Nhà xuất bản phát ra các giá trị khi một thông báo cụ thể được đăng lên NotificationCenter
. Bạn có thể sử dụng nhà xuất bản này để phản ứng với các sự kiện dành riêng cho hệ thống hoặc ứng dụng, chẳng hạn như xoay thiết bị hoặc thay đổi trạng thái mạng.KeyboardPublisher
: Nhà xuất bản phát ra các giá trị khi bàn phím được hiển thị hoặc ẩn. Bạn có thể sử dụng nhà xuất bản này để điều chỉnh bố cục của chế độ xem của mình khi bàn phím được hiển thị hoặc loại bỏ.CoreLocationPublisher
: Nhà xuất bản đưa ra các giá trị khi vị trí của người dùng thay đổi. Nhà xuất bản này có thể được sử dụng để theo dõi vị trí của người dùng và thực hiện các hành động dựa trên vị trí của họ.UIControlEventPublisher
: Nhà xuất bản phát ra giá trị khi một sự kiện cụ thể xảy ra trên UIControl
chẳng hạn như UIButton hoặc UITextField. Điều này có thể được sử dụng để xử lý các tương tác của người dùng theo cách phản ứng.Đây chỉ là một vài ví dụ về các loại nhà xuất bản tùy chỉnh có thể được tạo bằng cách sử dụng khung Kết hợp. Điều quan trọng là hiểu các yêu cầu của ứng dụng của bạn và sử dụng các khối xây dựng có sẵn do khung cung cấp để tạo các nhà xuất bản đáp ứng các yêu cầu đó.
Cuối cùng, điều quan trọng cần lưu ý là khung Kết hợp sử dụng mô hình lập trình phản ứng chức năng, đây là mô hình lập trình xử lý các luồng sự kiện theo thời gian. Các nhà xuất bản, cùng với người đăng ký và nhà khai thác, là các khối xây dựng cốt lõi của mô hình này và họ giúp dễ dàng tạo các ứng dụng phức tạp và đáp ứng.