Skip to content

Commit 2fbab64

Browse files
committed
v2 final updates
1 parent 08fe4ff commit 2fbab64

File tree

8 files changed

+200
-222
lines changed

8 files changed

+200
-222
lines changed

ReactiveUIKit.podspec

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
Pod::Spec.new do |s|
22
s.name = "ReactiveUIKit"
3-
s.version = "2.0.0-beta5"
3+
s.version = "2.0.0"
44
s.summary = "Reactive extensions for UIKit framework."
55
s.homepage = "https://github.com/ReactiveKit/ReactiveUIKit"
66
s.license = 'MIT'
77
s.author = { "Srdan Rasic" => "[email protected]" }
8-
s.source = { :git => "https://github.com/ReactiveKit/ReactiveUIKit.git", :tag => "v2.0.0-beta5" }
8+
s.source = { :git => "https://github.com/ReactiveKit/ReactiveUIKit.git", :tag => "v2.0.0" }
99

1010
s.ios.deployment_target = '8.0'
1111
s.tvos.deployment_target = '9.0'

ReactiveUIKit.xcodeproj/project.pbxproj

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77
objects = {
88

99
/* Begin PBXBuildFile section */
10+
165F031D1CEFA7A900FCE1C9 /* Protocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = 165F031C1CEFA7A900FCE1C9 /* Protocols.swift */; };
11+
165F031E1CEFA7A900FCE1C9 /* Protocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = 165F031C1CEFA7A900FCE1C9 /* Protocols.swift */; };
12+
165F03201CEFA7D600FCE1C9 /* UIApplication.swift in Sources */ = {isa = PBXBuildFile; fileRef = 165F031F1CEFA7D600FCE1C9 /* UIApplication.swift */; };
13+
165F03211CEFA7D600FCE1C9 /* UIApplication.swift in Sources */ = {isa = PBXBuildFile; fileRef = 165F031F1CEFA7D600FCE1C9 /* UIApplication.swift */; };
1014
EC5C668F1CB3D64B00C5F3C3 /* UITableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC8A9A2B1CABED670042A6AD /* UITableView.swift */; };
1115
EC5C66901CB3D64C00C5F3C3 /* UITableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC8A9A2B1CABED670042A6AD /* UITableView.swift */; };
1216
EC83BAF51CB00A5B007D7E47 /* UIBarButtonItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC83BAF41CB00A5B007D7E47 /* UIBarButtonItem.swift */; };
@@ -62,6 +66,8 @@
6266
/* End PBXContainerItemProxy section */
6367

