Skip to content

Commit

Permalink
v2 final updates
Browse files Browse the repository at this point in the history
  • Loading branch information
srdanrasic committed May 21, 2016
1 parent 08fe4ff commit 2fbab64
Show file tree
Hide file tree
Showing 8 changed files with 200 additions and 222 deletions.
4 changes: 2 additions & 2 deletions ReactiveUIKit.podspec
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
Pod::Spec.new do |s|
s.name = "ReactiveUIKit"
s.version = "2.0.0-beta5"
s.version = "2.0.0"
s.summary = "Reactive extensions for UIKit framework."
s.homepage = "https://github.com/ReactiveKit/ReactiveUIKit"
s.license = 'MIT'
s.author = { "Srdan Rasic" => "[email protected]" }
s.source = { :git => "https://github.com/ReactiveKit/ReactiveUIKit.git", :tag => "v2.0.0-beta5" }
s.source = { :git => "https://github.com/ReactiveKit/ReactiveUIKit.git", :tag => "v2.0.0" }

s.ios.deployment_target = '8.0'
s.tvos.deployment_target = '9.0'
Expand Down
14 changes: 13 additions & 1 deletion ReactiveUIKit.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
objects = {

/* Begin PBXBuildFile section */
165F031D1CEFA7A900FCE1C9 /* Protocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = 165F031C1CEFA7A900FCE1C9 /* Protocols.swift */; };
165F031E1CEFA7A900FCE1C9 /* Protocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = 165F031C1CEFA7A900FCE1C9 /* Protocols.swift */; };
165F03201CEFA7D600FCE1C9 /* UIApplication.swift in Sources */ = {isa = PBXBuildFile; fileRef = 165F031F1CEFA7D600FCE1C9 /* UIApplication.swift */; };
165F03211CEFA7D600FCE1C9 /* UIApplication.swift in Sources */ = {isa = PBXBuildFile; fileRef = 165F031F1CEFA7D600FCE1C9 /* UIApplication.swift */; };
EC5C668F1CB3D64B00C5F3C3 /* UITableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC8A9A2B1CABED670042A6AD /* UITableView.swift */; };
EC5C66901CB3D64C00C5F3C3 /* UITableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC8A9A2B1CABED670042A6AD /* UITableView.swift */; };
EC83BAF51CB00A5B007D7E47 /* UIBarButtonItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC83BAF41CB00A5B007D7E47 /* UIBarButtonItem.swift */; };
Expand Down Expand Up @@ -62,6 +66,8 @@
/* End PBXContainerItemProxy section */

