diff --git a/Sources/CombineCocoa/Controls/UIButton+Combine.swift b/Sources/CombineCocoa/Controls/UIButton+Combine.swift index 3375ee2..74004e3 100644 --- a/Sources/CombineCocoa/Controls/UIButton+Combine.swift +++ b/Sources/CombineCocoa/Controls/UIButton+Combine.swift @@ -13,7 +13,7 @@ import UIKit @available(iOS 13.0, *) public extension UIButton { /// A publisher emitting tap events from this button. - var tapPublisher: AnyPublisher { + var clickPublisher: AnyPublisher { controlEventPublisher(for: .touchUpInside) } } diff --git a/Sources/CombineCocoa/Controls/UICollectionView+Combine.swift b/Sources/CombineCocoa/Controls/UICollectionView+Combine.swift index 920fafc..c1899f5 100644 --- a/Sources/CombineCocoa/Controls/UICollectionView+Combine.swift +++ b/Sources/CombineCocoa/Controls/UICollectionView+Combine.swift @@ -16,6 +16,11 @@ import Combine public extension UICollectionView { /// Combine wrapper for `collectionView(_:didSelectItemAt:)` var didSelectItemPublisher: AnyPublisher { + guard let delegateProxy = innerDelegateWrap?.proxy else { + return Empty(completeImmediately: false) + .eraseToAnyPublisher() + } + let selector = #selector(UICollectionViewDelegate.collectionView(_:didSelectItemAt:)) return delegateProxy.interceptSelectorPublisher(selector) .map { $0[1] as! IndexPath } @@ -24,6 +29,11 @@ public extension UICollectionView { /// Combine wrapper for `collectionView(_:didDeselectItemAt:)` var didDeselectItemPublisher: AnyPublisher { + guard let delegateProxy = innerDelegateWrap?.proxy else { + return Empty(completeImmediately: false) + .eraseToAnyPublisher() + } + let selector = #selector(UICollectionViewDelegate.collectionView(_:didDeselectItemAt:)) return delegateProxy.interceptSelectorPublisher(selector) .map { $0[1] as! IndexPath } @@ -32,6 +42,11 @@ public extension UICollectionView { /// Combine wrapper for `collectionView(_:didHighlightItemAt:)` var didHighlightItemPublisher: AnyPublisher { + guard let delegateProxy = innerDelegateWrap?.proxy else { + return Empty(completeImmediately: false) + .eraseToAnyPublisher() + } + let selector = #selector(UICollectionViewDelegate.collectionView(_:didHighlightItemAt:)) return delegateProxy.interceptSelectorPublisher(selector) .map { $0[1] as! IndexPath } @@ -40,6 +55,11 @@ public extension UICollectionView { /// Combine wrapper for `collectionView(_:didUnhighlightItemAt:)` var didUnhighlightRowPublisher: AnyPublisher { + guard let delegateProxy = innerDelegateWrap?.proxy else { + return Empty(completeImmediately: false) + .eraseToAnyPublisher() + } + let selector = #selector(UICollectionViewDelegate.collectionView(_:didUnhighlightItemAt:)) return delegateProxy.interceptSelectorPublisher(selector) .map { $0[1] as! IndexPath } @@ -48,6 +68,11 @@ public extension UICollectionView { /// Combine wrapper for `collectionView(_:willDisplay:forItemAt:)` var willDisplayCellPublisher: AnyPublisher<(cell: UICollectionViewCell, indexPath: IndexPath), Never> { + guard let delegateProxy = innerDelegateWrap?.proxy else { + return Empty<(cell: UICollectionViewCell, indexPath: IndexPath), Never>(completeImmediately: false) + .eraseToAnyPublisher() + } + let selector = #selector(UICollectionViewDelegate.collectionView(_:willDisplay:forItemAt:)) return delegateProxy.interceptSelectorPublisher(selector) .map { ($0[1] as! UICollectionViewCell, $0[2] as! IndexPath) } @@ -56,6 +81,11 @@ public extension UICollectionView { /// Combine wrapper for `collectionView(_:willDisplaySupplementaryView:forElementKind:at:)` var willDisplaySupplementaryViewPublisher: AnyPublisher<(supplementaryView: UICollectionReusableView, elementKind: String, indexPath: IndexPath), Never> { + guard let delegateProxy = innerDelegateWrap?.proxy else { + return Empty<(supplementaryView: UICollectionReusableView, elementKind: String, indexPath: IndexPath), Never>(completeImmediately: false) + .eraseToAnyPublisher() + } + let selector = #selector(UICollectionViewDelegate.collectionView(_:willDisplaySupplementaryView:forElementKind:at:)) return delegateProxy.interceptSelectorPublisher(selector) .map { ($0[1] as! UICollectionReusableView, $0[2] as! String, $0[3] as! IndexPath) } @@ -64,6 +94,11 @@ public extension UICollectionView { /// Combine wrapper for `collectionView(_:didEndDisplaying:forItemAt:)` var didEndDisplayingCellPublisher: AnyPublisher<(cell: UICollectionViewCell, indexPath: IndexPath), Never> { + guard let delegateProxy = innerDelegateWrap?.proxy else { + return Empty<(cell: UICollectionViewCell, indexPath: IndexPath), Never>(completeImmediately: false) + .eraseToAnyPublisher() + } + let selector = #selector(UICollectionViewDelegate.collectionView(_:didEndDisplaying:forItemAt:)) return delegateProxy.interceptSelectorPublisher(selector) .map { ($0[1] as! UICollectionViewCell, $0[2] as! IndexPath) } @@ -72,22 +107,128 @@ public extension UICollectionView { /// Combine wrapper for `collectionView(_:didEndDisplayingSupplementaryView:forElementKind:at:)` var didEndDisplaySupplementaryViewPublisher: AnyPublisher<(supplementaryView: UICollectionReusableView, elementKind: String, indexPath: IndexPath), Never> { + guard let delegateProxy = innerDelegateWrap?.proxy else { + return Empty<(supplementaryView: UICollectionReusableView, elementKind: String, indexPath: IndexPath), Never>(completeImmediately: false) + .eraseToAnyPublisher() + } + let selector = #selector(UICollectionViewDelegate.collectionView(_:didEndDisplayingSupplementaryView:forElementOfKind:at:)) return delegateProxy.interceptSelectorPublisher(selector) .map { ($0[1] as! UICollectionReusableView, $0[2] as! String, $0[3] as! IndexPath) } .eraseToAnyPublisher() } + + @discardableResult + func setCellSize(_ callback: @escaping (_ indexPath: IndexPath) -> CGSize) -> Self { + innerDelegateWrap?.proxy?.cellSizeCallback = callback + return self + } + + @discardableResult + func setInset(_ callback: @escaping (_ section: Int) -> UIEdgeInsets) -> Self { + innerDelegateWrap?.proxy?.insetCallback = callback + return self + } + + @discardableResult + func setMinimumLineSpacing(_ callback: @escaping (_ section: Int) -> CGFloat) -> Self { + innerDelegateWrap?.proxy?.minimumLineSpacingCallback = callback + return self + } + + @discardableResult + func setMinimumInteritemSpacing(_ callback: @escaping (_ section: Int) -> CGFloat) -> Self { + innerDelegateWrap?.proxy?.minimumInteritemSpacingCallback = callback + return self + } + + @discardableResult + func setHeaderSize(_ callback: @escaping (_ section: Int) -> CGSize) -> Self { + innerDelegateWrap?.proxy?.headerSizeCallback = callback + return self + } + + @discardableResult + func setFooterSize(_ callback: @escaping (_ section: Int) -> CGSize) -> Self { + innerDelegateWrap?.proxy?.footerSizeCallback = callback + return self + } - override var delegateProxy: DelegateProxy { - CollectionViewDelegateProxy.createDelegateProxy(for: self) + func useInnerDelegate() { + innerDelegateWrap = .init() + innerDelegateWrap?.proxy = CollectionViewDelegateProxy.createDelegateProxy(for: self) + innerDelegateWrap?.proxy?.wrap = innerDelegateWrap + innerDelegateWrap?.collectionView = self + } + + func removeInnerDelegate() { + innerDelegateWrap = nil } + + fileprivate var innerFlowLayout: UICollectionViewFlowLayout? { + collectionViewLayout as? UICollectionViewFlowLayout + } + + static var innerDelegateWrapKey: Void? + private var innerDelegateWrap: CollectionViewDelegateWrap? { + get { objc_getAssociatedObject(self, &UICollectionView.innerDelegateWrapKey) as? CollectionViewDelegateWrap } + set { objc_setAssociatedObject(self, &UICollectionView.innerDelegateWrapKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } + } + + override public var delegateProxy: DelegateProxy? { + innerDelegateWrap?.proxy + } + } @available(iOS 13.0, *) -private class CollectionViewDelegateProxy: DelegateProxy, UICollectionViewDelegate, DelegateProxyType { +fileprivate class CollectionViewDelegateWrap { + + weak var proxy: CollectionViewDelegateProxy? + weak var collectionView: UICollectionView? + +} + +@available(iOS 13.0, *) +private class CollectionViewDelegateProxy: DelegateProxy, UICollectionViewDelegateFlowLayout, DelegateProxyType { + weak var wrap: CollectionViewDelegateWrap? + func setDelegate(to object: UICollectionView) { object.delegate = self } + + var cellSizeCallback: ((_ indexPath: IndexPath) -> CGSize)? + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { + cellSizeCallback?(indexPath) ?? wrap?.collectionView?.innerFlowLayout?.itemSize ?? CGSize(width: 0.1, height: 0.1) + } + + var insetCallback: ((_ section: Int) -> UIEdgeInsets)? + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets { + let result = insetCallback?(section) ?? wrap?.collectionView?.innerFlowLayout?.sectionInset ?? .zero + return result + } + + var minimumLineSpacingCallback: ((_ section: Int) -> CGFloat)? + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat { + let result = minimumLineSpacingCallback?(section) ?? wrap?.collectionView?.innerFlowLayout?.minimumLineSpacing ?? 0 + return max(result - 0.5, 0) + } + + var minimumInteritemSpacingCallback: ((_ section: Int) -> CGFloat)? + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat { + let result = minimumInteritemSpacingCallback?(section) ?? wrap?.collectionView?.innerFlowLayout?.minimumInteritemSpacing ?? 0 + return max(result - 0.5, 0) + } + + var headerSizeCallback: ((_ section: Int) -> CGSize)? + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize { + headerSizeCallback?(section) ?? wrap?.collectionView?.innerFlowLayout?.headerReferenceSize ?? .zero + } + + var footerSizeCallback: ((_ section: Int) -> CGSize)? + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForFooterInSection section: Int) -> CGSize { + footerSizeCallback?(section) ?? wrap?.collectionView?.innerFlowLayout?.footerReferenceSize ?? .zero + } } #endif // swiftlint:enable force_cast diff --git a/Sources/CombineCocoa/Controls/UIScrollView+Combine.swift b/Sources/CombineCocoa/Controls/UIScrollView+Combine.swift index 0c922a7..f070d0d 100644 --- a/Sources/CombineCocoa/Controls/UIScrollView+Combine.swift +++ b/Sources/CombineCocoa/Controls/UIScrollView+Combine.swift @@ -5,7 +5,6 @@ // Created by Joan Disho on 09/08/2019. // Copyright © 2020 Combine Community. All rights reserved. // - #if !(os(iOS) && (arch(i386) || arch(arm))) import UIKit import Combine @@ -41,6 +40,11 @@ public extension UIScrollView { /// Combine wrapper for `scrollViewDidScroll(_:)` var didScrollPublisher: AnyPublisher { + guard let delegateProxy = delegateProxy else { + return Empty(completeImmediately: false) + .eraseToAnyPublisher() + } + let selector = #selector(UIScrollViewDelegate.scrollViewDidScroll(_:)) return delegateProxy.interceptSelectorPublisher(selector) .map { _ in () } @@ -49,6 +53,11 @@ public extension UIScrollView { /// Combine wrapper for `scrollViewWillBeginDecelerating(_:)` var willBeginDeceleratingPublisher: AnyPublisher { + guard let delegateProxy = delegateProxy else { + return Empty(completeImmediately: false) + .eraseToAnyPublisher() + } + let selector = #selector(UIScrollViewDelegate.scrollViewWillBeginDecelerating(_:)) return delegateProxy.interceptSelectorPublisher(selector) .map { _ in () } @@ -57,6 +66,11 @@ public extension UIScrollView { /// Combine wrapper for `scrollViewDidEndDecelerating(_:)` var didEndDeceleratingPublisher: AnyPublisher { + guard let delegateProxy = delegateProxy else { + return Empty(completeImmediately: false) + .eraseToAnyPublisher() + } + let selector = #selector(UIScrollViewDelegate.scrollViewDidEndDecelerating(_:)) return delegateProxy.interceptSelectorPublisher(selector) .map { _ in () } @@ -65,6 +79,11 @@ public extension UIScrollView { /// Combine wrapper for `scrollViewWillBeginDragging(_:)` var willBeginDraggingPublisher: AnyPublisher { + guard let delegateProxy = delegateProxy else { + return Empty(completeImmediately: false) + .eraseToAnyPublisher() + } + let selector = #selector(UIScrollViewDelegate.scrollViewWillBeginDragging(_:)) return delegateProxy.interceptSelectorPublisher(selector) .map { _ in () } @@ -73,6 +92,11 @@ public extension UIScrollView { /// Combine wrapper for `scrollViewWillEndDragging(_:withVelocity:targetContentOffset:)` var willEndDraggingPublisher: AnyPublisher<(velocity: CGPoint, targetContentOffset: UnsafeMutablePointer), Never> { + guard let delegateProxy = delegateProxy else { + return Empty<(velocity: CGPoint, targetContentOffset: UnsafeMutablePointer), Never>(completeImmediately: false) + .eraseToAnyPublisher() + } + let selector = #selector(UIScrollViewDelegate.scrollViewWillEndDragging(_:withVelocity:targetContentOffset:)) return delegateProxy.interceptSelectorPublisher(selector) .map { values in @@ -86,6 +110,11 @@ public extension UIScrollView { /// Combine wrapper for `scrollViewDidEndDragging(_:willDecelerate:)` var didEndDraggingPublisher: AnyPublisher { + guard let delegateProxy = delegateProxy else { + return Empty(completeImmediately: false) + .eraseToAnyPublisher() + } + let selector = #selector(UIScrollViewDelegate.scrollViewDidEndDragging(_:willDecelerate:)) return delegateProxy.interceptSelectorPublisher(selector) .map { $0[1] as! Bool } @@ -94,6 +123,11 @@ public extension UIScrollView { /// Combine wrapper for `scrollViewDidZoom(_:)` var didZoomPublisher: AnyPublisher { + guard let delegateProxy = delegateProxy else { + return Empty(completeImmediately: false) + .eraseToAnyPublisher() + } + let selector = #selector(UIScrollViewDelegate.scrollViewDidZoom(_:)) return delegateProxy.interceptSelectorPublisher(selector) .map { _ in () } @@ -102,6 +136,11 @@ public extension UIScrollView { /// Combine wrapper for `scrollViewDidScrollToTop(_:)` var didScrollToTopPublisher: AnyPublisher { + guard let delegateProxy = delegateProxy else { + return Empty(completeImmediately: false) + .eraseToAnyPublisher() + } + let selector = #selector(UIScrollViewDelegate.scrollViewDidScrollToTop(_:)) return delegateProxy.interceptSelectorPublisher(selector) .map { _ in () } @@ -110,6 +149,11 @@ public extension UIScrollView { /// Combine wrapper for `scrollViewDidEndScrollingAnimation(_:)` var didEndScrollingAnimationPublisher: AnyPublisher { + guard let delegateProxy = delegateProxy else { + return Empty(completeImmediately: false) + .eraseToAnyPublisher() + } + let selector = #selector(UIScrollViewDelegate.scrollViewDidEndScrollingAnimation(_:)) return delegateProxy.interceptSelectorPublisher(selector) .map { _ in () } @@ -118,6 +162,11 @@ public extension UIScrollView { /// Combine wrapper for `scrollViewWillBeginZooming(_:with:)` var willBeginZoomingPublisher: AnyPublisher { + guard let delegateProxy = delegateProxy else { + return Empty(completeImmediately: false) + .eraseToAnyPublisher() + } + let selector = #selector(UIScrollViewDelegate.scrollViewWillBeginZooming(_:with:)) return delegateProxy.interceptSelectorPublisher(selector) .map { $0[1] as! UIView? } @@ -126,13 +175,18 @@ public extension UIScrollView { /// Combine wrapper for `scrollViewDidEndZooming(_:with:atScale:)` var didEndZooming: AnyPublisher<(view: UIView?, scale: CGFloat), Never> { + guard let delegateProxy = delegateProxy else { + return Empty<(view: UIView?, scale: CGFloat), Never>(completeImmediately: false) + .eraseToAnyPublisher() + } + let selector = #selector(UIScrollViewDelegate.scrollViewDidEndZooming(_:with:atScale:)) return delegateProxy.interceptSelectorPublisher(selector) .map { ($0[1] as! UIView?, $0[2] as! CGFloat) } .eraseToAnyPublisher() } - @objc var delegateProxy: DelegateProxy { + @objc var delegateProxy: DelegateProxy? { ScrollViewDelegateProxy.createDelegateProxy(for: self) } } diff --git a/Sources/CombineCocoa/Controls/UITableView+Combine.swift b/Sources/CombineCocoa/Controls/UITableView+Combine.swift index 829fea0..56a40f8 100644 --- a/Sources/CombineCocoa/Controls/UITableView+Combine.swift +++ b/Sources/CombineCocoa/Controls/UITableView+Combine.swift @@ -16,6 +16,11 @@ import Combine public extension UITableView { /// Combine wrapper for `tableView(_:willDisplay:forRowAt:)` var willDisplayCellPublisher: AnyPublisher<(cell: UITableViewCell, indexPath: IndexPath), Never> { + guard let delegateProxy = delegateProxy else { + return Empty<(cell: UITableViewCell, indexPath: IndexPath), Never>(completeImmediately: false) + .eraseToAnyPublisher() + } + let selector = #selector(UITableViewDelegate.tableView(_:willDisplay:forRowAt:)) return delegateProxy.interceptSelectorPublisher(selector) .map { ($0[1] as! UITableViewCell, $0[2] as! IndexPath) } @@ -24,6 +29,11 @@ public extension UITableView { /// Combine wrapper for `tableView(_:willDisplayHeaderView:forSection:)` var willDisplayHeaderViewPublisher: AnyPublisher<(headerView: UIView, section: Int), Never> { + guard let delegateProxy = delegateProxy else { + return Empty<(headerView: UIView, section: Int), Never>(completeImmediately: false) + .eraseToAnyPublisher() + } + let selector = #selector(UITableViewDelegate.tableView(_:willDisplayHeaderView:forSection:)) return delegateProxy.interceptSelectorPublisher(selector) .map { ($0[1] as! UIView, $0[2] as! Int) } @@ -32,6 +42,11 @@ public extension UITableView { /// Combine wrapper for `tableView(_:willDisplayFooterView:forSection:)` var willDisplayFooterViewPublisher: AnyPublisher<(footerView: UIView, section: Int), Never> { + guard let delegateProxy = delegateProxy else { + return Empty<(footerView: UIView, section: Int), Never>(completeImmediately: false) + .eraseToAnyPublisher() + } + let selector = #selector(UITableViewDelegate.tableView(_:willDisplayFooterView:forSection:)) return delegateProxy.interceptSelectorPublisher(selector) .map { ($0[1] as! UIView, $0[2] as! Int) } @@ -40,6 +55,11 @@ public extension UITableView { /// Combine wrapper for `tableView(_:didEndDisplaying:forRowAt:)` var didEndDisplayingCellPublisher: AnyPublisher<(cell: UITableViewCell, indexPath: IndexPath), Never> { + guard let delegateProxy = delegateProxy else { + return Empty<(cell: UITableViewCell, indexPath: IndexPath), Never>(completeImmediately: false) + .eraseToAnyPublisher() + } + let selector = #selector(UITableViewDelegate.tableView(_:didEndDisplaying:forRowAt:)) return delegateProxy.interceptSelectorPublisher(selector) .map { ($0[1] as! UITableViewCell, $0[2] as! IndexPath) } @@ -48,6 +68,11 @@ public extension UITableView { /// Combine wrapper for `tableView(_:didEndDisplayingHeaderView:forSection:)` var didEndDisplayingHeaderViewPublisher: AnyPublisher<(headerView: UIView, section: Int), Never> { + guard let delegateProxy = delegateProxy else { + return Empty<(headerView: UIView, section: Int), Never>(completeImmediately: false) + .eraseToAnyPublisher() + } + let selector = #selector(UITableViewDelegate.tableView(_:didEndDisplayingHeaderView:forSection:)) return delegateProxy.interceptSelectorPublisher(selector) .map { ($0[1] as! UIView, $0[2] as! Int) } @@ -56,6 +81,11 @@ public extension UITableView { /// Combine wrapper for `tableView(_:didEndDisplayingFooterView:forSection:)` var didEndDisplayingFooterView: AnyPublisher<(headerView: UIView, section: Int), Never> { + guard let delegateProxy = delegateProxy else { + return Empty<(headerView: UIView, section: Int), Never>(completeImmediately: false) + .eraseToAnyPublisher() + } + let selector = #selector(UITableViewDelegate.tableView(_:didEndDisplayingFooterView:forSection:)) return delegateProxy.interceptSelectorPublisher(selector) .map { ($0[1] as! UIView, $0[2] as! Int) } @@ -64,6 +94,11 @@ public extension UITableView { /// Combine wrapper for `tableView(_:accessoryButtonTappedForRowWith:)` var itemAccessoryButtonTappedPublisher: AnyPublisher { + guard let delegateProxy = delegateProxy else { + return Empty(completeImmediately: false) + .eraseToAnyPublisher() + } + let selector = #selector(UITableViewDelegate.tableView(_:accessoryButtonTappedForRowWith:)) return delegateProxy.interceptSelectorPublisher(selector) .map { $0[1] as! IndexPath } @@ -72,6 +107,11 @@ public extension UITableView { /// Combine wrapper for `tableView(_:didHighlightRowAt:)` var didHighlightRowPublisher: AnyPublisher { + guard let delegateProxy = delegateProxy else { + return Empty(completeImmediately: false) + .eraseToAnyPublisher() + } + let selector = #selector(UITableViewDelegate.tableView(_:didHighlightRowAt:)) return delegateProxy.interceptSelectorPublisher(selector) .map { $0[1] as! IndexPath } @@ -80,6 +120,11 @@ public extension UITableView { /// Combine wrapper for `tableView(_:didUnHighlightRowAt:)` var didUnhighlightRowPublisher: AnyPublisher { + guard let delegateProxy = delegateProxy else { + return Empty(completeImmediately: false) + .eraseToAnyPublisher() + } + let selector = #selector(UITableViewDelegate.tableView(_:didUnhighlightRowAt:)) return delegateProxy.interceptSelectorPublisher(selector) .map { $0[1] as! IndexPath } @@ -88,6 +133,11 @@ public extension UITableView { /// Combine wrapper for `tableView(_:didSelectRowAt:)` var didSelectRowPublisher: AnyPublisher { + guard let delegateProxy = delegateProxy else { + return Empty(completeImmediately: false) + .eraseToAnyPublisher() + } + let selector = #selector(UITableViewDelegate.tableView(_:didSelectRowAt:)) return delegateProxy.interceptSelectorPublisher(selector) .map { $0[1] as! IndexPath } @@ -96,6 +146,11 @@ public extension UITableView { /// Combine wrapper for `tableView(_:didDeselectRowAt:)` var didDeselectRowPublisher: AnyPublisher { + guard let delegateProxy = delegateProxy else { + return Empty(completeImmediately: false) + .eraseToAnyPublisher() + } + let selector = #selector(UITableViewDelegate.tableView(_:didDeselectRowAt:)) return delegateProxy.interceptSelectorPublisher(selector) .map { $0[1] as! IndexPath } @@ -104,6 +159,11 @@ public extension UITableView { /// Combine wrapper for `tableView(_:willBeginEditingRowAt:)` var willBeginEditingRowPublisher: AnyPublisher { + guard let delegateProxy = delegateProxy else { + return Empty(completeImmediately: false) + .eraseToAnyPublisher() + } + let selector = #selector(UITableViewDelegate.tableView(_:willBeginEditingRowAt:)) return delegateProxy.interceptSelectorPublisher(selector) .map { $0[1] as! IndexPath } @@ -112,13 +172,18 @@ public extension UITableView { /// Combine wrapper for `tableView(_:didEndEditingRowAt:)` var didEndEditingRowPublisher: AnyPublisher { + guard let delegateProxy = delegateProxy else { + return Empty(completeImmediately: false) + .eraseToAnyPublisher() + } + let selector = #selector(UITableViewDelegate.tableView(_:didEndEditingRowAt:)) return delegateProxy.interceptSelectorPublisher(selector) .map { $0[1] as! IndexPath } .eraseToAnyPublisher() } - override var delegateProxy: DelegateProxy { + override public var delegateProxy: DelegateProxy? { TableViewDelegateProxy.createDelegateProxy(for: self) } }