6468
/* Begin PBXFileReference section */
69+
165F031C1CEFA7A900FCE1C9 /* Protocols.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Protocols.swift; path = Sources/Protocols.swift; sourceTree = SOURCE_ROOT; };
70+
165F031F1CEFA7D600FCE1C9 /* UIApplication.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = UIApplication.swift; path = Sources/UIApplication.swift; sourceTree = SOURCE_ROOT; };
6571
16E125A11BF5056C00543EFB /* ReactiveUIKit.podspec */ = {isa = PBXFileReference; lastKnownFileType = text; path = ReactiveUIKit.podspec; sourceTree = "<group>"; };
6672
16E125A21BF5056C00543EFB /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
6773
16F69F6A1CB462D400FD2B5F /* Package.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Package.swift; sourceTree = "<group>"; };
@@ -146,11 +152,11 @@
146152
children = (
147153
ECB7A5441BEB71E10034053A /* ReactiveUIKit.h */,
148154
ECB7A5461BEB71E10034053A /* Info.plist */,
155+
165F031F1CEFA7D600FCE1C9 /* UIApplication.swift */,
149156
EC8A9A1D1CABED670042A6AD /* UIActivityIndicatorView.swift */,
150157
EC8A9A1E1CABED670042A6AD /* UIBarItem.swift */,
151158
EC83BAF41CB00A5B007D7E47 /* UIBarButtonItem.swift */,
152159
EC8A9A1F1CABED670042A6AD /* UIButton.swift */,
153-
EC8A9A201CABED670042A6AD /* UICollectionView.swift */,
154160
EC8A9A211CABED670042A6AD /* UIControl.swift */,
155161
EC8A9A221CABED670042A6AD /* UIDatePicker.swift */,
156162
EC8A9A231CABED670042A6AD /* UIImageView.swift */,
@@ -162,9 +168,11 @@
162168
EC8A9A291CABED670042A6AD /* UISlider.swift */,
163169
EC8A9A2A1CABED670042A6AD /* UISwitch.swift */,
164170
EC8A9A2B1CABED670042A6AD /* UITableView.swift */,
171+
EC8A9A201CABED670042A6AD /* UICollectionView.swift */,
165172
EC8A9A2C1CABED670042A6AD /* UITextField.swift */,
166173
EC8A9A2D1CABED670042A6AD /* UITextView.swift */,
167174
EC8A9A2E1CABED670042A6AD /* UIView.swift */,
175+
165F031C1CEFA7A900FCE1C9 /* Protocols.swift */,
168176
);
169177
path = ReactiveUIKit;
170178
sourceTree = "<group>";
@@ -332,12 +340,14 @@
332340
EC8A9A3C1CABED670042A6AD /* UIImageView.swift in Sources */,
333341
EC8A9A401CABED670042A6AD /* UINavigationItem.swift in Sources */,
334342
EC8A9A301CABED670042A6AD /* UIActivityIndicatorView.swift in Sources */,
343+
165F031E1CEFA7A900FCE1C9 /* Protocols.swift in Sources */,
335344
EC8A9A441CABED670042A6AD /* UIRefreshControl.swift in Sources */,
336345
EC8A9A3A1CABED670042A6AD /* UIDatePicker.swift in Sources */,
337346
EC5C66901CB3D64C00C5F3C3 /* UITableView.swift in Sources */,
338347
EC83BAF61CB00A5B007D7E47 /* UIBarButtonItem.swift in Sources */,
339348
EC8A9A421CABED670042A6AD /* UIProgressView.swift in Sources */,
340349
EC8A9A3E1CABED670042A6AD /* UILabel.swift in Sources */,
350+
165F03211CEFA7D600FCE1C9 /* UIApplication.swift in Sources */,
341351
EC8A9A341CABED670042A6AD /* UIButton.swift in Sources */,
342352
EC8A9A481CABED670042A6AD /* UISlider.swift in Sources */,
343353
EC8A9A521CABED670042A6AD /* UIView.swift in Sources */,
@@ -358,12 +368,14 @@
358368
EC8A9A3B1CABED670042A6AD /* UIImageView.swift in Sources */,
359369
EC8A9A3F1CABED670042A6AD /* UINavigationItem.swift in Sources */,
360370
EC8A9A2F1CABED670042A6AD /* UIActivityIndicatorView.swift in Sources */,
371+
165F031D1CEFA7A900FCE1C9 /* Protocols.swift in Sources */,
361372
EC8A9A431CABED670042A6AD /* UIRefreshControl.swift in Sources */,
362373
EC8A9A391CABED670042A6AD /* UIDatePicker.swift in Sources */,
363374
EC5C668F1CB3D64B00C5F3C3 /* UITableView.swift in Sources */,
364375
EC83BAF51CB00A5B007D7E47 /* UIBarButtonItem.swift in Sources */,
365376
EC8A9A411CABED670042A6AD /* UIProgressView.swift in Sources */,
366377
EC8A9A3D1CABED670042A6AD /* UILabel.swift in Sources */,
378+
165F03201CEFA7D600FCE1C9 /* UIApplication.swift in Sources */,
367379
EC8A9A331CABED670042A6AD /* UIButton.swift in Sources */,
368380
EC8A9A471CABED670042A6AD /* UISlider.swift in Sources */,
369381
EC8A9A511CABED670042A6AD /* UIView.swift in Sources */,