/* Begin PBXFileReference section */
165F031C1CEFA7A900FCE1C9 /* Protocols.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Protocols.swift; path = Sources/Protocols.swift; sourceTree = SOURCE_ROOT; };
165F031F1CEFA7D600FCE1C9 /* UIApplication.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = UIApplication.swift; path = Sources/UIApplication.swift; sourceTree = SOURCE_ROOT; };
16E125A11BF5056C00543EFB /* ReactiveUIKit.podspec */ = {isa = PBXFileReference; lastKnownFileType = text; path = ReactiveUIKit.podspec; sourceTree = "<group>"; };
16E125A21BF5056C00543EFB /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
16F69F6A1CB462D400FD2B5F /* Package.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Package.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -146,11 +152,11 @@
children = (
ECB7A5441BEB71E10034053A /* ReactiveUIKit.h */,
ECB7A5461BEB71E10034053A /* Info.plist */,
165F031F1CEFA7D600FCE1C9 /* UIApplication.swift */,
EC8A9A1D1CABED670042A6AD /* UIActivityIndicatorView.swift */,
EC8A9A1E1CABED670042A6AD /* UIBarItem.swift */,
EC83BAF41CB00A5B007D7E47 /* UIBarButtonItem.swift */,
EC8A9A1F1CABED670042A6AD /* UIButton.swift */,
EC8A9A201CABED670042A6AD /* UICollectionView.swift */,
EC8A9A211CABED670042A6AD /* UIControl.swift */,
EC8A9A221CABED670042A6AD /* UIDatePicker.swift */,
EC8A9A231CABED670042A6AD /* UIImageView.swift */,
Expand All @@ -162,9 +168,11 @@
EC8A9A291CABED670042A6AD /* UISlider.swift */,
EC8A9A2A1CABED670042A6AD /* UISwitch.swift */,
EC8A9A2B1CABED670042A6AD /* UITableView.swift */,
EC8A9A201CABED670042A6AD /* UICollectionView.swift */,
EC8A9A2C1CABED670042A6AD /* UITextField.swift */,
EC8A9A2D1CABED670042A6AD /* UITextView.swift */,
EC8A9A2E1CABED670042A6AD /* UIView.swift */,
165F031C1CEFA7A900FCE1C9 /* Protocols.swift */,
);
path = ReactiveUIKit;
sourceTree = "<group>";
Expand Down Expand Up @@ -332,12 +340,14 @@
EC8A9A3C1CABED670042A6AD /* UIImageView.swift in Sources */,
EC8A9A401CABED670042A6AD /* UINavigationItem.swift in Sources */,
EC8A9A301CABED670042A6AD /* UIActivityIndicatorView.swift in Sources */,
165F031E1CEFA7A900FCE1C9 /* Protocols.swift in Sources */,
EC8A9A441CABED670042A6AD /* UIRefreshControl.swift in Sources */,
EC8A9A3A1CABED670042A6AD /* UIDatePicker.swift in Sources */,
EC5C66901CB3D64C00C5F3C3 /* UITableView.swift in Sources */,
EC83BAF61CB00A5B007D7E47 /* UIBarButtonItem.swift in Sources */,
EC8A9A421CABED670042A6AD /* UIProgressView.swift in Sources */,
EC8A9A3E1CABED670042A6AD /* UILabel.swift in Sources */,
165F03211CEFA7D600FCE1C9 /* UIApplication.swift in Sources */,
EC8A9A341CABED670042A6AD /* UIButton.swift in Sources */,
EC8A9A481CABED670042A6AD /* UISlider.swift in Sources */,
EC8A9A521CABED670042A6AD /* UIView.swift in Sources */,
Expand All @@ -358,12 +368,14 @@
EC8A9A3B1CABED670042A6AD /* UIImageView.swift in Sources */,
EC8A9A3F1CABED670042A6AD /* UINavigationItem.swift in Sources */,
EC8A9A2F1CABED670042A6AD /* UIActivityIndicatorView.swift in Sources */,
165F031D1CEFA7A900FCE1C9 /* Protocols.swift in Sources */,
EC8A9A431CABED670042A6AD /* UIRefreshControl.swift in Sources */,
EC8A9A391CABED670042A6AD /* UIDatePicker.swift in Sources */,
EC5C668F1CB3D64B00C5F3C3 /* UITableView.swift in Sources */,
EC83BAF51CB00A5B007D7E47 /* UIBarButtonItem.swift in Sources */,
EC8A9A411CABED670042A6AD /* UIProgressView.swift in Sources */,
EC8A9A3D1CABED670042A6AD /* UILabel.swift in Sources */,
165F03201CEFA7D600FCE1C9 /* UIApplication.swift in Sources */,
EC8A9A331CABED670042A6AD /* UIButton.swift in Sources */,
EC8A9A471CABED670042A6AD /* UISlider.swift in Sources */,
EC8A9A511CABED670042A6AD /* UIView.swift in Sources */,
Expand Down
34 changes: 34 additions & 0 deletions Sources/Protocols.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
//
// The MIT License (MIT)
//
// Copyright (c) 2015 Srdan Rasic (@srdanrasic)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//

public protocol ArrayConvertible {
associatedtype Element
func toArray() -> [Element]
}

