Add prefetchDataSource support for UITableView

This commit is contained in:
Rowan Livingstone 2018-02-16 09:45:02 +13:00
parent bc3bb79df4
commit e51fbf901f
4 changed files with 176 additions and 1 deletions

View File

@ -136,6 +136,10 @@
B5624790203515DE00D3EE75 /* RxCollectionViewDataSourcePrefetchingProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = B562478D2035154900D3EE75 /* RxCollectionViewDataSourcePrefetchingProxy.swift */; };
B5624791203515DF00D3EE75 /* RxCollectionViewDataSourcePrefetchingProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = B562478D2035154900D3EE75 /* RxCollectionViewDataSourcePrefetchingProxy.swift */; };
B5624792203515DF00D3EE75 /* RxCollectionViewDataSourcePrefetchingProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = B562478D2035154900D3EE75 /* RxCollectionViewDataSourcePrefetchingProxy.swift */; };
B5624794203532F500D3EE75 /* RxTableViewDataSourcePrefetchingProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5624793203532F500D3EE75 /* RxTableViewDataSourcePrefetchingProxy.swift */; };
B5624795203532F500D3EE75 /* RxTableViewDataSourcePrefetchingProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5624793203532F500D3EE75 /* RxTableViewDataSourcePrefetchingProxy.swift */; };
B5624796203532F500D3EE75 /* RxTableViewDataSourcePrefetchingProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5624793203532F500D3EE75 /* RxTableViewDataSourcePrefetchingProxy.swift */; };
B5624797203532F500D3EE75 /* RxTableViewDataSourcePrefetchingProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5624793203532F500D3EE75 /* RxTableViewDataSourcePrefetchingProxy.swift */; };
C801DE361F6EAD3C008DB060 /* SingleTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C801DE351F6EAD3C008DB060 /* SingleTest.swift */; };
C801DE371F6EAD3C008DB060 /* SingleTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C801DE351F6EAD3C008DB060 /* SingleTest.swift */; };
C801DE381F6EAD3C008DB060 /* SingleTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C801DE351F6EAD3C008DB060 /* SingleTest.swift */; };
@ -1802,6 +1806,7 @@
A5CD03891F1660F40005A376 /* RxPickerViewAdapter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxPickerViewAdapter.swift; sourceTree = "<group>"; };
AAE623751C82475700FC7801 /* UIProgressView+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIProgressView+Rx.swift"; sourceTree = "<group>"; };
B562478D2035154900D3EE75 /* RxCollectionViewDataSourcePrefetchingProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RxCollectionViewDataSourcePrefetchingProxy.swift; sourceTree = "<group>"; };
B5624793203532F500D3EE75 /* RxTableViewDataSourcePrefetchingProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RxTableViewDataSourcePrefetchingProxy.swift; sourceTree = "<group>"; };
C801DE351F6EAD3C008DB060 /* SingleTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SingleTest.swift; sourceTree = "<group>"; };
C801DE391F6EAD48008DB060 /* MaybeTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MaybeTest.swift; sourceTree = "<group>"; };
C801DE3D1F6EAD57008DB060 /* CompletableTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompletableTest.swift; sourceTree = "<group>"; };
@ -3095,12 +3100,14 @@
C88253F91B8A752B00B02D69 /* Proxies */ = {
isa = PBXGroup;
children = (
B562478D2035154900D3EE75 /* RxCollectionViewDataSourcePrefetchingProxy.swift */,
C88253FC1B8A752B00B02D69 /* RxCollectionViewDataSourceProxy.swift */,
C88253FD1B8A752B00B02D69 /* RxCollectionViewDelegateProxy.swift */,
C88253FE1B8A752B00B02D69 /* RxScrollViewDelegateProxy.swift */,
C88253FF1B8A752B00B02D69 /* RxSearchBarDelegateProxy.swift */,
ECBBA59D1DF8C0D400DDDC2E /* RxTabBarControllerDelegateProxy.swift */,
88D98F2D1CE7549A00D50457 /* RxTabBarDelegateProxy.swift */,
B5624793203532F500D3EE75 /* RxTableViewDataSourcePrefetchingProxy.swift */,
C88254001B8A752B00B02D69 /* RxTableViewDataSourceProxy.swift */,
C88254011B8A752B00B02D69 /* RxTableViewDelegateProxy.swift */,
C88254021B8A752B00B02D69 /* RxTextViewDelegateProxy.swift */,
@ -3110,7 +3117,6 @@
D9080ACD1EA05A16002B433B /* RxNavigationControllerDelegateProxy.swift */,
4613457B1D9A4AEE001ABAF2 /* RxWebViewDelegateProxy.swift */,
A520FFFB1F0D291500573734 /* RxPickerViewDataSourceProxy.swift */,
B562478D2035154900D3EE75 /* RxCollectionViewDataSourcePrefetchingProxy.swift */,
);
path = Proxies;
sourceTree = "<group>";
@ -4228,6 +4234,7 @@
C88254181B8A752B00B02D69 /* ItemEvents.swift in Sources */,
C89AB1731DAAC1680065FBE6 /* ControlTarget.swift in Sources */,
C882541B1B8A752B00B02D69 /* RxTableViewDataSourceType.swift in Sources */,
B5624794203532F500D3EE75 /* RxTableViewDataSourcePrefetchingProxy.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -4252,6 +4259,7 @@
C88F76821CE5341700D5A014 /* TextInput.swift in Sources */,
C8C8BCD01F8944B800501D4D /* BehaviorRelay.swift in Sources */,
C8091C581FAA39C1001DB32A /* ControlEvent+Signal.swift in Sources */,
B5624795203532F500D3EE75 /* RxTableViewDataSourcePrefetchingProxy.swift in Sources */,
C89AB1DF1DAAC3350065FBE6 /* ObservableConvertibleType+Driver.swift in Sources */,
6B9CA56C202A1F44002C2D11 /* KeyPathBinder.swift in Sources */,
C89AB1D71DAAC3350065FBE6 /* Driver+Subscription.swift in Sources */,
@ -5458,6 +5466,7 @@
C89AB2111DAAC3350065FBE6 /* Logging.swift in Sources */,
C89AB1761DAAC1680065FBE6 /* ControlTarget.swift in Sources */,
D9080AD61EA05DEC002B433B /* UINavigationController+Rx.swift in Sources */,
B5624797203532F500D3EE75 /* RxTableViewDataSourcePrefetchingProxy.swift in Sources */,
C8E65EFE1F6E91D1004478C3 /* Binder.swift in Sources */,
C89AB21D1DAAC3350065FBE6 /* NSObject+Rx+RawRepresentable.swift in Sources */,
6B9CA56A202A1F44002C2D11 /* KeyPathBinder.swift in Sources */,
@ -5553,6 +5562,7 @@
C89AB20C1DAAC3350065FBE6 /* KVORepresentable.swift in Sources */,
54D213921CE08D0C0028D5B4 /* UINavigationItem+Rx.swift in Sources */,
C89AB1F41DAAC3350065FBE6 /* SharedSequence+Operators.swift in Sources */,
B5624796203532F500D3EE75 /* RxTableViewDataSourcePrefetchingProxy.swift in Sources */,
C817729A1E7F408100EA679B /* Deprecated.swift in Sources */,
C8BCD3EF1C14B5FB005F1280 /* UIView+Rx.swift in Sources */,
46307D4F1CDE77D800E47A1C /* UIAlertAction+Rx.swift in Sources */,

View File

@ -0,0 +1,88 @@
//
// RxTableViewDataSourcePrefetchingProxy.swift
// RxCocoa
//
// Created by Rowan Livingstone on 2/15/18.
// Copyright © 2018 Krunoslav Zaher. All rights reserved.
//
#if os(iOS) || os(tvOS)
import UIKit
import RxSwift
@available(iOS 10.0, tvOS 10.0, *)
extension UITableView: HasPrefetchDataSource {
public typealias PrefetchDataSource = UITableViewDataSourcePrefetching
}
@available(iOS 10.0, tvOS 10.0, *)
let tableViewPrefetchDataSourceNotSet = TableViewPrefetchDataSourceNotSet()
@available(iOS 10.0, tvOS 10.0, *)
final class TableViewPrefetchDataSourceNotSet : NSObject, UITableViewDataSourcePrefetching {
func tableView(_ tableView: UITableView, prefetchRowsAt indexPaths: [IndexPath]) {}
}
@available(iOS 10.0, tvOS 10.0, *)
open class RxTableViewDataSourcePrefetchingProxy: DelegateProxy<UITableView, UITableViewDataSourcePrefetching>, DelegateProxyType, UITableViewDataSourcePrefetching {
/// Typed parent object.
public weak private(set) var tableView: UITableView?
/// - parameter tableView: Parent object for delegate proxy.
public init(tableView: ParentObject) {
self.tableView = tableView
super.init(parentObject: tableView, delegateProxy: RxTableViewDataSourcePrefetchingProxy.self)
}
// Register known implementations
public static func registerKnownImplementations() {
self.register { RxTableViewDataSourcePrefetchingProxy(tableView: $0) }
}
fileprivate var _prefetchItemsPublishSubject: PublishSubject<[IndexPath]>?
/// Optimized version used for observing prefetch items callbacks.
internal var prefetchItemsPublishSubject: PublishSubject<[IndexPath]> {
if let subject = _prefetchItemsPublishSubject {
return subject
}
let subject = PublishSubject<[IndexPath]>()
_prefetchItemsPublishSubject = subject
return subject
}
private weak var _requiredMethodsPrefetchDataSource: UITableViewDataSourcePrefetching? = tableViewPrefetchDataSourceNotSet
// MARK: delegate
/// Required delegate method implementation.
public func tableView(_ tableView: UITableView, prefetchRowsAt indexPaths: [IndexPath]) {
if let subject = _prefetchItemsPublishSubject {
subject.on(.next(indexPaths))
}
(_requiredMethodsPrefetchDataSource ?? tableViewPrefetchDataSourceNotSet).tableView(tableView, prefetchRowsAt: indexPaths)
}
/// For more information take a look at `DelegateProxyType`.
open override func setForwardToDelegate(_ forwardToDelegate: UITableViewDataSourcePrefetching?, retainDelegate: Bool) {
_requiredMethodsPrefetchDataSource = forwardToDelegate ?? tableViewPrefetchDataSourceNotSet
super.setForwardToDelegate(forwardToDelegate, retainDelegate: retainDelegate)
}
deinit {
if let subject = _prefetchItemsPublishSubject {
subject.on(.completed)
}
}
}
#endif

View File

@ -372,6 +372,47 @@ extension Reactive where Base: UITableView {
}
}
@available(iOS 10.0, tvOS 10.0, *)
extension Reactive where Base: UITableView {
/// Reactive wrapper for `prefetchDataSource`.
///
/// For more information take a look at `DelegateProxyType` protocol documentation.
public var prefetchDataSource: DelegateProxy<UITableView, UITableViewDataSourcePrefetching> {
return RxTableViewDataSourcePrefetchingProxy.proxy(for: base)
}
/**
Installs prefetch data source as forwarding delegate on `rx.prefetchDataSource`.
Prefetch data source won't be retained.
It enables using normal delegate mechanism with reactive delegate mechanism.
- parameter prefetchDataSource: Prefetch data source object.
- returns: Disposable object that can be used to unbind the data source.
*/
public func setPrefetchDataSource(_ prefetchDataSource: UITableViewDataSourcePrefetching)
-> Disposable {
return RxTableViewDataSourcePrefetchingProxy.installForwardDelegate(prefetchDataSource, retainDelegate: false, onProxyForObject: self.base)
}
/// Reactive wrapper for `prefetchDataSource` message `tableView(_:prefetchRowsAt:)`.
public var prefetchItems: ControlEvent<[IndexPath]> {
let source = RxTableViewDataSourcePrefetchingProxy.proxy(for: base).prefetchItemsPublishSubject
return ControlEvent(events: source)
}
/// Reactive wrapper for `prefetchDataSource` message `tableView(_:cancelPrefetchingForRowsAt:)`.
public var cancelPrefetchingForItems: ControlEvent<[IndexPath]> {
let source = prefetchDataSource.methodInvoked(#selector(UITableViewDataSourcePrefetching.tableView(_:cancelPrefetchingForRowsAt:)))
.map { a in
return try castOrThrow(Array<IndexPath>.self, a[1])
}
return ControlEvent(events: source)
}
}
#endif
#if os(tvOS)

View File

@ -218,6 +218,42 @@ final class UITableViewTests : RxTest {
dataSourceSubscription.dispose()
}
@available(iOS 10.0, tvOS 10.0, *)
func test_prefetchItems() {
let tableView = UITableView(frame: CGRect(x: 0, y: 0, width: 1, height: 1))
var indexPaths: [IndexPath] = []
let subscription = tableView.rx.prefetchItems
.subscribe(onNext: {
indexPaths = $0
})
let testIndexPaths = [IndexPath(item: 1, section: 0), IndexPath(item: 2, section: 0)]
tableView.prefetchDataSource!.tableView(tableView, prefetchRowsAt: testIndexPaths)
XCTAssertEqual(indexPaths, testIndexPaths)
subscription.dispose()
}
@available(iOS 10.0, tvOS 10.0, *)
func test_cancelPrefetchingForItems() {
let tableView = UITableView(frame: CGRect(x: 0, y: 0, width: 1, height: 1))
var indexPaths: [IndexPath] = []
let subscription = tableView.rx.cancelPrefetchingForItems
.subscribe(onNext: {
indexPaths = $0
})
let testIndexPaths = [IndexPath(item: 1, section: 0), IndexPath(item: 2, section: 0)]
tableView.prefetchDataSource!.tableView!(tableView, cancelPrefetchingForRowsAt: testIndexPaths)
XCTAssertEqual(indexPaths, testIndexPaths)
subscription.dispose()
}
func test_delegateEventCompletesOnDealloc1() {
let items: Observable<[Int]> = Observable.just([1, 2, 3])