Sources/Protocols.swift

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
//
2+
// The MIT License (MIT)
3+
//
4+
// Copyright (c) 2015 Srdan Rasic (@srdanrasic)
5+
//
6+
// Permission is hereby granted, free of charge, to any person obtaining a copy
7+
// of this software and associated documentation files (the "Software"), to deal
8+
// in the Software without restriction, including without limitation the rights
9+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
// copies of the Software, and to permit persons to whom the Software is
11+
// furnished to do so, subject to the following conditions:
12+
//
13+
// The above copyright notice and this permission notice shall be included in
14+
// all copies or substantial portions of the Software.
15+
//
16+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22+
// THE SOFTWARE.
23+
//
24+
25+
public protocol ArrayConvertible {
26+
associatedtype Element
27+
func toArray() -> [Element]
28+
}
29+
30+
extension Array: ArrayConvertible {
31+
public func toArray() -> [Element] {
32+
return self
33+
}
34+
}

Sources/UIApplication.swift

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
//
2+
// UIApplication.swift
3+
// ReactiveUIKit
4+
//
5+
// Created by Srdan Rasic on 20/05/16.
6+
// Copyright © 2016 Srdan Rasic. All rights reserved.
7+
//
8+
9+
import UIKit
10+
import ReactiveKit
11+
12+
extension UIApplication {
13+
14+
public var rNetworkActivityIndicatorVisible: Property<Bool> {
15+
return rAssociatedPropertyForValueForKey("networkActivityIndicatorVisible")
16+
}
17+
}

Sources/UICollectionView.swift

Lines changed: 67 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -25,114 +25,88 @@
2525
import ReactiveKit
2626
import UIKit
2727

