Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@d-date
Last active April 15, 2024 09:58
Show Gist options
  • Star 40 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save d-date/735fa126e37ed88ff9d0011f6dc0510b to your computer and use it in GitHub Desktop.
Save d-date/735fa126e37ed88ff9d0011f6dc0510b to your computer and use it in GitHub Desktop.
import Combine
import UIKit
public protocol CombineCompatible {}
// MARK: - UIControl
public extension UIControl {
final class Subscription<SubscriberType: Subscriber, Control: UIControl>: Combine.Subscription where SubscriberType.Input == Control {
private var subscriber: SubscriberType?
private let input: Control
public init(subscriber: SubscriberType, input: Control, event: UIControl.Event) {
self.subscriber = subscriber
self.input = input
input.addTarget(self, action: #selector(eventHandler), for: event)
}
public func request(_ demand: Subscribers.Demand) {}
public func cancel() {
subscriber = nil
}
@objc private func eventHandler() {
_ = subscriber?.receive(input)
}
}
struct Publisher<Output: UIControl>: Combine.Publisher {
public typealias Output = Output
public typealias Failure = Never
let output: Output
let event: UIControl.Event
public init(output: Output, event: UIControl.Event) {
self.output = output
self.event = event
}
public func receive<S>(subscriber: S) where S: Subscriber, Self.Failure == S.Failure, Self.Output == S.Input {
let subscription = Subscription(subscriber: subscriber, input: output, event: event)
subscriber.receive(subscription: subscription)
}
}
}
extension UIControl: CombineCompatible {}
public extension CombineCompatible where Self: UIControl {
func publisher(for event: UIControl.Event) -> UIControl.Publisher<UIControl> {
.init(output: self, event: event)
}
}
// MARK: - UIBarButtonItem
public extension UIBarButtonItem {
final class Subscription<SubscriberType: Subscriber, Input: UIBarButtonItem>: Combine.Subscription where SubscriberType.Input == Input {
private var subscriber: SubscriberType?
private let input: Input
public init(subscriber: SubscriberType, input: Input) {
self.subscriber = subscriber
self.input = input
input.target = self
input.action = #selector(eventHandler)
}
public func request(_ demand: Subscribers.Demand) {}
public func cancel() {
subscriber = nil
}
@objc private func eventHandler() {
_ = subscriber?.receive(input)
}
}
struct Publisher<Output: UIBarButtonItem>: Combine.Publisher {
public typealias Output = Output
public typealias Failure = Never
let output: Output
public init(output: Output) {
self.output = output
}
public func receive<S>(subscriber: S) where S: Subscriber, Self.Failure == S.Failure, Self.Output == S.Input {
let subscription = Subscription(subscriber: subscriber, input: output)
subscriber.receive(subscription: subscription)
}
}
}
extension UIBarButtonItem: CombineCompatible {
public convenience init(image: UIImage?, style: UIBarButtonItem.Style, cancellables: inout Set<AnyCancellable>, action: @escaping () -> Void) {
self.init(image: image, style: style, target: nil, action: nil)
self.publisher.sink { _ in action() }.store(in: &cancellables)
}
public convenience init(image: UIImage?, landscapeImagePhone: UIImage?, style: UIBarButtonItem.Style, cancellables: inout Set<AnyCancellable>, action: @escaping () -> Void) {
self.init(image: image, landscapeImagePhone: landscapeImagePhone, style: style, target: nil, action: nil)
self.publisher.sink { _ in action() }.store(in: &cancellables)
}
public convenience init(title: String?, style: UIBarButtonItem.Style, cancellables: inout Set<AnyCancellable>, action: @escaping () -> Void) {
self.init(title: title, style: style, target: nil, action: nil)
self.publisher.sink { _ in action() }.store(in: &cancellables)
}
public convenience init(barButtonSystemItem systemItem: UIBarButtonItem.SystemItem, cancellables: inout Set<AnyCancellable>, action: @escaping () -> Void) {
self.init(barButtonSystemItem: systemItem, target: nil, action: nil)
self.publisher.sink { _ in action() }.store(in: &cancellables)
}
}
public extension CombineCompatible where Self: UIBarButtonItem {
var publisher: UIBarButtonItem.Publisher<UIBarButtonItem> {
.init(output: self)
}
}
import UIKit
import Combine
class ViewController: UIViewController {
var cancellables: Set<AnyCancellable> = []
let button1 = UIButton()
let button2 = UIButton()
override func viewDidLoad() {
super.viewDidLoad()
// No good
let textField = UITextField()
textField.publisher(for: \.text)
.map { _ in (textField.text ?? "").count == 4 }
.assign(to: \.isEnabled, on: button1)
.store(in: &cancellables)
// OK
textField.publisher(for: .allEditingEvents)
.map { _ in (textField.text ?? "").count == 4 }
.assign(to: \.isEnabled, on: button1)
.store(in: &cancellables)
// OK
let completion: (Notification) -> Void = { _ in }
NotificationCenter.default.publisher(for: UIResponder.keyboardWillShowNotification)
.receive(on: DispatchQueue.main)
.sink { [weak self] in completion($0) }
.store(in: &cancellables)
// OK
button1.publisher(for: .touchUpInside)
.sink(receivedValue: {
// do something
})
.store(in: &cancellables)
// OK
let buttonEvent: PassthroughSubject<UIControl, Never> = .init()
button1.publisher(for: .touchUpInside)
.subscribe(buttonEvent)
.store(in: &cancellables)
// OK
Publishers.Merge(button1.publisher(for: .touchUpInside), button2.publisher(for: .touchUpInside))
.subscribe(buttonEvent)
.store(in: &cancellables)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment