Skip to content

Commit

Permalink
Merge pull request #9 from yankodimitrov/SignalKit-2.0
Browse files Browse the repository at this point in the history
SignalKit 2.0
  • Loading branch information
yankodimitrov committed Oct 1, 2015
2 parents 6b80dd2 + 2c0e821 commit 1c069fd
Show file tree
Hide file tree
Showing 84 changed files with 2,943 additions and 372 deletions.
56 changes: 46 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@ let button = UIButton()
button.observe().tapEvent.next { _ in print("Tap!") }
```

Now in order to preserve the chain of operations we need to store it in a property or we can use an instance of the <code>SignalBag</code> class:
Now in order to preserve the chain of operations we need to store it in a property or we can use an instance of the <code>DisposableBag</code> class:

```swift
let signalsBag = SignalBag()
let signalsBag = DisposableBag()
...
name.observe()
.bindTo(textIn: nameLabel)
Expand Down Expand Up @@ -121,11 +121,8 @@ Keyboard.observe().willShow
.addTo(signalsBag)
```


### Custom events

#### ObservablePropety
Observable property is a thread safe <code>Observable</code> implementation that have a notion of a current value. You can get the current <code>value</code> and if you set a value it will be dispatched to all observers. Alternatively you can call <code>dispatch(newValue)</code> to notify the observers:
### ObservablePropety
`ObservablePropety` is an `Observable` protocol implementation that have a notion of a current value. If you change the value it will be dispatched to all observers:

```swift
// ViewModel
Expand All @@ -141,6 +138,45 @@ name.observe()
name.value = "John" // prints "Name: John"
```

### ObservableArray
When we alter the contents of the `ObservableArray` a new `ObservableArrayEvent` will be dispatched to all of its observers. We can also perform a batch update which will dispatch a single `ObservableArrayEvent.Batch` event with associated values for all inserted, updated and removed indexes.

#### UITableView and UICollectionView bindings
We can bind the changes in `ObservableArray` to `UITableView` or `UICollectionView`:

```swift
let list = ObservableArray([1, 2, 3])
let dataSource = MyCollectionViewDataSource(items: list)
let collectionView = UICollectionView(...)

list.observe()
.bindTo(collectionView: collectionView, dataSource: dataSource)
.addTo(signalsBag)

list.append(4)
list.removeAtIndex(0)
```
*Note: If we need only one section of rows/items we can use a single dimensional array as shown above.*

#### Batch Updates
Batch updates in `ObservableArray` will reflect to batch updates in the binded `UITableView` or `UICollectionView`:

```swift
let sectionOne = ObservableArray([1, 2])
let sectionTwo = ObservableArray([3, 4])
let list = ObservableArray<ObservableArray<Int>>([sectionOne, sectionTwo])

list.observe()
.bindTo(tableView: tableView, dataSource: dataSource)
.addTo(signalsBag)

list.performBatchUpdate { collection in

collection[0].append(22)
collection[0].removeAtIndex(0)
collection.removeAtIndex(1)
}
```

### Signal Operations

Expand Down Expand Up @@ -211,9 +247,9 @@ name.observe().bindTo(anotherName)
*Note: There are special bindTo(...) extensions for the UIKit UI components like UIView, UIControl and more.*

#### addTo
Stores a chain of signal operations in a container that conforms to the <code>SignalContainerType</code> protocol:
Stores a chain of signal operations in a `DisposableBag`:
```swift
let signalsBag = SignalBag()
let signalsBag = DisposableBag()

name.observe().next { print($0) }.addTo(signalsBag)
```
Expand Down Expand Up @@ -278,7 +314,7 @@ I will really love to include extensions for AppKit and WatchKit. Any help with

## Installation

SignalKit requires Swift 2.0 and XCode 7 beta 5
SignalKit requires Swift 2.0 and XCode 7

#### Carthage
Add the following line to your [Cartfile](https://github.com/carthage/carthage)
Expand Down
194 changes: 112 additions & 82 deletions SignalKit.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
shouldUseLaunchSchemeArgsEnv = "YES"
codeCoverageEnabled = "YES">
<Testables>
<TestableReference
skipped = "NO">
Expand Down
23 changes: 23 additions & 0 deletions SignalKit/Extensions/NSIndexSet+Signal.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//
// NSIndexSet+Signal.swift
// SignalKit
//
// Created by Yanko Dimitrov on 9/30/15.
// Copyright © 2015 Yanko Dimitrov. All rights reserved.
//

import Foundation

internal extension NSIndexSet {

convenience init(withSet set: Set<Int>) {

let indexSet = NSMutableIndexSet()

for index in set {
indexSet.addIndex(index)
}

self.init(indexSet: indexSet)
}
}
81 changes: 81 additions & 0 deletions SignalKit/Extensions/UIKit/UICollectionView+Signal.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
//
// UICollectionView+Signal.swift
// SignalKit
//
// Created by Yanko Dimitrov on 10/1/15.
// Copyright © 2015 Yanko Dimitrov. All rights reserved.
//

import UIKit

public extension ArrayBindingObserver {

/**
Bind the changes in the ObservableArray to a UICollectionView

*/
public func bindTo(collectionView collectionView: UICollectionView, dataSource: UICollectionViewDataSource) -> Disposable {

bindingStrategy = CollectionViewBindingStrategy(collectionView: collectionView)

collectionView.dataSource = dataSource
collectionView.reloadData()

return self
}
}

internal final class CollectionViewBindingStrategy: ArrayBindingStrategyType {

private weak var collectionView: UICollectionView?

init(collectionView: UICollectionView) {

self.collectionView = collectionView
}

func reloadAllSections() {

collectionView?.reloadData()
}

func insertSections(sections: NSIndexSet) {

collectionView?.insertSections(sections)
}

func reloadSections(sections: NSIndexSet) {

collectionView?.reloadSections(sections)
}

func deleteSections(sections: NSIndexSet) {

collectionView?.deleteSections(sections)
}

func reloadRowsInSections(sections: NSIndexSet) {

collectionView?.reloadSections(sections)
}

func insertRowsAtIndexPaths(paths: [NSIndexPath]) {

collectionView?.insertItemsAtIndexPaths(paths)
}

func reloadRowsAtIndexPaths(paths: [NSIndexPath]) {

collectionView?.reloadItemsAtIndexPaths(paths)
}

func deleteRowsAtIndexPaths(paths: [NSIndexPath]) {

collectionView?.deleteItemsAtIndexPaths(paths)
}

func performBatchUpdate(update: () -> Void) {

collectionView?.performBatchUpdates(update, completion: nil)
}
}
2 changes: 1 addition & 1 deletion SignalKit/Extensions/UIKit/UIControl+Signal.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public extension SignalEventType where Sender: UIControl {
}
}

public extension SignalType where Item == Bool {
public extension SignalType where ObservationType == Bool {

/**
Bind the Boolean value of the signal to the enabled property of UIControl
Expand Down
2 changes: 1 addition & 1 deletion SignalKit/Extensions/UIKit/UIImageView+Signal.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

import UIKit

public extension SignalType where Item == UIImage? {
public extension SignalType where ObservationType == UIImage? {

/**
Bind a UIImage to the image property of UIImageView
Expand Down
4 changes: 2 additions & 2 deletions SignalKit/Extensions/UIKit/UILabel+Signal.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

import UIKit

public extension SignalType where Item == String {
public extension SignalType where ObservationType == String {

/**
Bind a String value to the text property of UILabel
Expand All @@ -25,7 +25,7 @@ public extension SignalType where Item == String {
}
}

public extension SignalType where Item == NSAttributedString {
public extension SignalType where ObservationType == NSAttributedString {

/**
Bind a NSAttributedString to the atrributedText property of UILabel
Expand Down
2 changes: 1 addition & 1 deletion SignalKit/Extensions/UIKit/UISlider+Signal.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public extension SignalEventType where Sender: UISlider {
}
}

public extension SignalType where Item == Float {
public extension SignalType where ObservationType == Float {

/**
Bind a Float to the value property of UISlider
Expand Down
2 changes: 1 addition & 1 deletion SignalKit/Extensions/UIKit/UISwitch+Signal.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public extension SignalEventType where Sender: UISwitch {
}
}

public extension SignalType where Item == Bool {
public extension SignalType where ObservationType == Bool {

/**
Bind a Bool to the on state of UISwitch
Expand Down
85 changes: 85 additions & 0 deletions SignalKit/Extensions/UIKit/UITableView+Signal.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
//
// UITableView+Signal.swift
// SignalKit
//
// Created by Yanko Dimitrov on 9/30/15.
// Copyright © 2015 Yanko Dimitrov. All rights reserved.
//

import UIKit

public extension ArrayBindingObserver {

/**
Bind the changes in the ObservableArray to a UITableView

*/
public func bindTo(tableView tableView: UITableView, dataSource: UITableViewDataSource) -> Disposable {

bindingStrategy = TableViewBindingStrategy(tableView: tableView)

tableView.dataSource = dataSource
tableView.reloadData()

return self
}
}

internal final class TableViewBindingStrategy: ArrayBindingStrategyType {

private weak var tableView: UITableView?

init(tableView: UITableView) {

self.tableView = tableView
}

func reloadAllSections() {

tableView?.reloadData()
}

func insertSections(sections: NSIndexSet) {

tableView?.insertSections(sections, withRowAnimation: .Automatic)
}

func reloadSections(sections: NSIndexSet) {

tableView?.reloadSections(sections, withRowAnimation: .Automatic)
}

func deleteSections(sections: NSIndexSet) {

tableView?.deleteSections(sections, withRowAnimation: .Automatic)
}

func reloadRowsInSections(sections: NSIndexSet) {

tableView?.reloadSections(sections, withRowAnimation: .Automatic)
}

func insertRowsAtIndexPaths(paths: [NSIndexPath]) {

tableView?.insertRowsAtIndexPaths(paths, withRowAnimation: .Automatic)
}

func reloadRowsAtIndexPaths(paths: [NSIndexPath]) {

tableView?.reloadRowsAtIndexPaths(paths, withRowAnimation: .Automatic)
}

func deleteRowsAtIndexPaths(paths: [NSIndexPath]) {

tableView?.deleteRowsAtIndexPaths(paths, withRowAnimation: .Automatic)
}

func performBatchUpdate(update: () -> Void) {

tableView?.beginUpdates()

update()

tableView?.endUpdates()
}
}
4 changes: 3 additions & 1 deletion SignalKit/Extensions/UIKit/UITextField+Signal.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ public extension SignalEventType where Sender: UITextField {

let signal = ControlSignal(control: sender, events: .EditingChanged).map { $0.text ?? "" }

signal.dispatch(sender.text ?? "")
if let text = sender.text {
signal.dispatch(text)
}

return signal
}
Expand Down
Loading

0 comments on commit 1c069fd

Please sign in to comment.