extension Array: ArrayConvertible {
public func toArray() -> [Element] {
return self
}
}
17 changes: 17 additions & 0 deletions Sources/UIApplication.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
//
// UIApplication.swift
// ReactiveUIKit
//
// Created by Srdan Rasic on 20/05/16.
// Copyright © 2016 Srdan Rasic. All rights reserved.
//

import UIKit
import ReactiveKit

extension UIApplication {

public var rNetworkActivityIndicatorVisible: Property<Bool> {
return rAssociatedPropertyForValueForKey("networkActivityIndicatorVisible")
}
}
160 changes: 67 additions & 93 deletions Sources/UICollectionView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,114 +25,88 @@
import ReactiveKit
import UIKit

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

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

let dataSource = RKCollectionViewDataSource(stream: self, collectionView: collectionView, animated: animated, proxyDataSource: proxyDataSource, createCell: createCell)
objc_setAssociatedObject(collectionView, &UICollectionView.AssociatedKeys.DataSourceKey, dataSource, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)

return BlockDisposable { [weak collectionView] in
if let collectionView = collectionView {
objc_setAssociatedObject(collectionView, &UICollectionView.AssociatedKeys.DataSourceKey, nil, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
if changeSet.deletes.count > 0 {
let indexPaths = changeSet.deletes.map { NSIndexPath(forItem: $0, inSection: sectionIndex) }
collectionView.deleteItemsAtIndexPaths(indexPaths)
}
}

@objc public protocol RKCollectionViewProxyDataSource {
optional func collectionView(collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, atIndexPath indexPath: NSIndexPath) -> UICollectionReusableView
optional func collectionView(collectionView: UICollectionView, canMoveItemAtIndexPath indexPath: NSIndexPath) -> Bool
optional func collectionView(collectionView: UICollectionView, moveItemAtIndexPath sourceIndexPath: NSIndexPath, toIndexPath destinationIndexPath: NSIndexPath)
extension StreamType where Element: ArrayConvertible {

public func bindTo(collectionView: UICollectionView, animated: Bool = true, createCell: (NSIndexPath, [Element.Element], UICollectionView) -> UICollectionViewCell) -> Disposable {
return map { CollectionChangeset.initial($0.toArray()) }.bindTo(collectionView, animated: animated, createCell: createCell)
}
}

public class RKCollectionViewDataSource<S: StreamType where S.Event.Element: CollectionChangesetType, S.Event.Element.Collection.Index == Int>: NSObject, UICollectionViewDataSource {

private typealias Collection = S.Event.Element.Collection

private let stream: S
private var sourceCollection: Collection? = nil
private weak var collectionView: UICollectionView!
private let createCell: (NSIndexPath, Collection, UICollectionView) -> UICollectionViewCell
private weak var proxyDataSource: RKCollectionViewProxyDataSource?
private let animated: Bool

public init(stream: S, collectionView: UICollectionView, animated: Bool = true, proxyDataSource: RKCollectionViewProxyDataSource?, createCell: (NSIndexPath, Collection, UICollectionView) -> UICollectionViewCell) {
self.collectionView = collectionView
self.createCell = createCell
self.proxyDataSource = proxyDataSource
self.stream = stream
self.animated = animated
super.init()

collectionView.dataSource = self
extension StreamType where Element: CollectionChangesetType, Element.Collection.Index == Int, Event.Element == Element {

public func bindTo(collectionView: UICollectionView, animated: Bool = true, createCell: (NSIndexPath, Element.Collection, UICollectionView) -> UICollectionViewCell) -> Disposable {

typealias Collection = Element.Collection

let dataSource = collectionView.rDataSource
let numberOfItems = Property(0)
let collection = Property<Collection!>(nil)

dataSource.feed(
collection,
to: #selector(UICollectionViewDataSource.collectionView(_:cellForItemAtIndexPath:)),
map: { (value: Collection!, collectionView: UICollectionView, indexPath: NSIndexPath) -> UICollectionViewCell in
return createCell(indexPath, value, collectionView)
})

dataSource.feed(
numberOfItems,
to: #selector(UICollectionViewDataSource.collectionView(_:numberOfItemsInSection:)),
map: { (value: Int, _: UICollectionView, _: Int) -> Int in value }
)

dataSource.feed(
Property(1),
to: #selector(UICollectionViewDataSource.numberOfSectionsInCollectionView(_:)),
map: { (value: Int, _: UICollectionView) -> Int in value }
)

collectionView.reloadData()

stream.observeNext { [weak self] event in
if let uSelf = self {
let justReload = uSelf.sourceCollection == nil
uSelf.sourceCollection = event.collection
if justReload || !animated {
uSelf.collectionView.reloadData()
let serialDisposable = SerialDisposable(otherDisposable: nil)
serialDisposable.otherDisposable = observeNext { [weak collectionView] event in
ImmediateOnMainExecutionContext {
guard let collectionView = collectionView else { serialDisposable.dispose(); return }
let justReload = collection.value == nil
collection.value = event.collection
numberOfItems.value = event.collection.count
if justReload || !animated || event.inserts.count + event.deletes.count + event.updates.count == 0 {
collectionView.reloadData()
} else {
uSelf.collectionView.performBatchUpdates({
RKCollectionViewDataSource.applyRowUnitChangeSet(event, collectionView: uSelf.collectionView, sectionIndex: 0, dataSource: uSelf.proxyDataSource)
}, completion: nil)
collectionView.performBatchUpdates({
applyRowUnitChangeSet(event, collectionView: collectionView, sectionIndex: 0)
}, completion: nil)
}
}
}.disposeIn(rBag)
}

private class func applyRowUnitChangeSet(changeSet: S.Event.Element, collectionView: UICollectionView, sectionIndex: Int, dataSource: RKCollectionViewProxyDataSource?) {

if changeSet.inserts.count > 0 {
let indexPaths = changeSet.inserts.map { NSIndexPath(forItem: $0, inSection: sectionIndex) }
collectionView.insertItemsAtIndexPaths(indexPaths)
}

if changeSet.updates.count > 0 {
let indexPaths = changeSet.updates.map { NSIndexPath(forItem: $0, inSection: sectionIndex) }
collectionView.reloadItemsAtIndexPaths(indexPaths)
}

if changeSet.deletes.count > 0 {
let indexPaths = changeSet.deletes.map { NSIndexPath(forItem: $0, inSection: sectionIndex) }
collectionView.deleteItemsAtIndexPaths(indexPaths)
}
}

/// MARK - UICollectionViewDataSource

@objc public func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 1
return serialDisposable
}

@objc public func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return sourceCollection?.count ?? 0
}

@objc public func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
return createCell(indexPath, sourceCollection!, collectionView)
}

@objc public func collectionView(collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, atIndexPath indexPath: NSIndexPath) -> UICollectionReusableView {
if let view = proxyDataSource?.collectionView?(collectionView, viewForSupplementaryElementOfKind: kind, atIndexPath: indexPath) {
return view
} else {
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!")
}
}

@objc public func collectionView(collectionView: UICollectionView, canMoveItemAtIndexPath indexPath: NSIndexPath) -> Bool {
return proxyDataSource?.collectionView?(collectionView, canMoveItemAtIndexPath: indexPath) ?? false
}

extension UICollectionView {

public var rDelegate: ProtocolProxy {
return protocolProxyFor(UICollectionViewDelegate.self, setter: NSSelectorFromString("setDelegate:"))
}
@objc public func collectionView(collectionView: UICollectionView, moveItemAtIndexPath sourceIndexPath: NSIndexPath, toIndexPath destinationIndexPath: NSIndexPath) {
proxyDataSource?.collectionView?(collectionView, moveItemAtIndexPath: sourceIndexPath, toIndexPath: destinationIndexPath)

public var rDataSource: ProtocolProxy {
return protocolProxyFor(UICollectionViewDataSource.self, setter: NSSelectorFromString("setDataSource:"))
}
}
Loading

0 comments on commit 2fbab64

Please sign in to comment.