28-
extension UICollectionView {
29-
private struct AssociatedKeys {
30-
static var DataSourceKey = "r_DataSourceKey"
28+
private func applyRowUnitChangeSet<C: CollectionChangesetType where C.Collection.Index == Int>(changeSet: C, collectionView: UICollectionView, sectionIndex: Int) {
29+
if changeSet.inserts.count > 0 {
30+
let indexPaths = changeSet.inserts.map { NSIndexPath(forItem: $0, inSection: sectionIndex) }
31+
collectionView.insertItemsAtIndexPaths(indexPaths)
3132
}
32-
}
3333

34-
extension StreamType where Event.Element: CollectionChangesetType, Event.Element.Collection.Index == Int {
35-
public func bindTo(collectionView: UICollectionView, animated: Bool = true, proxyDataSource: RKCollectionViewProxyDataSource? = nil, createCell: (NSIndexPath, Event.Element.Collection, UICollectionView) -> UICollectionViewCell) -> Disposable {
34+
if changeSet.updates.count > 0 {
35+
let indexPaths = changeSet.updates.map { NSIndexPath(forItem: $0, inSection: sectionIndex) }
36+
collectionView.reloadItemsAtIndexPaths(indexPaths)
37+
}
3638

37-
let dataSource = RKCollectionViewDataSource(stream: self, collectionView: collectionView, animated: animated, proxyDataSource: proxyDataSource, createCell: createCell)
38-
objc_setAssociatedObject(collectionView, &UICollectionView.AssociatedKeys.DataSourceKey, dataSource, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
39-
40-
return BlockDisposable { [weak collectionView] in
41-
if let collectionView = collectionView {
42-
objc_setAssociatedObject(collectionView, &UICollectionView.AssociatedKeys.DataSourceKey, nil, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
43-
}
44-
}
39+
if changeSet.deletes.count > 0 {
40+
let indexPaths = changeSet.deletes.map { NSIndexPath(forItem: $0, inSection: sectionIndex) }
41+
collectionView.deleteItemsAtIndexPaths(indexPaths)
4542
}
4643
}
4744

48-
@objc public protocol RKCollectionViewProxyDataSource {
49-
optional func collectionView(collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, atIndexPath indexPath: NSIndexPath) -> UICollectionReusableView
50-
optional func collectionView(collectionView: UICollectionView, canMoveItemAtIndexPath indexPath: NSIndexPath) -> Bool
51-
optional func collectionView(collectionView: UICollectionView, moveItemAtIndexPath sourceIndexPath: NSIndexPath, toIndexPath destinationIndexPath: NSIndexPath)
45+
extension StreamType where Element: ArrayConvertible {
46+
47+
public func bindTo(collectionView: UICollectionView, animated: Bool = true, createCell: (NSIndexPath, [Element.Element], UICollectionView) -> UICollectionViewCell) -> Disposable {
48+
return map { CollectionChangeset.initial($0.toArray()) }.bindTo(collectionView, animated: animated, createCell: createCell)
49+
}
5250
}
5351

54-
public class RKCollectionViewDataSource<S: StreamType where S.Event.Element: CollectionChangesetType, S.Event.Element.Collection.Index == Int>: NSObject, UICollectionViewDataSource {
55-
56-
private typealias Collection = S.Event.Element.Collection
57-
58-
private let stream: S
59-
private var sourceCollection: Collection? = nil
60-
private weak var collectionView: UICollectionView!
61-
private let createCell: (NSIndexPath, Collection, UICollectionView) -> UICollectionViewCell
62-
private weak var proxyDataSource: RKCollectionViewProxyDataSource?
63-
private let animated: Bool
64-
65-
public init(stream: S, collectionView: UICollectionView, animated: Bool = true, proxyDataSource: RKCollectionViewProxyDataSource?, createCell: (NSIndexPath, Collection, UICollectionView) -> UICollectionViewCell) {
66-
self.collectionView = collectionView
67-
self.createCell = createCell
68-
self.proxyDataSource = proxyDataSource
69-
self.stream = stream
70-
self.animated = animated
71-
super.init()
72-
73-
collectionView.dataSource = self
52+
extension StreamType where Element: CollectionChangesetType, Element.Collection.Index == Int, Event.Element == Element {
53+
54+
public func bindTo(collectionView: UICollectionView, animated: Bool = true, createCell: (NSIndexPath, Element.Collection, UICollectionView) -> UICollectionViewCell) -> Disposable {
55+
56+
typealias Collection = Element.Collection
57+
58+
let dataSource = collectionView.rDataSource
59+
let numberOfItems = Property(0)
60+
let collection = Property<Collection!>(nil)
61+
62+
dataSource.feed(
63+
collection,
64+
to: #selector(UICollectionViewDataSource.collectionView(_:cellForItemAtIndexPath:)),
65+
map: { (value: Collection!, collectionView: UICollectionView, indexPath: NSIndexPath) -> UICollectionViewCell in
66+
return createCell(indexPath, value, collectionView)
67+
})
68+
69+
dataSource.feed(
70+
numberOfItems,
71+
to: #selector(UICollectionViewDataSource.collectionView(_:numberOfItemsInSection:)),
72+
map: { (value: Int, _: UICollectionView, _: Int) -> Int in value }
73+
)
74+
75+
dataSource.feed(
76+
Property(1),
77+
to: #selector(UICollectionViewDataSource.numberOfSectionsInCollectionView(_:)),
78+
map: { (value: Int, _: UICollectionView) -> Int in value }
79+
)
80+
7481
collectionView.reloadData()
7582

76-
stream.observeNext { [weak self] event in
77-
if let uSelf = self {
78-
let justReload = uSelf.sourceCollection == nil
79-
uSelf.sourceCollection = event.collection
80-
if justReload || !animated {
81-
uSelf.collectionView.reloadData()
83+
let serialDisposable = SerialDisposable(otherDisposable: nil)
84+
serialDisposable.otherDisposable = observeNext { [weak collectionView] event in
85+
ImmediateOnMainExecutionContext {
86+
guard let collectionView = collectionView else { serialDisposable.dispose(); return }
87+
let justReload = collection.value == nil
88+
collection.value = event.collection
89+
numberOfItems.value = event.collection.count
90+
if justReload || !animated || event.inserts.count + event.deletes.count + event.updates.count == 0 {
91+
collectionView.reloadData()
8292
} else {
83-
uSelf.collectionView.performBatchUpdates({
84-
RKCollectionViewDataSource.applyRowUnitChangeSet(event, collectionView: uSelf.collectionView, sectionIndex: 0, dataSource: uSelf.proxyDataSource)
85-
}, completion: nil)
93+
collectionView.performBatchUpdates({
94+
applyRowUnitChangeSet(event, collectionView: collectionView, sectionIndex: 0)
95+
}, completion: nil)
8696
}
8797
}
88-
}.disposeIn(rBag)
89-
}
90-
91-
private class func applyRowUnitChangeSet(changeSet: S.Event.Element, collectionView: UICollectionView, sectionIndex: Int, dataSource: RKCollectionViewProxyDataSource?) {
92-
93-
if changeSet.inserts.count > 0 {
94-
let indexPaths = changeSet.inserts.map { NSIndexPath(forItem: $0, inSection: sectionIndex) }
95-
collectionView.insertItemsAtIndexPaths(indexPaths)
96-
}
97-
98-
if changeSet.updates.count > 0 {
99-
let indexPaths = changeSet.updates.map { NSIndexPath(forItem: $0, inSection: sectionIndex) }
100-
collectionView.reloadItemsAtIndexPaths(indexPaths)
10198
}
102-
103-
if changeSet.deletes.count > 0 {
104-
let indexPaths = changeSet.deletes.map { NSIndexPath(forItem: $0, inSection: sectionIndex) }
105-
collectionView.deleteItemsAtIndexPaths(indexPaths)
106-
}
107-
}
108-
109-
/// MARK - UICollectionViewDataSource
110-
111-
@objc public func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
112-
return 1
99+
return serialDisposable
113100
}
114-
115-
@objc public func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
116-
return sourceCollection?.count ?? 0
117-
}
118-
119-
@objc public func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
120-
return createCell(indexPath, sourceCollection!, collectionView)
121-
}
122-
123-
@objc public func collectionView(collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, atIndexPath indexPath: NSIndexPath) -> UICollectionReusableView {
124-
if let view = proxyDataSource?.collectionView?(collectionView, viewForSupplementaryElementOfKind: kind, atIndexPath: indexPath) {
125-
return view
126-
} else {
127-
fatalError("Dear Sir/Madam, your collection view has asked for a supplementary view of a \(kind) kind. Please provide a proxy data source object in bindTo() method that implements `collectionView(collectionView:viewForSupplementaryElementOfKind:atIndexPath)` method!")
128-
}
129-
}
130-
131-
@objc public func collectionView(collectionView: UICollectionView, canMoveItemAtIndexPath indexPath: NSIndexPath) -> Bool {
132-
return proxyDataSource?.collectionView?(collectionView, canMoveItemAtIndexPath: indexPath) ?? false
101+
}
102+
103+
extension UICollectionView {
104+
105+
public var rDelegate: ProtocolProxy {
106+
return protocolProxyFor(UICollectionViewDelegate.self, setter: NSSelectorFromString("setDelegate:"))
133107
}
134-
135-
@objc public func collectionView(collectionView: UICollectionView, moveItemAtIndexPath sourceIndexPath: NSIndexPath, toIndexPath destinationIndexPath: NSIndexPath) {
136-
proxyDataSource?.collectionView?(collectionView, moveItemAtIndexPath: sourceIndexPath, toIndexPath: destinationIndexPath)
108+
109+
public var rDataSource: ProtocolProxy {
110+
return protocolProxyFor(UICollectionViewDataSource.self, setter: NSSelectorFromString("setDataSource:"))
137111
}
138112
}

0 commit comments

Comments
 (0)