diff --git a/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata b/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 00000000..919434a6
--- /dev/null
+++ b/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/Bond-App/AppDelegate.swift b/Bond-App/AppDelegate.swift
index a272a07b..36cc993f 100644
--- a/Bond-App/AppDelegate.swift
+++ b/Bond-App/AppDelegate.swift
@@ -6,20 +6,18 @@
// Copyright © 2016 Swift Bond. All rights reserved.
//
-import UIKit
import Bond
import ReactiveKit
+import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
-
var window: UIWindow?
- func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
+ func application(_: UIApplication, didFinishLaunchingWithOptions _: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
window = UIWindow(frame: UIScreen.main.bounds)
window?.rootViewController = UIViewController()
window?.makeKeyAndVisible()
return true
}
}
-
diff --git a/Extensions/Bond+Realm.swift b/Extensions/Bond+Realm.swift
index 4c4b97e9..2cb2f9c6 100644
--- a/Extensions/Bond+Realm.swift
+++ b/Extensions/Bond+Realm.swift
@@ -28,44 +28,42 @@ import RealmSwift
/*
-// Get the default Realm
-let realm = try! Realm()
+ // Get the default Realm
+ let realm = try! Realm()
-// Convert realm results into a changeset signal
-let puppies = realm.objects(Dog.self).toChangesetSignal()
+ // Convert realm results into a changeset signal
+ let puppies = realm.objects(Dog.self).toChangesetSignal()
-// Changeset signals can then be bound to table or collection views
-puppies.suppressError(logging: true).bind(to: tableView, cellType: UITableViewCell.self) { (cell, dog) in
- cell.textLabel?.text = dog.name
-}
+ // Changeset signals can then be bound to table or collection views
+ puppies.suppressError(logging: true).bind(to: tableView, cellType: UITableViewCell.self) { (cell, dog) in
+ cell.textLabel?.text = dog.name
+ }
-// Adding something to the results will cause the table view to insert the respective row
-DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
- try! realm.write {
- realm.add(myDog)
- }
-}
+ // Adding something to the results will cause the table view to insert the respective row
+ DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
+ try! realm.write {
+ realm.add(myDog)
+ }
+ }
-*/
+ */
public extension RealmCollectionChange where CollectionType: Swift.Collection, CollectionType.Index == Int {
-
- public func toOrderedCollectionChangeset() throws -> OrderedCollectionChangeset {
+ func toOrderedCollectionChangeset() throws -> OrderedCollectionChangeset {
switch self {
- case .initial(let collection):
+ case let .initial(collection):
return OrderedCollectionChangeset(collection: collection, diff: OrderedCollectionDiff())
- case .update(let collection, let deletions, let insertions, let modifications):
+ case let .update(collection, deletions, insertions, modifications):
let diff = OrderedCollectionDiff(inserts: insertions, deletes: deletions, updates: modifications, moves: [])
return OrderedCollectionChangeset(collection: collection, diff: diff)
- case .error(let error):
+ case let .error(error):
throw error
}
}
}
public extension Results {
-
- public func toChangesetSignal() -> Signal>, NSError> {
+ func toChangesetSignal() -> Signal>, NSError> {
return Signal { observer in
let token = self.observe { change in
do {
@@ -82,16 +80,15 @@ public extension Results {
}
public extension Results: QueryableSectionedDataSourceProtocol {
-
- public var numberOfSections: Int {
+ var numberOfSections: Int {
return 1
}
- public func numberOfItems(inSection section: Int) -> Int {
+ func numberOfItems(inSection _: Int) -> Int {
return count
}
- public func item(at indexPath: IndexPath) -> Element {
+ func item(at indexPath: IndexPath) -> Element {
return self[indexPath.row]
}
}
diff --git a/Playground-iOS.playground/Pages/Key Value Observing.xcplaygroundpage/Contents.swift b/Playground-iOS.playground/Pages/Key Value Observing.xcplaygroundpage/Contents.swift
index dd6ee191..265f91cb 100644
--- a/Playground-iOS.playground/Pages/Key Value Observing.xcplaygroundpage/Contents.swift
+++ b/Playground-iOS.playground/Pages/Key Value Observing.xcplaygroundpage/Contents.swift
@@ -1,12 +1,12 @@
//: [Previous](@previous)
-///: Before running the playground, make sure to build "Bond-iOS" and "PlaygroundSupport"
-///: targets with any iOS Simulator as a destination.
+/// : Before running the playground, make sure to build "Bond-iOS" and "PlaygroundSupport"
+/// : targets with any iOS Simulator as a destination.
-import UIKit
import Bond
-import ReactiveKit
import PlaygroundSupport
+import ReactiveKit
+import UIKit
class Contact: NSObject {
@objc dynamic var name: String? = "n/a"
diff --git a/Playground-iOS.playground/Pages/Observable Collections.xcplaygroundpage/Contents.swift b/Playground-iOS.playground/Pages/Observable Collections.xcplaygroundpage/Contents.swift
index 7c098e00..cbfaad3f 100644
--- a/Playground-iOS.playground/Pages/Observable Collections.xcplaygroundpage/Contents.swift
+++ b/Playground-iOS.playground/Pages/Observable Collections.xcplaygroundpage/Contents.swift
@@ -1,21 +1,21 @@
//: Playground - noun: a place where people can play
-///: Before running the playground, make sure to build "Bond-iOS" and "PlaygroundSupport"
-///: targets with any iOS Simulator as a destination.
+/// : Before running the playground, make sure to build "Bond-iOS" and "PlaygroundSupport"
+/// : targets with any iOS Simulator as a destination.
import Bond
-import ReactiveKit
import PlaygroundSupport
+import ReactiveKit
// Array
let a = MutableObservableArray([1, 2, 3])
-a.observeNext { (cs) in
+a.observeNext { cs in
print(cs.diff, cs.patch)
}
-a.batchUpdate { (a) in
+a.batchUpdate { a in
a.append(1)
a.append(2)
}
@@ -26,7 +26,7 @@ let array2D = MutableObservableArray2D(Array2D(sectionsWithItems
("Cities", ["Paris", "Berlin"])
]))
-array2D.observeNext { (cs) in
+array2D.observeNext { cs in
print(cs.collection, cs.diff, cs.patch)
}
@@ -39,7 +39,7 @@ array2D.appendItem("France", toSectionAt: 1)
let s = MutableObservableSet(Set([1, 4, 3]))
-s.sortedCollection().mapCollection { $0 * 2 }.filterCollection { $0 > 2 }.observeNext { (changeset) in
+s.sortedCollection().mapCollection { $0 * 2 }.filterCollection { $0 > 2 }.observeNext { changeset in
print(changeset.collection, changeset.diff, changeset.patch)
}
@@ -49,7 +49,7 @@ s.insert(5)
let dictionary = MutableObservableDictionary(["A": 1])
-dictionary.sortedCollection(by: { $0.key < $1.key }).mapCollection({ "\($0.key): \($0.value)"}).observeNext { (changeset) in
+dictionary.sortedCollection(by: { $0.key < $1.key }).mapCollection { "\($0.key): \($0.value)" }.observeNext { changeset in
print(changeset.collection, changeset.diff, changeset.patch)
}
@@ -59,7 +59,7 @@ dictionary["B"] = 2
let data = MutableObservableCollection(Data([0x0A, 0x0B]))
-data.observeNext { (changeset) in
+data.observeNext { changeset in
print(changeset.collection, changeset.diff, changeset.patch)
}
@@ -75,20 +75,20 @@ var t = TreeArray([
TreeNode("Child 002", [
TreeNode("Child 0020"),
TreeNode("Child 0021")
- ])
- ]),
+ ])
+ ]),
TreeNode("Child 01")
- ])
+])
let ot = MutableObservableTree(t)
-ot.observeNext { (cs) in
+ot.observeNext { cs in
print(cs.collection, cs.diff, cs.patch)
}
ot.insert(TreeNode("New"), at: [0, 0])
-ot.batchUpdate { (ot) in
+ot.batchUpdate { ot in
ot.remove(at: [0, 0])
ot.remove(at: [0, 2, 1])
}
diff --git a/Playground-iOS.playground/Pages/Trees.xcplaygroundpage/Contents.swift b/Playground-iOS.playground/Pages/Trees.xcplaygroundpage/Contents.swift
index 1b12e60f..f8e48eba 100644
--- a/Playground-iOS.playground/Pages/Trees.xcplaygroundpage/Contents.swift
+++ b/Playground-iOS.playground/Pages/Trees.xcplaygroundpage/Contents.swift
@@ -1,11 +1,11 @@
//: [Previous](@previous)
-///: Before running the playground, make sure to build "Bond-iOS" and "PlaygroundSupport"
-///: targets with any iOS Simulator as a destination.
+/// : Before running the playground, make sure to build "Bond-iOS" and "PlaygroundSupport"
+/// : targets with any iOS Simulator as a destination.
+import Bond
import Foundation
import UIKit
-import Bond
// Tree node is a tree with a single root
@@ -48,7 +48,6 @@ print(t.depthFirst.randomElement()!)
// Custom trees
extension UIView: TreeProtocol {
-
public var children: [UIView] {
return subviews
}
@@ -61,5 +60,4 @@ print(v.breadthFirst.map { type(of: $0) })
print(v[childAt: [0, 0]])
-
//: [Next](@next)
diff --git a/Playground-iOS.playground/Pages/UICollectionView+ObservableArray2D.xcplaygroundpage/Contents.swift b/Playground-iOS.playground/Pages/UICollectionView+ObservableArray2D.xcplaygroundpage/Contents.swift
index 7e66fb0f..9899be73 100644
--- a/Playground-iOS.playground/Pages/UICollectionView+ObservableArray2D.xcplaygroundpage/Contents.swift
+++ b/Playground-iOS.playground/Pages/UICollectionView+ObservableArray2D.xcplaygroundpage/Contents.swift
@@ -1,16 +1,15 @@
//: [Previous](@previous)
-///: Before running the playground, make sure to build "Bond-iOS" and "PlaygroundSupport"
-///: targets with any iOS Simulator as a destination.
+/// : Before running the playground, make sure to build "Bond-iOS" and "PlaygroundSupport"
+/// : targets with any iOS Simulator as a destination.
+import Bond
import Foundation
import PlaygroundSupport
-import UIKit
-import Bond
import ReactiveKit
+import UIKit
class Cell: UICollectionViewCell {
-
let titleLabel = UILabel()
override init(frame: CGRect) {
@@ -20,7 +19,7 @@ class Cell: UICollectionViewCell {
contentView.backgroundColor = .white
}
- required init?(coder aDecoder: NSCoder) {
+ required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
@@ -31,13 +30,12 @@ class Cell: UICollectionViewCell {
}
class SectionHeader: UICollectionReusableView {
-
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = .red
}
- required init?(coder aDecoder: NSCoder) {
+ required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
@@ -59,7 +57,6 @@ PlaygroundPage.current.needsIndefiniteExecution = true
// Using custom binder to provide table view header titles
class CustomBinder: CollectionViewBinderDataSource, UICollectionViewDelegateFlowLayout where Changeset.Collection == Array2D {
-
override var collectionView: UICollectionView? {
didSet {
collectionView?.delegate = self
@@ -68,7 +65,7 @@ class CustomBinder: CollectionViewBinde
// Due to a bug in Swift related to generic subclases, we have to specify ObjC delegate method name
// if it's different than Swift name (https://bugs.swift.org/browse/SR-2817).
- @objc (collectionView:viewForSupplementaryElementOfKind:atIndexPath:)
+ @objc(collectionView:viewForSupplementaryElementOfKind:atIndexPath:)
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
switch kind {
case UICollectionView.elementKindSectionHeader:
@@ -79,7 +76,7 @@ class CustomBinder: CollectionViewBinde
}
}
- func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
+ func collectionView(_: UICollectionView, layout _: UICollectionViewLayout, referenceSizeForHeaderInSection _: Int) -> CGSize {
return CGSize(width: 200, height: 20)
}
}
@@ -91,12 +88,11 @@ class CustomBinder: CollectionViewBinde
let initialData = Array2D(sectionsWithItems: [
("A", [1, 2]),
("B", [10, 20])
- ])
+])
let data = MutableObservableArray2D(initialData)
-
-data.bind(to: collectionView, cellType: Cell.self, using: CustomBinder()) { (cell, item) in
+data.bind(to: collectionView, cellType: Cell.self, using: CustomBinder()) { cell, item in
cell.titleLabel.text = "\(item)"
}
@@ -105,7 +101,7 @@ DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
}
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
- data.batchUpdate { (data) in
+ data.batchUpdate { data in
data.appendItem(4, toSectionAt: 0)
data.insert(section: "Aa", at: 1)
data.appendItem(100, toSectionAt: 1)
@@ -120,4 +116,5 @@ DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
DispatchQueue.main.asyncAfter(deadline: .now() + 4) {
data.replaceItems(ofSectionAt: 1, with: [1, 100, 20], performDiff: true)
}
+
//: [Next](@next)
diff --git a/Playground-iOS.playground/Pages/UIPickerView+ObservableArray2D.xcplaygroundpage/Contents.swift b/Playground-iOS.playground/Pages/UIPickerView+ObservableArray2D.xcplaygroundpage/Contents.swift
index 67492dab..3e47af93 100644
--- a/Playground-iOS.playground/Pages/UIPickerView+ObservableArray2D.xcplaygroundpage/Contents.swift
+++ b/Playground-iOS.playground/Pages/UIPickerView+ObservableArray2D.xcplaygroundpage/Contents.swift
@@ -1,13 +1,13 @@
//: [Previous](@previous)
-///: Before running the playground, make sure to build "Bond-iOS" and "PlaygroundSupport"
-///: targets with any iOS Simulator as a destination.
+/// : Before running the playground, make sure to build "Bond-iOS" and "PlaygroundSupport"
+/// : targets with any iOS Simulator as a destination.
+import Bond
import Foundation
import PlaygroundSupport
-import UIKit
-import Bond
import ReactiveKit
+import UIKit
let pickerView = UIPickerView()
pickerView.frame.size = CGSize(width: 300, height: 300)
@@ -31,7 +31,7 @@ data.bind(to: pickerView)
// Handle cell selection
let selectedRow = pickerView.reactive.selectedRow
-selectedRow.observeNext { (row, component) in
+selectedRow.observeNext { row, component in
print("selected", row, component)
}
@@ -41,7 +41,7 @@ let selectedPair = selectedRow.scan([0, 0]) { (pair, rowAndComponent) -> [Int] i
return pair
}
-selectedPair.observeNext { (pair) in
+selectedPair.observeNext { pair in
print("selected indices", pair)
let items = pair.enumerated().map {
data[itemAt: [$0.offset, $0.element]]
diff --git a/Playground-iOS.playground/Pages/UITableView+ObservableArray.xcplaygroundpage/Contents.swift b/Playground-iOS.playground/Pages/UITableView+ObservableArray.xcplaygroundpage/Contents.swift
index b42ed122..68a7930b 100644
--- a/Playground-iOS.playground/Pages/UITableView+ObservableArray.xcplaygroundpage/Contents.swift
+++ b/Playground-iOS.playground/Pages/UITableView+ObservableArray.xcplaygroundpage/Contents.swift
@@ -1,13 +1,13 @@
//: [Previous](@previous)
-///: Before running the playground, make sure to build "Bond-iOS" and "PlaygroundSupport"
-///: targets with any iOS Simulator as a destination.
+/// : Before running the playground, make sure to build "Bond-iOS" and "PlaygroundSupport"
+/// : targets with any iOS Simulator as a destination.
+import Bond
import Foundation
import PlaygroundSupport
-import UIKit
-import Bond
import ReactiveKit
+import UIKit
let tableView = UITableView()
tableView.frame.size = CGSize(width: 300, height: 600)
@@ -18,7 +18,7 @@ PlaygroundPage.current.needsIndefiniteExecution = true
let data = MutableObservableArray(["A", "B", "C"])
-data.bind(to: tableView, cellType: UITableViewCell.self) { (cell, string) in
+data.bind(to: tableView, cellType: UITableViewCell.self) { cell, string in
cell.textLabel?.text = string
}
@@ -27,7 +27,7 @@ DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
}
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
- data.batchUpdate { (data) in
+ data.batchUpdate { data in
data.remove(at: 0)
data[0] = "W"
}
@@ -38,7 +38,7 @@ DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
}
// Handle cell selection
-tableView.reactive.selectedRowIndexPath.observeNext { (indexPath) in
+tableView.reactive.selectedRowIndexPath.observeNext { indexPath in
print("selected row", indexPath)
}
diff --git a/Playground-iOS.playground/Pages/UITableView+ObservableArray2D.xcplaygroundpage/Contents.swift b/Playground-iOS.playground/Pages/UITableView+ObservableArray2D.xcplaygroundpage/Contents.swift
index fc57b9f2..a756c79d 100644
--- a/Playground-iOS.playground/Pages/UITableView+ObservableArray2D.xcplaygroundpage/Contents.swift
+++ b/Playground-iOS.playground/Pages/UITableView+ObservableArray2D.xcplaygroundpage/Contents.swift
@@ -1,13 +1,13 @@
//: [Previous](@previous)
-///: Before running the playground, make sure to build "Bond-iOS" and "PlaygroundSupport"
-///: targets with any iOS Simulator as a destination.
+/// : Before running the playground, make sure to build "Bond-iOS" and "PlaygroundSupport"
+/// : targets with any iOS Simulator as a destination.
+import Bond
import Foundation
import PlaygroundSupport
-import UIKit
-import Bond
import ReactiveKit
+import UIKit
let tableView = UITableView()
tableView.frame.size = CGSize(width: 300, height: 600)
@@ -19,9 +19,8 @@ PlaygroundPage.current.needsIndefiniteExecution = true
// Using custom binder to provide table view header titles
class CustomBinder: TableViewBinderDataSource where Changeset.Collection == Array2D {
-
// Important: Annotate UITableViewDataSource methods with `@objc` in the subclass, otherwise UIKit will not see your method!
- @objc func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
+ @objc func tableView(_: UITableView, titleForHeaderInSection section: Int) -> String? {
return changeset?.collection[sectionAt: section].metadata
}
}
@@ -37,7 +36,7 @@ let initialData = Array2D(sectionsWithItems: [
let data = MutableObservableArray2D(initialData)
-data.bind(to: tableView, cellType: UITableViewCell.self, using: CustomBinder()) { (cell, item) in
+data.bind(to: tableView, cellType: UITableViewCell.self, using: CustomBinder()) { cell, item in
cell.textLabel?.text = "\(item)"
}
@@ -46,7 +45,7 @@ DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
}
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
- data.batchUpdate { (data) in
+ data.batchUpdate { data in
data.appendItem(4, toSectionAt: 0)
data.insert(section: "Aa", at: 1)
data.appendItem(100, toSectionAt: 1)
diff --git a/Playground-iOS.playground/Pages/UITableView+Signal+Diff.xcplaygroundpage/Contents.swift b/Playground-iOS.playground/Pages/UITableView+Signal+Diff.xcplaygroundpage/Contents.swift
index 58c1d901..e3aae4d3 100644
--- a/Playground-iOS.playground/Pages/UITableView+Signal+Diff.xcplaygroundpage/Contents.swift
+++ b/Playground-iOS.playground/Pages/UITableView+Signal+Diff.xcplaygroundpage/Contents.swift
@@ -1,13 +1,13 @@
//: [Previous](@previous)
-///: Before running the playground, make sure to build "Bond-iOS" and "PlaygroundSupport"
-///: targets with any iOS Simulator as a destination.
+/// : Before running the playground, make sure to build "Bond-iOS" and "PlaygroundSupport"
+/// : targets with any iOS Simulator as a destination.
+import Bond
import Foundation
import PlaygroundSupport
-import UIKit
-import Bond
import ReactiveKit
+import UIKit
let tableView = UITableView()
tableView.frame.size = CGSize(width: 300, height: 300)
@@ -21,15 +21,15 @@ let pulse = SafeSignal(sequence: 0..., interval: 1)
// A signal of [String]
let data = SafeSignal(sequence: [
- ["A"],
- ["A", "B", "C"],
- ["A", "C"],
- ["C", "A"]
- ])
+ ["A"],
+ ["A", "B", "C"],
+ ["A", "C"],
+ ["C", "A"]
+])
.zip(with: pulse) { data, _ in data } // add 1 second delay between events
.diff() // diff each new array against the previous one
-data.bind(to: tableView, cellType: UITableViewCell.self) { (cell, string) in
+data.bind(to: tableView, cellType: UITableViewCell.self) { cell, string in
cell.textLabel?.text = string
}
diff --git a/Playground-macOS.playground/Pages/NSOutlineView Bindings Simpler.xcplaygroundpage/Contents.swift b/Playground-macOS.playground/Pages/NSOutlineView Bindings Simpler.xcplaygroundpage/Contents.swift
index 716448d3..58b024d5 100644
--- a/Playground-macOS.playground/Pages/NSOutlineView Bindings Simpler.xcplaygroundpage/Contents.swift
+++ b/Playground-macOS.playground/Pages/NSOutlineView Bindings Simpler.xcplaygroundpage/Contents.swift
@@ -1,12 +1,12 @@
//: [Previous](@previous)
-///: Before running the playground, make sure to build "Bond-macOS" and "PlaygroundSupport"
-///: targets with Mac as a destination.
+/// : Before running the playground, make sure to build "Bond-macOS" and "PlaygroundSupport"
+/// : targets with Mac as a destination.
+import Bond
+import Cocoa
import Foundation
import PlaygroundSupport
-import Cocoa
-import Bond
import ReactiveKit
let outlineView = NSOutlineView()
@@ -30,7 +30,6 @@ outlineView.outlineTableColumn = column
// Let's make NSView a tree...
extension NSView: TreeProtocol {
-
public var children: [NSView] {
return subviews
}
diff --git a/Playground-macOS.playground/Pages/NSOutlineView Bindings.xcplaygroundpage/Contents.swift b/Playground-macOS.playground/Pages/NSOutlineView Bindings.xcplaygroundpage/Contents.swift
index 6111d795..2f7a8a77 100644
--- a/Playground-macOS.playground/Pages/NSOutlineView Bindings.xcplaygroundpage/Contents.swift
+++ b/Playground-macOS.playground/Pages/NSOutlineView Bindings.xcplaygroundpage/Contents.swift
@@ -1,12 +1,12 @@
//: [Previous](@previous)
-///: Before running the playground, make sure to build "Bond-macOS" and "PlaygroundSupport"
-///: targets with Mac as a destination.
+/// : Before running the playground, make sure to build "Bond-macOS" and "PlaygroundSupport"
+/// : targets with Mac as a destination.
+import Bond
+import Cocoa
import Foundation
import PlaygroundSupport
-import Cocoa
-import Bond
import ReactiveKit
let outlineView = NSOutlineView()
@@ -58,7 +58,7 @@ DispatchQueue.main.asyncAfter(deadline: .now() + 4) {
}
DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
- data.batchUpdate { (data) in
+ data.batchUpdate { data in
data.move(from: [2], to: [2, 0, 0])
data.insert(TreeNode("G"), at: [2])
data.insert(TreeNode("Dd"), at: [1, 0])
diff --git a/Playground-macOS.playground/Pages/NSTableView Bindings.xcplaygroundpage/Contents.swift b/Playground-macOS.playground/Pages/NSTableView Bindings.xcplaygroundpage/Contents.swift
index de85653f..ef208b5d 100644
--- a/Playground-macOS.playground/Pages/NSTableView Bindings.xcplaygroundpage/Contents.swift
+++ b/Playground-macOS.playground/Pages/NSTableView Bindings.xcplaygroundpage/Contents.swift
@@ -1,13 +1,13 @@
//: [Previous](@previous)
-///: Before running the playground, make sure to build "Bond-macOS" and "PlaygroundSupport"
-///: targets with Mac as a destination.
+/// : Before running the playground, make sure to build "Bond-macOS" and "PlaygroundSupport"
+/// : targets with Mac as a destination.
import Foundation
-import PlaygroundSupport
-import Cocoa
import Bond
+import Cocoa
+import PlaygroundSupport
import ReactiveKit
let tableView = NSTableView()
@@ -41,7 +41,7 @@ DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
}
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
- data.batchUpdate { (data) in
+ data.batchUpdate { data in
data.remove(at: 0)
data[0] = "Jerry"
data.append("Jenne")
@@ -49,7 +49,7 @@ DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
}
DispatchQueue.main.asyncAfter(deadline: .now() + 4) {
- data.replace(with: ["Ann", "Jerry"], performDiff: true)
+ data.replace(with: ["Ann", "Jerry"], performDiff: true)
}
//: [Next](@next)
diff --git a/Sources/Bond/AppKit/NSAppearanceCustomization.swift b/Sources/Bond/AppKit/NSAppearanceCustomization.swift
index b02fd554..2b4a3acd 100644
--- a/Sources/Bond/AppKit/NSAppearanceCustomization.swift
+++ b/Sources/Bond/AppKit/NSAppearanceCustomization.swift
@@ -24,14 +24,13 @@
#if os(macOS)
-import AppKit
-import ReactiveKit
+ import AppKit
+ import ReactiveKit
-extension ReactiveExtensions where Base: NSObject, Base: NSAppearanceCustomization {
-
- public var appearance: Bond {
- return bond(context: .immediateOnMain) { $0.appearance = $1 }
+ extension ReactiveExtensions where Base: NSObject, Base: NSAppearanceCustomization {
+ public var appearance: Bond {
+ return bond(context: .immediateOnMain) { $0.appearance = $1 }
+ }
}
-}
#endif
diff --git a/Sources/Bond/AppKit/NSButton.swift b/Sources/Bond/AppKit/NSButton.swift
index 77f278a2..6e5ad5bd 100644
--- a/Sources/Bond/AppKit/NSButton.swift
+++ b/Sources/Bond/AppKit/NSButton.swift
@@ -24,102 +24,101 @@
#if os(macOS)
-import AppKit
-import ReactiveKit
-
-extension ReactiveExtensions where Base: NSButton {
-
- public var state: DynamicSubject {
- return dynamicSubject(
- signal: controlEvent.eraseType(),
- get: { $0.state },
- set: { $0.state = $1 }
- )
- }
-
- public var title: Bond {
- return bond { $0.title = $1 }
- }
-
- public var alternateTitle: Bond {
- return bond { $0.alternateTitle = $1 }
- }
-
- public var image: Bond {
- return bond { $0.image = $1 }
- }
-
- public var alternateImage: Bond {
- return bond { $0.alternateImage = $1 }
- }
-
- public var imagePosition: Bond {
- return bond { $0.imagePosition = $1 }
- }
-
- public var imageScaling: Bond {
- return bond { $0.imageScaling = $1 }
- }
-
- @available(macOS 10.12, *)
- public var imageHugsTitle: Bond {
- return bond { $0.imageHugsTitle = $1 }
- }
-
- public var isBordered: Bond {
- return bond { $0.isBordered = $1 }
- }
-
- public var isTransparent: Bond {
- return bond { $0.isTransparent = $1 }
- }
-
- public var keyEquivalent: Bond {
- return bond { $0.keyEquivalent = $1 }
- }
-
- public var keyEquivalentModifierMask: Bond {
- return bond { $0.keyEquivalentModifierMask = $1 }
- }
-
- @available(macOS 10.10.3, *)
- public var isSpringLoaded: Bond {
- return bond { $0.isSpringLoaded = $1 }
- }
-
- @available(macOS 10.10.3, *)
- public var maxAcceleratorLevel: Bond {
- return bond { $0.maxAcceleratorLevel = $1 }
- }
-
- @available(macOS 10.12.2, *)
- public var bezelColor: Bond {
- return bond { $0.bezelColor = $1 }
- }
-
- public var attributedTitle: Bond {
- return bond { $0.attributedTitle = $1 }
- }
-
- public var attributedAlternateTitle: Bond {
- return bond { $0.attributedAlternateTitle = $1 }
- }
-
- public var bezelStyle: Bond {
- return bond { $0.bezelStyle = $1 }
- }
-
- public var allowsMixedState: Bond {
- return bond { $0.allowsMixedState = $1 }
- }
-
- public var showsBorderOnlyWhileMouseInside: Bond {
- return bond { $0.showsBorderOnlyWhileMouseInside = $1 }
- }
-
- public var sound: Bond {
- return bond { $0.sound = $1 }
+ import AppKit
+ import ReactiveKit
+
+ extension ReactiveExtensions where Base: NSButton {
+ public var state: DynamicSubject {
+ return dynamicSubject(
+ signal: controlEvent.eraseType(),
+ get: { $0.state },
+ set: { $0.state = $1 }
+ )
+ }
+
+ public var title: Bond {
+ return bond { $0.title = $1 }
+ }
+
+ public var alternateTitle: Bond {
+ return bond { $0.alternateTitle = $1 }
+ }
+
+ public var image: Bond {
+ return bond { $0.image = $1 }
+ }
+
+ public var alternateImage: Bond {
+ return bond { $0.alternateImage = $1 }
+ }
+
+ public var imagePosition: Bond {
+ return bond { $0.imagePosition = $1 }
+ }
+
+ public var imageScaling: Bond {
+ return bond { $0.imageScaling = $1 }
+ }
+
+ @available(macOS 10.12, *)
+ public var imageHugsTitle: Bond {
+ return bond { $0.imageHugsTitle = $1 }
+ }
+
+ public var isBordered: Bond {
+ return bond { $0.isBordered = $1 }
+ }
+
+ public var isTransparent: Bond {
+ return bond { $0.isTransparent = $1 }
+ }
+
+ public var keyEquivalent: Bond {
+ return bond { $0.keyEquivalent = $1 }
+ }
+
+ public var keyEquivalentModifierMask: Bond {
+ return bond { $0.keyEquivalentModifierMask = $1 }
+ }
+
+ @available(macOS 10.10.3, *)
+ public var isSpringLoaded: Bond {
+ return bond { $0.isSpringLoaded = $1 }
+ }
+
+ @available(macOS 10.10.3, *)
+ public var maxAcceleratorLevel: Bond {
+ return bond { $0.maxAcceleratorLevel = $1 }
+ }
+
+ @available(macOS 10.12.2, *)
+ public var bezelColor: Bond {
+ return bond { $0.bezelColor = $1 }
+ }
+
+ public var attributedTitle: Bond {
+ return bond { $0.attributedTitle = $1 }
+ }
+
+ public var attributedAlternateTitle: Bond {
+ return bond { $0.attributedAlternateTitle = $1 }
+ }
+
+ public var bezelStyle: Bond {
+ return bond { $0.bezelStyle = $1 }
+ }
+
+ public var allowsMixedState: Bond {
+ return bond { $0.allowsMixedState = $1 }
+ }
+
+ public var showsBorderOnlyWhileMouseInside: Bond {
+ return bond { $0.showsBorderOnlyWhileMouseInside = $1 }
+ }
+
+ public var sound: Bond {
+ return bond { $0.sound = $1 }
+ }
}
-}
#endif
diff --git a/Sources/Bond/AppKit/NSCollectionView+DataSource.swift b/Sources/Bond/AppKit/NSCollectionView+DataSource.swift
index d576343c..59807662 100644
--- a/Sources/Bond/AppKit/NSCollectionView+DataSource.swift
+++ b/Sources/Bond/AppKit/NSCollectionView+DataSource.swift
@@ -24,225 +24,217 @@
#if os(macOS)
-import AppKit
-import ReactiveKit
+ import AppKit
+ import ReactiveKit
-private var CollectionViewBinderDataSourceAssociationKey = "CollectionViewBinderDataSource"
+ private var CollectionViewBinderDataSourceAssociationKey = "CollectionViewBinderDataSource"
-open class CollectionViewBinderDataSource: NSObject, NSCollectionViewDataSource {
+ open class CollectionViewBinderDataSource: NSObject, NSCollectionViewDataSource {
+ public var createCell: ((Changeset.Collection, IndexPath, NSCollectionView) -> NSCollectionViewItem)?
- public var createCell: ((Changeset.Collection, IndexPath, NSCollectionView) -> NSCollectionViewItem)?
-
- public var changeset: Changeset? {
- didSet {
- if let changeset = changeset, oldValue != nil {
- applyChangeset(changeset)
- } else {
- collectionView?.animator().reloadData()
+ public var changeset: Changeset? {
+ didSet {
+ if let changeset = changeset, oldValue != nil {
+ applyChangeset(changeset)
+ } else {
+ collectionView?.animator().reloadData()
+ }
}
}
- }
- public weak var collectionView: NSCollectionView? {
- didSet {
- guard let collectionView = collectionView else { return }
- associateWithCollectionView(collectionView)
+ public weak var collectionView: NSCollectionView? {
+ didSet {
+ guard let collectionView = collectionView else { return }
+ associateWithCollectionView(collectionView)
+ }
}
- }
- public override init() {
- self.createCell = nil
- super.init()
- }
-
- /// - parameter createCell: A closure that creates cell for a given collection view and configures it with the given data source at the given index path.
- public init(_ createCell: @escaping (Changeset.Collection, IndexPath, NSCollectionView) -> NSCollectionViewItem) {
- self.createCell = createCell
- }
-
- // MARK: - NSCollectionViewDataSource
+ public override init() {
+ createCell = nil
+ super.init()
+ }
- public func numberOfSections(in collectionView: NSCollectionView) -> Int {
- return changeset?.collection.numberOfSections ?? 0
- }
+ /// - parameter createCell: A closure that creates cell for a given collection view and configures it with the given data source at the given index path.
+ public init(_ createCell: @escaping (Changeset.Collection, IndexPath, NSCollectionView) -> NSCollectionViewItem) {
+ self.createCell = createCell
+ }
- public func collectionView(_ collectionView: NSCollectionView, numberOfItemsInSection section: Int) -> Int {
- return changeset?.collection.numberOfItems(inSection: section) ?? 0
- }
+ // MARK: - NSCollectionViewDataSource
- public func collectionView(_ collectionView: NSCollectionView, itemForRepresentedObjectAt indexPath: IndexPath) -> NSCollectionViewItem {
- guard let changeset = changeset else { fatalError() }
- if let createCell = createCell {
- return createCell(changeset.collection, indexPath, collectionView)
- } else {
- fatalError("Subclass of CollectionViewBinderDataSource should override and implement \(#function) method if they do not initialize a `createCell` closure.")
+ public func numberOfSections(in _: NSCollectionView) -> Int {
+ return changeset?.collection.numberOfSections ?? 0
}
- }
- open func applyChangeset(_ changeset: Changeset) {
- guard let collectionView = collectionView else { return }
- let diff = changeset.diff.asOrderedCollectionDiff.map { $0.asSectionDataIndexPath }
- if diff.isEmpty {
- collectionView.animator().reloadData()
- } else {
- collectionView.animator()
- .performBatchUpdates({
- self.applyChangesetDiff(diff)
- }, completionHandler: nil)
+ public func collectionView(_: NSCollectionView, numberOfItemsInSection section: Int) -> Int {
+ return changeset?.collection.numberOfItems(inSection: section) ?? 0
}
- }
- open func applyChangesetDiff(_ diff: OrderedCollectionDiff) {
- guard let collectionView = collectionView else { return }
- let insertedSections = diff.inserts.filter { $0.count == 1 }.map { $0[0] }
- if !insertedSections.isEmpty {
- collectionView.animator().insertSections(IndexSet(insertedSections))
- }
- let insertedItems = Set(diff.inserts.filter { $0.count == 2 })
- if !insertedItems.isEmpty {
- collectionView.animator().insertItems(at: insertedItems)
- }
- let deletedSections = diff.deletes.filter { $0.count == 1 }.map { $0[0] }
- if !deletedSections.isEmpty {
- collectionView.animator().deleteSections(IndexSet(deletedSections))
- }
- let deletedItems = Set(diff.deletes.filter { $0.count == 2 })
- if !deletedItems.isEmpty {
- collectionView.animator().deleteItems(at: deletedItems)
- }
- let updatedItems = Set(diff.updates.filter { $0.count == 2 })
- if !updatedItems.isEmpty {
- collectionView.animator().reloadItems(at: updatedItems)
- }
- let updatedSections = diff.updates.filter { $0.count == 1 }.map { $0[0] }
- if !updatedSections.isEmpty {
- collectionView.animator().reloadSections(IndexSet(updatedSections))
- }
- for move in diff.moves {
- if move.from.count == 2, move.to.count == 2 {
- collectionView.animator().moveItem(at: move.from, to: move.to)
- } else if move.from.count == 1, move.to.count == 1 {
- collectionView.animator().moveSection(move.from[0], toSection: move.to[0])
+ public func collectionView(_ collectionView: NSCollectionView, itemForRepresentedObjectAt indexPath: IndexPath) -> NSCollectionViewItem {
+ guard let changeset = changeset else { fatalError() }
+ if let createCell = createCell {
+ return createCell(changeset.collection, indexPath, collectionView)
+ } else {
+ fatalError("Subclass of CollectionViewBinderDataSource should override and implement \(#function) method if they do not initialize a `createCell` closure.")
}
}
- }
- private func associateWithCollectionView(_ collectionView: NSCollectionView) {
- objc_setAssociatedObject(collectionView, &CollectionViewBinderDataSourceAssociationKey, self, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
- if collectionView.reactive.hasProtocolProxy(for: NSCollectionViewDataSource.self) {
- collectionView.reactive.dataSource.forwardTo = self
- } else {
- collectionView.dataSource = self
+ open func applyChangeset(_ changeset: Changeset) {
+ guard let collectionView = collectionView else { return }
+ let diff = changeset.diff.asOrderedCollectionDiff.map { $0.asSectionDataIndexPath }
+ if diff.isEmpty {
+ collectionView.animator().reloadData()
+ } else {
+ collectionView.animator()
+ .performBatchUpdates({
+ self.applyChangesetDiff(diff)
+ }, completionHandler: nil)
+ }
}
- }
-
-}
-extension CollectionViewBinderDataSource {
-
- public class ReloadingBinder: CollectionViewBinderDataSource {
- public override func applyChangeset(_ changeset: Changeset) {
- collectionView?.animator().reloadData()
+ open func applyChangesetDiff(_ diff: OrderedCollectionDiff) {
+ guard let collectionView = collectionView else { return }
+ let insertedSections = diff.inserts.filter { $0.count == 1 }.map { $0[0] }
+ if !insertedSections.isEmpty {
+ collectionView.animator().insertSections(IndexSet(insertedSections))
+ }
+ let insertedItems = Set(diff.inserts.filter { $0.count == 2 })
+ if !insertedItems.isEmpty {
+ collectionView.animator().insertItems(at: insertedItems)
+ }
+ let deletedSections = diff.deletes.filter { $0.count == 1 }.map { $0[0] }
+ if !deletedSections.isEmpty {
+ collectionView.animator().deleteSections(IndexSet(deletedSections))
+ }
+ let deletedItems = Set(diff.deletes.filter { $0.count == 2 })
+ if !deletedItems.isEmpty {
+ collectionView.animator().deleteItems(at: deletedItems)
+ }
+ let updatedItems = Set(diff.updates.filter { $0.count == 2 })
+ if !updatedItems.isEmpty {
+ collectionView.animator().reloadItems(at: updatedItems)
+ }
+ let updatedSections = diff.updates.filter { $0.count == 1 }.map { $0[0] }
+ if !updatedSections.isEmpty {
+ collectionView.animator().reloadSections(IndexSet(updatedSections))
+ }
+ for move in diff.moves {
+ if move.from.count == 2, move.to.count == 2 {
+ collectionView.animator().moveItem(at: move.from, to: move.to)
+ } else if move.from.count == 1, move.to.count == 1 {
+ collectionView.animator().moveSection(move.from[0], toSection: move.to[0])
+ }
+ }
}
- }
-
-}
-
-extension SignalProtocol where Element: SectionedDataSourceChangesetConvertible, Error == Never {
- /// Binds the signal of data source elements to the given collection view.
- ///
- /// - parameters:
- /// - collectionView: A collection view that should display the data from the data source.
- /// - animated: Animate partial or batched updates. Default is `true`.
- /// - returns: A disposable object that can terminate the binding. Safe to ignore - the binding will be automatically terminated when the collection view is deallocated.
- @discardableResult
- public func bind(to collectionView: NSCollectionView, createCell: @escaping (Element.Changeset.Collection, IndexPath, NSCollectionView) -> NSCollectionViewItem) -> Disposable {
- let binder = CollectionViewBinderDataSource(createCell)
- return bind(to: collectionView, using: binder)
+ private func associateWithCollectionView(_ collectionView: NSCollectionView) {
+ objc_setAssociatedObject(collectionView, &CollectionViewBinderDataSourceAssociationKey, self, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
+ if collectionView.reactive.hasProtocolProxy(for: NSCollectionViewDataSource.self) {
+ collectionView.reactive.dataSource.forwardTo = self
+ } else {
+ collectionView.dataSource = self
+ }
+ }
}
- /// Binds the signal of data source elements to the given collection view.
- ///
- /// - parameters:
- /// - collectionView: A collection view that should display the data from the data source.
- /// - binder: A `CollectionViewBinder` or its subclass that will manage the binding.
- /// - returns: A disposable object that can terminate the binding. Safe to ignore - the binding will be automatically terminated when the collection view is deallocated.
- @discardableResult
- public func bind(to collectionView: NSCollectionView, using binder: CollectionViewBinderDataSource) -> Disposable {
- binder.collectionView = collectionView
- return bind(to: collectionView) { _, changeset in
- binder.changeset = changeset.asSectionedDataSourceChangeset
+ extension CollectionViewBinderDataSource {
+ public class ReloadingBinder: CollectionViewBinderDataSource {
+ public override func applyChangeset(_: Changeset) {
+ collectionView?.animator().reloadData()
+ }
}
}
-}
-
-extension SignalProtocol where Element: SectionedDataSourceChangesetConvertible, Element.Changeset.Collection: QueryableSectionedDataSourceProtocol, Error == Never {
-
- /// Binds the signal of data source elements to the given collection view.
- ///
- /// - parameters:
- /// - collectionView: A collectionView view that should display the data from the data source.
- /// - itemType: A type of the cells that should display the data.
- /// - animated: Animate partial or batched updates. Default is `true`.
- /// - rowAnimation: Row animation for partial or batched updates. Relevant only when `animated` is `true`. Default is `.automatic`.
- /// - configureCell: A closure that configures the cell with the data source item at the respective index path.
- /// - returns: A disposable object that can terminate the binding. Safe to ignore - the binding will be automatically terminated when the collection view is deallocated.
- ///
- /// Note that the cell type name will be used as a reusable identifier and the binding will automatically register and dequeue the cell.
- /// If there exists a nib file in the bundle with the same name as the cell type name, the framework will load the cell from the nib file.
- @discardableResult
- public func bind(to collectionView: NSCollectionView, itemType: Item.Type, configureItem: @escaping (Item, Element.Changeset.Collection.Item) -> Void) -> Disposable {
- let identifierString = String(describing: Item.self)
- let identifier = NSUserInterfaceItemIdentifier(rawValue: identifierString)
- let bundle = Bundle(for: Item.self)
- if bundle.path(forResource: identifierString, ofType: "nib") != nil {
- let nib = NSNib(nibNamed: identifierString, bundle: bundle)
- collectionView.register(nib, forItemWithIdentifier: identifier)
- } else {
- collectionView.register(itemType as AnyClass, forItemWithIdentifier: identifier)
+ extension SignalProtocol where Element: SectionedDataSourceChangesetConvertible, Error == Never {
+ /// Binds the signal of data source elements to the given collection view.
+ ///
+ /// - parameters:
+ /// - collectionView: A collection view that should display the data from the data source.
+ /// - animated: Animate partial or batched updates. Default is `true`.
+ /// - returns: A disposable object that can terminate the binding. Safe to ignore - the binding will be automatically terminated when the collection view is deallocated.
+ @discardableResult
+ public func bind(to collectionView: NSCollectionView, createCell: @escaping (Element.Changeset.Collection, IndexPath, NSCollectionView) -> NSCollectionViewItem) -> Disposable {
+ let binder = CollectionViewBinderDataSource(createCell)
+ return bind(to: collectionView, using: binder)
+ }
+
+ /// Binds the signal of data source elements to the given collection view.
+ ///
+ /// - parameters:
+ /// - collectionView: A collection view that should display the data from the data source.
+ /// - binder: A `CollectionViewBinder` or its subclass that will manage the binding.
+ /// - returns: A disposable object that can terminate the binding. Safe to ignore - the binding will be automatically terminated when the collection view is deallocated.
+ @discardableResult
+ public func bind(to collectionView: NSCollectionView, using binder: CollectionViewBinderDataSource) -> Disposable {
+ binder.collectionView = collectionView
+ return bind(to: collectionView) { _, changeset in
+ binder.changeset = changeset.asSectionedDataSourceChangeset
+ }
}
- return bind(to: collectionView, createCell: { (dataSource, indexPath, collectionView) -> NSCollectionViewItem in
- let viewItem = collectionView.makeItem(withIdentifier: identifier, for: indexPath) as! Item
- let item = dataSource.item(at: indexPath)
- configureItem(viewItem, item)
- return viewItem
- })
}
- /// Binds the signal of data source elements to the given collection view.
- ///
- /// - parameters:
- /// - collectionView: A collection view that should display the data from the data source.
- /// - itemType: A type of the cells that should display the data. Cell type name will be used as reusable identifier and the binding will automatically dequeue cell.
- /// - animated: Animate partial or batched updates. Default is `true`.
- /// - rowAnimation: Row animation for partial or batched updates. Relevant only when `animated` is `true`. Default is `.automatic`.
- /// - configureCell: A closure that configures the cell with the data source item at the respective index path.
- /// - returns: A disposable object that can terminate the binding. Safe to ignore - the binding will be automatically terminated when the collection view is deallocated.
- ///
- /// Note that the cell type name will be used as a reusable identifier and the binding will automatically register and dequeue the cell.
- /// If there exists a nib file in the bundle with the same name as the cell type name, the framework will load the cell from the nib file.
- @discardableResult
- public func bind(to collectionView: NSCollectionView, itemType: Item.Type, using binderDataSource: CollectionViewBinderDataSource, configureItem: @escaping (Item, Element.Changeset.Collection.Item) -> Void) -> Disposable {
- let identifierString = String(describing: Item.self)
- let identifier = NSUserInterfaceItemIdentifier(rawValue: identifierString)
- let bundle = Bundle(for: Item.self)
- if bundle.path(forResource: identifierString, ofType: "nib") != nil {
- let nib = NSNib(nibNamed: identifierString, bundle: bundle)
- collectionView.register(nib, forItemWithIdentifier: identifier)
- } else {
- collectionView.register(itemType as AnyClass, forItemWithIdentifier: identifier)
- }
- binderDataSource.createCell = { (dataSource, indexPath, collectionView) -> NSCollectionViewItem in
- let viewItem = collectionView.makeItem(withIdentifier: identifier, for: indexPath) as! Item
- let item = dataSource.item(at: indexPath)
- configureItem(viewItem, item)
- return viewItem
+ extension SignalProtocol where Element: SectionedDataSourceChangesetConvertible, Element.Changeset.Collection: QueryableSectionedDataSourceProtocol, Error == Never {
+ /// Binds the signal of data source elements to the given collection view.
+ ///
+ /// - parameters:
+ /// - collectionView: A collectionView view that should display the data from the data source.
+ /// - itemType: A type of the cells that should display the data.
+ /// - animated: Animate partial or batched updates. Default is `true`.
+ /// - rowAnimation: Row animation for partial or batched updates. Relevant only when `animated` is `true`. Default is `.automatic`.
+ /// - configureCell: A closure that configures the cell with the data source item at the respective index path.
+ /// - returns: A disposable object that can terminate the binding. Safe to ignore - the binding will be automatically terminated when the collection view is deallocated.
+ ///
+ /// Note that the cell type name will be used as a reusable identifier and the binding will automatically register and dequeue the cell.
+ /// If there exists a nib file in the bundle with the same name as the cell type name, the framework will load the cell from the nib file.
+ @discardableResult
+ public func bind(to collectionView: NSCollectionView, itemType: Item.Type, configureItem: @escaping (Item, Element.Changeset.Collection.Item) -> Void) -> Disposable {
+ let identifierString = String(describing: Item.self)
+ let identifier = NSUserInterfaceItemIdentifier(rawValue: identifierString)
+ let bundle = Bundle(for: Item.self)
+ if bundle.path(forResource: identifierString, ofType: "nib") != nil {
+ let nib = NSNib(nibNamed: identifierString, bundle: bundle)
+ collectionView.register(nib, forItemWithIdentifier: identifier)
+ } else {
+ collectionView.register(itemType as AnyClass, forItemWithIdentifier: identifier)
+ }
+ return bind(to: collectionView, createCell: { (dataSource, indexPath, collectionView) -> NSCollectionViewItem in
+ let viewItem = collectionView.makeItem(withIdentifier: identifier, for: indexPath) as! Item
+ let item = dataSource.item(at: indexPath)
+ configureItem(viewItem, item)
+ return viewItem
+ })
+ }
+
+ /// Binds the signal of data source elements to the given collection view.
+ ///
+ /// - parameters:
+ /// - collectionView: A collection view that should display the data from the data source.
+ /// - itemType: A type of the cells that should display the data. Cell type name will be used as reusable identifier and the binding will automatically dequeue cell.
+ /// - animated: Animate partial or batched updates. Default is `true`.
+ /// - rowAnimation: Row animation for partial or batched updates. Relevant only when `animated` is `true`. Default is `.automatic`.
+ /// - configureCell: A closure that configures the cell with the data source item at the respective index path.
+ /// - returns: A disposable object that can terminate the binding. Safe to ignore - the binding will be automatically terminated when the collection view is deallocated.
+ ///
+ /// Note that the cell type name will be used as a reusable identifier and the binding will automatically register and dequeue the cell.
+ /// If there exists a nib file in the bundle with the same name as the cell type name, the framework will load the cell from the nib file.
+ @discardableResult
+ public func bind(to collectionView: NSCollectionView, itemType: Item.Type, using binderDataSource: CollectionViewBinderDataSource, configureItem: @escaping (Item, Element.Changeset.Collection.Item) -> Void) -> Disposable {
+ let identifierString = String(describing: Item.self)
+ let identifier = NSUserInterfaceItemIdentifier(rawValue: identifierString)
+ let bundle = Bundle(for: Item.self)
+ if bundle.path(forResource: identifierString, ofType: "nib") != nil {
+ let nib = NSNib(nibNamed: identifierString, bundle: bundle)
+ collectionView.register(nib, forItemWithIdentifier: identifier)
+ } else {
+ collectionView.register(itemType as AnyClass, forItemWithIdentifier: identifier)
+ }
+ binderDataSource.createCell = { (dataSource, indexPath, collectionView) -> NSCollectionViewItem in
+ let viewItem = collectionView.makeItem(withIdentifier: identifier, for: indexPath) as! Item
+ let item = dataSource.item(at: indexPath)
+ configureItem(viewItem, item)
+ return viewItem
+ }
+ return bind(to: collectionView, using: binderDataSource)
}
- return bind(to: collectionView, using: binderDataSource)
}
-}
-
#endif
diff --git a/Sources/Bond/AppKit/NSCollectionView.swift b/Sources/Bond/AppKit/NSCollectionView.swift
index 6bfd7404..7b09597b 100644
--- a/Sources/Bond/AppKit/NSCollectionView.swift
+++ b/Sources/Bond/AppKit/NSCollectionView.swift
@@ -24,19 +24,17 @@
#if os(macOS)
-import AppKit
-import ReactiveKit
+ import AppKit
+ import ReactiveKit
-public extension ReactiveExtensions where Base: NSCollectionView {
+ public extension ReactiveExtensions where Base: NSCollectionView {
+ var delegate: ProtocolProxy {
+ return protocolProxy(for: NSCollectionViewDelegate.self, keyPath: \.delegate)
+ }
- var delegate: ProtocolProxy {
- return protocolProxy(for: NSCollectionViewDelegate.self, keyPath: \.delegate)
+ var dataSource: ProtocolProxy {
+ return protocolProxy(for: NSCollectionViewDataSource.self, keyPath: \.dataSource)
+ }
}
- var dataSource: ProtocolProxy {
- return protocolProxy(for: NSCollectionViewDataSource.self, keyPath: \.dataSource)
- }
-
-}
-
#endif
diff --git a/Sources/Bond/AppKit/NSColorWell.swift b/Sources/Bond/AppKit/NSColorWell.swift
index 08d2ad35..4731ddb1 100644
--- a/Sources/Bond/AppKit/NSColorWell.swift
+++ b/Sources/Bond/AppKit/NSColorWell.swift
@@ -24,23 +24,22 @@
#if os(macOS)
-import AppKit
-import ReactiveKit
+ import AppKit
+ import ReactiveKit
-extension ReactiveExtensions where Base: NSColorWell {
+ extension ReactiveExtensions where Base: NSColorWell {
+ public var color: DynamicSubject {
+ return dynamicSubject(
+ signal: controlEvent.eraseType(),
+ triggerEventOnSetting: false,
+ get: { $0.color },
+ set: { $0.color = $1 }
+ )
+ }
- public var color: DynamicSubject {
- return dynamicSubject(
- signal: controlEvent.eraseType(),
- triggerEventOnSetting: false,
- get: { $0.color },
- set: { $0.color = $1 }
- )
+ public var isBordered: Bond {
+ return bond { $0.isBordered = $1 }
+ }
}
- public var isBordered: Bond {
- return bond { $0.isBordered = $1 }
- }
-}
-
#endif
diff --git a/Sources/Bond/AppKit/NSControl.swift b/Sources/Bond/AppKit/NSControl.swift
index 30a9b751..abf40eae 100644
--- a/Sources/Bond/AppKit/NSControl.swift
+++ b/Sources/Bond/AppKit/NSControl.swift
@@ -24,134 +24,130 @@
#if os(macOS)
-import AppKit
-import ReactiveKit
+ import AppKit
+ import ReactiveKit
-private extension NSControl {
+ private extension NSControl {
+ struct AssociatedKeys {
+ static var ControlHelperKey = "bnd_ControlHelperKey"
+ }
- struct AssociatedKeys {
- static var ControlHelperKey = "bnd_ControlHelperKey"
- }
+ @objc class BondHelper: NSObject {
+ weak var control: NSControl?
+ let subject = PassthroughSubject()
- @objc class BondHelper: NSObject
- {
- weak var control: NSControl?
- let subject = PassthroughSubject()
+ init(control: NSControl) {
+ self.control = control
+ super.init()
- init(control: NSControl) {
- self.control = control
- super.init()
+ control.target = self
+ control.action = #selector(eventHandler)
+ }
- control.target = self
- control.action = #selector(eventHandler)
- }
+ @objc func eventHandler(_ sender: NSControl?) {
+ subject.send(sender)
+ }
- @objc func eventHandler(_ sender: NSControl?) {
- subject.send(sender)
- }
-
- deinit {
- control?.target = nil
- control?.action = nil
- subject.send(completion: .finished)
+ deinit {
+ control?.target = nil
+ control?.action = nil
+ subject.send(completion: .finished)
+ }
}
}
-}
-
-extension ReactiveExtensions where Base: NSControl {
- public var controlEvent: SafeSignal {
- if let controlHelper = objc_getAssociatedObject(base, &NSControl.AssociatedKeys.ControlHelperKey) as AnyObject? {
- return (controlHelper as! NSControl.BondHelper).subject.map { $0 as! Base }.toSignal()
- } else {
- let controlHelper = NSControl.BondHelper(control: base)
- objc_setAssociatedObject(base, &NSControl.AssociatedKeys.ControlHelperKey, controlHelper, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
- return controlHelper.subject.map { $0 as! Base }.toSignal()
+ extension ReactiveExtensions where Base: NSControl {
+ public var controlEvent: SafeSignal {
+ if let controlHelper = objc_getAssociatedObject(base, &NSControl.AssociatedKeys.ControlHelperKey) as AnyObject? {
+ return (controlHelper as! NSControl.BondHelper).subject.map { $0 as! Base }.toSignal()
+ } else {
+ let controlHelper = NSControl.BondHelper(control: base)
+ objc_setAssociatedObject(base, &NSControl.AssociatedKeys.ControlHelperKey, controlHelper, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
+ return controlHelper.subject.map { $0 as! Base }.toSignal()
+ }
}
- }
- public func controlEvent(_ read: @escaping (Base) -> T) -> SafeSignal {
- return controlEvent.map(read)
- }
+ public func controlEvent(_ read: @escaping (Base) -> T) -> SafeSignal {
+ return controlEvent.map(read)
+ }
- public var isEnabled: DynamicSubject {
- return dynamicSubject(
- signal: keyPath(#keyPath(NSControl.isEnabled), ofType: Bool.self).eraseType(),
- triggerEventOnSetting: false,
- get: { $0.isEnabled },
- set: { $0.isEnabled = $1 }
- )
- }
+ public var isEnabled: DynamicSubject {
+ return dynamicSubject(
+ signal: keyPath(#keyPath(NSControl.isEnabled), ofType: Bool.self).eraseType(),
+ triggerEventOnSetting: false,
+ get: { $0.isEnabled },
+ set: { $0.isEnabled = $1 }
+ )
+ }
- public var isHighlighted: DynamicSubject {
- return dynamicSubject(
- signal: keyPath(#keyPath(NSControl.isHighlighted), ofType: Bool.self).eraseType(),
- triggerEventOnSetting: false,
- get: { $0.isHighlighted },
- set: { $0.isHighlighted = $1 }
- )
- }
+ public var isHighlighted: DynamicSubject {
+ return dynamicSubject(
+ signal: keyPath(#keyPath(NSControl.isHighlighted), ofType: Bool.self).eraseType(),
+ triggerEventOnSetting: false,
+ get: { $0.isHighlighted },
+ set: { $0.isHighlighted = $1 }
+ )
+ }
- public var objectValue: DynamicSubject {
- return dynamicSubject(
- signal: controlEvent.eraseType(),
- triggerEventOnSetting: false,
- get: { $0.objectValue },
- set: { $0.objectValue = $1 }
- )
- }
+ public var objectValue: DynamicSubject {
+ return dynamicSubject(
+ signal: controlEvent.eraseType(),
+ triggerEventOnSetting: false,
+ get: { $0.objectValue },
+ set: { $0.objectValue = $1 }
+ )
+ }
- public var stringValue: DynamicSubject {
- return dynamicSubject(
- signal: objectValue.eraseType(),
- triggerEventOnSetting: false,
- get: { $0.stringValue },
- set: { $0.stringValue = $1 }
- )
- }
+ public var stringValue: DynamicSubject {
+ return dynamicSubject(
+ signal: objectValue.eraseType(),
+ triggerEventOnSetting: false,
+ get: { $0.stringValue },
+ set: { $0.stringValue = $1 }
+ )
+ }
- public var attributedStringValue: DynamicSubject {
- return dynamicSubject(
- signal: objectValue.eraseType(),
- triggerEventOnSetting: false,
- get: { $0.attributedStringValue },
- set: { $0.attributedStringValue = $1 }
- )
- }
+ public var attributedStringValue: DynamicSubject {
+ return dynamicSubject(
+ signal: objectValue.eraseType(),
+ triggerEventOnSetting: false,
+ get: { $0.attributedStringValue },
+ set: { $0.attributedStringValue = $1 }
+ )
+ }
- public var integerValue: DynamicSubject {
- return dynamicSubject(
- signal: objectValue.eraseType(),
- triggerEventOnSetting: false,
- get: { $0.integerValue },
- set: { $0.integerValue = $1 }
- )
- }
+ public var integerValue: DynamicSubject {
+ return dynamicSubject(
+ signal: objectValue.eraseType(),
+ triggerEventOnSetting: false,
+ get: { $0.integerValue },
+ set: { $0.integerValue = $1 }
+ )
+ }
- public var floatValue: DynamicSubject {
- return dynamicSubject(
- signal: objectValue.eraseType(),
- triggerEventOnSetting: false,
- get: { $0.floatValue },
- set: { $0.floatValue = $1 }
- )
- }
+ public var floatValue: DynamicSubject {
+ return dynamicSubject(
+ signal: objectValue.eraseType(),
+ triggerEventOnSetting: false,
+ get: { $0.floatValue },
+ set: { $0.floatValue = $1 }
+ )
+ }
- public var doubleValue: DynamicSubject {
- return dynamicSubject(
- signal: objectValue.eraseType(),
- triggerEventOnSetting: false,
- get: { $0.doubleValue },
- set: { $0.doubleValue = $1 }
- )
+ public var doubleValue: DynamicSubject {
+ return dynamicSubject(
+ signal: objectValue.eraseType(),
+ triggerEventOnSetting: false,
+ get: { $0.doubleValue },
+ set: { $0.doubleValue = $1 }
+ )
+ }
}
-}
-
-extension NSControl: BindableProtocol {
- public func bind(signal: Signal) -> Disposable {
- return reactive.objectValue.bind(signal: signal)
+ extension NSControl: BindableProtocol {
+ public func bind(signal: Signal) -> Disposable {
+ return reactive.objectValue.bind(signal: signal)
+ }
}
-}
#endif
diff --git a/Sources/Bond/AppKit/NSGestureRecognizer.swift b/Sources/Bond/AppKit/NSGestureRecognizer.swift
index 9688c73a..8f536340 100644
--- a/Sources/Bond/AppKit/NSGestureRecognizer.swift
+++ b/Sources/Bond/AppKit/NSGestureRecognizer.swift
@@ -24,112 +24,112 @@
#if os(macOS)
-import AppKit
-import ReactiveKit
+ import AppKit
+ import ReactiveKit
-extension NSGestureRecognizer: BindingExecutionContextProvider {
- public var bindingExecutionContext: ExecutionContext { return .immediateOnMain }
-}
+ extension NSGestureRecognizer: BindingExecutionContextProvider {
+ public var bindingExecutionContext: ExecutionContext { return .immediateOnMain }
+ }
-extension ReactiveExtensions where Base: NSGestureRecognizer {
- public var isEnabled: Bond {
- return bond { $0.isEnabled = $1 }
+ extension ReactiveExtensions where Base: NSGestureRecognizer {
+ public var isEnabled: Bond {
+ return bond { $0.isEnabled = $1 }
+ }
}
-}
-
-extension ReactiveExtensions where Base: NSView {
- public func addGestureRecognizer(_ gestureRecognizer: T) -> SafeSignal {
- let base = self.base
- return Signal { [weak base] observer in
- guard let base = base else {
- observer.receive(completion: .finished)
- return NonDisposable.instance
- }
- let target = BNDGestureTarget(view: base, gestureRecognizer: gestureRecognizer) { recog in
- // swiftlint:disable:next force_cast
- observer.receive(recog as! T)
- }
- return MainBlockDisposable {
- target.unregister()
+
+ extension ReactiveExtensions where Base: NSView {
+ public func addGestureRecognizer(_ gestureRecognizer: T) -> SafeSignal {
+ let base = self.base
+ return Signal { [weak base] observer in
+ guard let base = base else {
+ observer.receive(completion: .finished)
+ return NonDisposable.instance
+ }
+ let target = BNDGestureTarget(view: base, gestureRecognizer: gestureRecognizer) { recog in
+ // swiftlint:disable:next force_cast
+ observer.receive(recog as! T)
+ }
+ return MainBlockDisposable {
+ target.unregister()
+ }
}
+ .prefix(untilOutputFrom: base.deallocated)
}
- .prefix(untilOutputFrom: base.deallocated)
- }
- public func clickGesture(numberOfClicks: Int = 1, numberOfTouches: Int = 1) -> SafeSignal {
- let gesture = NSClickGestureRecognizer()
- gesture.numberOfClicksRequired = numberOfClicks
+ public func clickGesture(numberOfClicks: Int = 1, numberOfTouches: Int = 1) -> SafeSignal {
+ let gesture = NSClickGestureRecognizer()
+ gesture.numberOfClicksRequired = numberOfClicks
- if #available(macOS 10.12.2, *) {
- gesture.numberOfTouchesRequired = numberOfTouches
+ if #available(macOS 10.12.2, *) {
+ gesture.numberOfTouchesRequired = numberOfTouches
+ }
+
+ return addGestureRecognizer(gesture)
}
- return addGestureRecognizer(gesture)
- }
+ public func magnificationGesture(magnification: CGFloat = 1.0, numberOfTouches _: Int = 1) -> SafeSignal {
+ let gesture = NSMagnificationGestureRecognizer()
+ gesture.magnification = magnification
+ return addGestureRecognizer(gesture)
+ }
- public func magnificationGesture(magnification: CGFloat = 1.0, numberOfTouches: Int = 1) -> SafeSignal {
- let gesture = NSMagnificationGestureRecognizer()
- gesture.magnification = magnification
- return addGestureRecognizer(gesture)
- }
+ public func panGesture(buttonMask: Int = 0x01, numberOfTouches: Int = 1) -> SafeSignal {
+ let gesture = NSPanGestureRecognizer()
+ gesture.buttonMask = buttonMask
- public func panGesture(buttonMask: Int = 0x01, numberOfTouches: Int = 1) -> SafeSignal {
- let gesture = NSPanGestureRecognizer()
- gesture.buttonMask = buttonMask
+ if #available(macOS 10.12.2, *) {
+ gesture.numberOfTouchesRequired = numberOfTouches
+ }
- if #available(macOS 10.12.2, *) {
- gesture.numberOfTouchesRequired = numberOfTouches
+ return addGestureRecognizer(gesture)
}
- return addGestureRecognizer(gesture)
- }
+ public func pressGesture(buttonMask: Int = 0x01, minimumPressDuration: TimeInterval = NSEvent.doubleClickInterval, allowableMovement: CGFloat = 10.0, numberOfTouches: Int = 1) -> SafeSignal {
+ let gesture = NSPressGestureRecognizer()
+ gesture.buttonMask = buttonMask
+ gesture.minimumPressDuration = minimumPressDuration
+ gesture.allowableMovement = allowableMovement
- public func pressGesture(buttonMask: Int = 0x01, minimumPressDuration: TimeInterval = NSEvent.doubleClickInterval, allowableMovement: CGFloat = 10.0, numberOfTouches: Int = 1) -> SafeSignal {
- let gesture = NSPressGestureRecognizer()
- gesture.buttonMask = buttonMask
- gesture.minimumPressDuration = minimumPressDuration
- gesture.allowableMovement = allowableMovement
+ if #available(macOS 10.12.2, *) {
+ gesture.numberOfTouchesRequired = numberOfTouches
+ }
- if #available(macOS 10.12.2, *) {
- gesture.numberOfTouchesRequired = numberOfTouches
+ return addGestureRecognizer(gesture)
}
- return addGestureRecognizer(gesture)
- }
-
- public func rotationGesture() -> SafeSignal {
- return addGestureRecognizer(NSRotationGestureRecognizer())
+ public func rotationGesture() -> SafeSignal {
+ return addGestureRecognizer(NSRotationGestureRecognizer())
+ }
}
-}
-@objc private class BNDGestureTarget: NSObject {
- private weak var view: NSView?
- private let observer: (NSGestureRecognizer) -> Void
- private let gestureRecognizer: NSGestureRecognizer
+ @objc private class BNDGestureTarget: NSObject {
+ private weak var view: NSView?
+ private let observer: (NSGestureRecognizer) -> Void
+ private let gestureRecognizer: NSGestureRecognizer
- fileprivate init(view: NSView, gestureRecognizer: NSGestureRecognizer, observer: @escaping (NSGestureRecognizer) -> Void) {
- self.view = view
- self.gestureRecognizer = gestureRecognizer
- self.observer = observer
+ fileprivate init(view: NSView, gestureRecognizer: NSGestureRecognizer, observer: @escaping (NSGestureRecognizer) -> Void) {
+ self.view = view
+ self.gestureRecognizer = gestureRecognizer
+ self.observer = observer
- super.init()
+ super.init()
- gestureRecognizer.target = self
- gestureRecognizer.action = #selector(actionHandler(recogniser:))
- view.addGestureRecognizer(gestureRecognizer)
- }
+ gestureRecognizer.target = self
+ gestureRecognizer.action = #selector(actionHandler(recogniser:))
+ view.addGestureRecognizer(gestureRecognizer)
+ }
- @objc private func actionHandler(recogniser: NSGestureRecognizer) {
- observer(recogniser)
- }
+ @objc private func actionHandler(recogniser: NSGestureRecognizer) {
+ observer(recogniser)
+ }
- fileprivate func unregister() {
- view?.removeGestureRecognizer(gestureRecognizer)
- }
+ fileprivate func unregister() {
+ view?.removeGestureRecognizer(gestureRecognizer)
+ }
- deinit {
- unregister()
+ deinit {
+ unregister()
+ }
}
-}
#endif
diff --git a/Sources/Bond/AppKit/NSImageView.swift b/Sources/Bond/AppKit/NSImageView.swift
index 611f8b89..04666fb8 100644
--- a/Sources/Bond/AppKit/NSImageView.swift
+++ b/Sources/Bond/AppKit/NSImageView.swift
@@ -24,45 +24,43 @@
#if os(macOS)
-import AppKit
-import ReactiveKit
+ import AppKit
+ import ReactiveKit
-extension ReactiveExtensions where Base: NSImageView {
+ extension ReactiveExtensions where Base: NSImageView {
+ public var image: Bond {
+ return bond { $0.image = $1 }
+ }
- public var image: Bond {
- return bond { $0.image = $1 }
- }
-
- public var imageAlignment: Bond {
- return bond { $0.imageAlignment = $1 }
- }
+ public var imageAlignment: Bond {
+ return bond { $0.imageAlignment = $1 }
+ }
- public var imageScaling: Bond {
- return bond { $0.imageScaling = $1 }
- }
+ public var imageScaling: Bond {
+ return bond { $0.imageScaling = $1 }
+ }
- public var imageFrameStyle: Bond {
- return bond { $0.imageFrameStyle = $1 }
- }
+ public var imageFrameStyle: Bond {
+ return bond { $0.imageFrameStyle = $1 }
+ }
- public var isEditable: Bond {
- return bond { $0.isEditable = $1 }
- }
+ public var isEditable: Bond {
+ return bond { $0.isEditable = $1 }
+ }
- public var animates: Bond {
- return bond { $0.animates = $1 }
- }
+ public var animates: Bond {
+ return bond { $0.animates = $1 }
+ }
- public var allowsCutCopyPaste: Bond {
- return bond { $0.allowsCutCopyPaste = $1 }
+ public var allowsCutCopyPaste: Bond {
+ return bond { $0.allowsCutCopyPaste = $1 }
+ }
}
-}
-
-extension NSImageView {
- public func bind(signal: Signal) -> Disposable {
- return reactive.image.bind(signal: signal)
+ extension NSImageView {
+ public func bind(signal: Signal) -> Disposable {
+ return reactive.image.bind(signal: signal)
+ }
}
-}
#endif
diff --git a/Sources/Bond/AppKit/NSMenuItem.swift b/Sources/Bond/AppKit/NSMenuItem.swift
index 6452562c..87722e07 100644
--- a/Sources/Bond/AppKit/NSMenuItem.swift
+++ b/Sources/Bond/AppKit/NSMenuItem.swift
@@ -24,22 +24,21 @@
#if os(macOS)
-import AppKit
-import ReactiveKit
+ import AppKit
+ import ReactiveKit
-extension NSMenuItem: BindingExecutionContextProvider {
- public var bindingExecutionContext: ExecutionContext { return .immediateOnMain }
-}
-
-extension ReactiveExtensions where Base: NSMenuItem {
-
- public var state: Bond {
- return bond { $0.state = $1 }
+ extension NSMenuItem: BindingExecutionContextProvider {
+ public var bindingExecutionContext: ExecutionContext { return .immediateOnMain }
}
- public var isEnabled: Bond {
- return bond { $0.isEnabled = $1 }
+ extension ReactiveExtensions where Base: NSMenuItem {
+ public var state: Bond {
+ return bond { $0.state = $1 }
+ }
+
+ public var isEnabled: Bond {
+ return bond { $0.isEnabled = $1 }
+ }
}
-}
#endif
diff --git a/Sources/Bond/AppKit/NSOutlineView+Changeset.swift b/Sources/Bond/AppKit/NSOutlineView+Changeset.swift
index 94d01f30..0b30589b 100644
--- a/Sources/Bond/AppKit/NSOutlineView+Changeset.swift
+++ b/Sources/Bond/AppKit/NSOutlineView+Changeset.swift
@@ -24,234 +24,231 @@
#if canImport(AppKit)
-import AppKit
-import ReactiveKit
-
-open class OutlineViewBinder: NSObject, NSOutlineViewDataSource {
-
- // NSOutlineView display a tree data structure that does not have a single root.
- //
- // "Items" of type `Any` that NSOutlineView asks for in the data source methods are tree nodes themselves, in our case `TreeNode`.
- // Note that we can't use value types here because NSOutlineView is a ObjC type so we need to use TreeArray.Object and TreeNode.Object variants.
-
- public typealias IsItemExpandable = (Changeset.Collection.Children.Element, NSOutlineView) -> Bool
- public typealias ObjectValueForItem = (Changeset.Collection.Children.Element) -> Any?
-
- public var isItemExpandable: IsItemExpandable? = nil
- public var objectValueForItem: ObjectValueForItem
-
- /// Local clone of the bound data tree wrapped into a class based tree type.
- public var rootNode = ObjectTreeArray()
-
- public var changeset: Changeset? = nil {
- didSet {
- if let changeset = changeset {
- if oldValue != nil {
- applyChangeset(changeset)
- } else {
- rootNode = clone(changeset.collection)
+ import AppKit
+ import ReactiveKit
+
+ open class OutlineViewBinder: NSObject, NSOutlineViewDataSource {
+ // NSOutlineView display a tree data structure that does not have a single root.
+ //
+ // "Items" of type `Any` that NSOutlineView asks for in the data source methods are tree nodes themselves, in our case `TreeNode`.
+ // Note that we can't use value types here because NSOutlineView is a ObjC type so we need to use TreeArray.Object and TreeNode.Object variants.
+
+ public typealias IsItemExpandable = (Changeset.Collection.Children.Element, NSOutlineView) -> Bool
+ public typealias ObjectValueForItem = (Changeset.Collection.Children.Element) -> Any?
+
+ public var isItemExpandable: IsItemExpandable?
+ public var objectValueForItem: ObjectValueForItem
+
+ /// Local clone of the bound data tree wrapped into a class based tree type.
+ public var rootNode = ObjectTreeArray()
+
+ public var changeset: Changeset? {
+ didSet {
+ if let changeset = changeset {
+ if oldValue != nil {
+ applyChangeset(changeset)
+ } else {
+ rootNode = clone(changeset.collection)
+ outlineView?.reloadData()
+ }
+ } else {
+ rootNode.children = []
outlineView?.reloadData()
}
- } else {
- rootNode.children = []
- outlineView?.reloadData()
}
}
- }
- public weak var outlineView: NSOutlineView? = nil {
- didSet {
- guard let outlineView = outlineView else { return }
- associate(with: outlineView)
+ public weak var outlineView: NSOutlineView? {
+ didSet {
+ guard let outlineView = outlineView else { return }
+ associate(with: outlineView)
+ }
}
- }
- open var itemInsertionAnimation: NSOutlineView.AnimationOptions = [.effectFade, .slideUp]
- open var itemDeletionAnimation: NSOutlineView.AnimationOptions = [.effectFade, .slideUp]
+ open var itemInsertionAnimation: NSOutlineView.AnimationOptions = [.effectFade, .slideUp]
+ open var itemDeletionAnimation: NSOutlineView.AnimationOptions = [.effectFade, .slideUp]
- /// Default initializer.
- ///
- /// - parameters:
- /// - objectValueForItem: A closure that returns object value for the given node to be used in NSOutlineViewDataSource.
- public init(objectValueForItem: @escaping ObjectValueForItem) {
- self.objectValueForItem = objectValueForItem
- super.init()
- }
+ /// Default initializer.
+ ///
+ /// - parameters:
+ /// - objectValueForItem: A closure that returns object value for the given node to be used in NSOutlineViewDataSource.
+ public init(objectValueForItem: @escaping ObjectValueForItem) {
+ self.objectValueForItem = objectValueForItem
+ super.init()
+ }
- public func item(at indexPath: IndexPath) -> ObjectTreeNode {
- return rootNode[childAt: indexPath]
- }
+ public func item(at indexPath: IndexPath) -> ObjectTreeNode {
+ return rootNode[childAt: indexPath]
+ }
- public func treeNode(forItem item: Any) -> Changeset.Collection.Children.Element? {
- return (item as? ObjectTreeNode)?.value
- }
+ public func treeNode(forItem item: Any) -> Changeset.Collection.Children.Element? {
+ return (item as? ObjectTreeNode)?.value
+ }
- // MARK: - NSOutlineViewDataSource
+ // MARK: - NSOutlineViewDataSource
- @objc(outlineView:isItemExpandable:)
- open func outlineView(_ outlineView: NSOutlineView, isItemExpandable item: Any) -> Bool {
- guard let item = item as? ObjectTreeNode else { return false }
- return isItemExpandable?(item.value, outlineView) ?? item.children.isEmpty == false
- }
+ @objc(outlineView:isItemExpandable:)
+ open func outlineView(_ outlineView: NSOutlineView, isItemExpandable item: Any) -> Bool {
+ guard let item = item as? ObjectTreeNode else { return false }
+ return isItemExpandable?(item.value, outlineView) ?? item.children.isEmpty == false
+ }
- @objc(outlineView:numberOfChildrenOfItem:)
- open func outlineView(_ outlineView: NSOutlineView, numberOfChildrenOfItem item: Any?) -> Int {
- if let item = item as? ObjectTreeNode {
- return item.children.count
- } else {
- return rootNode.children.count
+ @objc(outlineView:numberOfChildrenOfItem:)
+ open func outlineView(_: NSOutlineView, numberOfChildrenOfItem item: Any?) -> Int {
+ if let item = item as? ObjectTreeNode {
+ return item.children.count
+ } else {
+ return rootNode.children.count
+ }
}
- }
- @objc(outlineView:child:ofItem:)
- open func outlineView(_ outlineView: NSOutlineView, child index: Int, ofItem item: Any?) -> Any {
- if let item = item as? ObjectTreeNode {
- return item[[index]]
- } else {
- return rootNode[childAt: [index]]
+ @objc(outlineView:child:ofItem:)
+ open func outlineView(_: NSOutlineView, child index: Int, ofItem item: Any?) -> Any {
+ if let item = item as? ObjectTreeNode {
+ return item[[index]]
+ } else {
+ return rootNode[childAt: [index]]
+ }
}
- }
- @objc(outlineView:objectValueForTableColumn:byItem:)
- open func outlineView(_ outlineView: NSOutlineView, objectValueFor tableColumn: NSTableColumn?, byItem item: Any?) -> Any? {
- if let item = item as? ObjectTreeNode {
- return objectValueForItem(item.value)
- } else {
- return nil
+ @objc(outlineView:objectValueForTableColumn:byItem:)
+ open func outlineView(_: NSOutlineView, objectValueFor _: NSTableColumn?, byItem item: Any?) -> Any? {
+ if let item = item as? ObjectTreeNode {
+ return objectValueForItem(item.value)
+ } else {
+ return nil
+ }
}
- }
- open func applyChangeset(_ changeset: Changeset) {
- guard let outlineView = outlineView else { return }
- let patch = changeset.patch
- if patch.isEmpty {
- rootNode = clone(changeset.collection)
- outlineView.reloadData()
- } else {
- outlineView.beginUpdates()
- patch.forEach { applyOperation($0.asOrderedCollectionOperation) }
- outlineView.endUpdates()
+ open func applyChangeset(_ changeset: Changeset) {
+ guard let outlineView = outlineView else { return }
+ let patch = changeset.patch
+ if patch.isEmpty {
+ rootNode = clone(changeset.collection)
+ outlineView.reloadData()
+ } else {
+ outlineView.beginUpdates()
+ patch.forEach { applyOperation($0.asOrderedCollectionOperation) }
+ outlineView.endUpdates()
+ }
}
- }
- open func applyOperation(_ operation: OrderedCollectionOperation) {
- guard let outlineView = outlineView else { return }
+ open func applyOperation(_ operation: OrderedCollectionOperation) {
+ guard let outlineView = outlineView else { return }
- switch operation {
- case .insert(_, let at):
- rootNode.apply(operation.asOrderedCollectionOperation.mapElement { clone($0) })
- let parent = parentNode(forPath: at) // parent after the tree is patched
- outlineView.insertItems(at: IndexSet(integer: at.last!), inParent: parent, withAnimation: itemInsertionAnimation)
- case .delete(let at):
- let parent = parentNode(forPath: at) // parent before the tree is patched
- rootNode.apply(operation.asOrderedCollectionOperation.mapElement { clone($0) })
- outlineView.removeItems(at: IndexSet(integer: at.last!), inParent: parent, withAnimation: itemDeletionAnimation)
- case .update(let at, _):
- let parent = parentNode(forPath: at) // parent before the tree is patched
- rootNode.apply(operation.asOrderedCollectionOperation.mapElement { clone($0) })
- outlineView.removeItems(at: IndexSet(integer: at.last!), inParent: parent, withAnimation: itemDeletionAnimation)
- outlineView.insertItems(at: IndexSet(integer: at.last!), inParent: parent, withAnimation: itemInsertionAnimation)
- case .move(let from, let to):
- let fromParent = parentNode(forPath: from) // parent before the tree is patched
- rootNode.apply(operation.asOrderedCollectionOperation.mapElement { clone($0) })
- let toParent = parentNode(forPath: to) // parent after the tree is patched
- outlineView.moveItem(at: from.last!, inParent: fromParent, to: to.last!, inParent: toParent)
+ switch operation {
+ case let .insert(_, at):
+ rootNode.apply(operation.asOrderedCollectionOperation.mapElement { clone($0) })
+ let parent = parentNode(forPath: at) // parent after the tree is patched
+ outlineView.insertItems(at: IndexSet(integer: at.last!), inParent: parent, withAnimation: itemInsertionAnimation)
+ case let .delete(at):
+ let parent = parentNode(forPath: at) // parent before the tree is patched
+ rootNode.apply(operation.asOrderedCollectionOperation.mapElement { clone($0) })
+ outlineView.removeItems(at: IndexSet(integer: at.last!), inParent: parent, withAnimation: itemDeletionAnimation)
+ case let .update(at, _):
+ let parent = parentNode(forPath: at) // parent before the tree is patched
+ rootNode.apply(operation.asOrderedCollectionOperation.mapElement { clone($0) })
+ outlineView.removeItems(at: IndexSet(integer: at.last!), inParent: parent, withAnimation: itemDeletionAnimation)
+ outlineView.insertItems(at: IndexSet(integer: at.last!), inParent: parent, withAnimation: itemInsertionAnimation)
+ case let .move(from, to):
+ let fromParent = parentNode(forPath: from) // parent before the tree is patched
+ rootNode.apply(operation.asOrderedCollectionOperation.mapElement { clone($0) })
+ let toParent = parentNode(forPath: to) // parent after the tree is patched
+ outlineView.moveItem(at: from.last!, inParent: fromParent, to: to.last!, inParent: toParent)
+ }
}
- }
- public func parentNode(forPath path: IndexPath) -> ObjectTreeNode? {
- guard path.count > 1 else {
- return nil
+ public func parentNode(forPath path: IndexPath) -> ObjectTreeNode? {
+ guard path.count > 1 else {
+ return nil
+ }
+ return rootNode[childAt: path.dropLast()]
}
- return rootNode[childAt: path.dropLast()]
- }
- private func associate(with outlineView: NSOutlineView) {
- objc_setAssociatedObject(outlineView, &OutlineViewBinderDataSourceAssociationKey, self, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
+ private func associate(with outlineView: NSOutlineView) {
+ objc_setAssociatedObject(outlineView, &OutlineViewBinderDataSourceAssociationKey, self, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
- if outlineView.reactive.hasProtocolProxy(for: NSOutlineViewDataSource.self) {
- outlineView.reactive.dataSource.forwardTo = self
- } else {
- outlineView.dataSource = self
+ if outlineView.reactive.hasProtocolProxy(for: NSOutlineViewDataSource.self) {
+ outlineView.reactive.dataSource.forwardTo = self
+ } else {
+ outlineView.dataSource = self
+ }
}
- }
- private func clone(_ array: Changeset.Collection) -> ObjectTreeArray {
- return ObjectTreeArray(array.children.map { clone($0) })
- }
+ private func clone(_ array: Changeset.Collection) -> ObjectTreeArray {
+ return ObjectTreeArray(array.children.map { clone($0) })
+ }
- private func clone(_ node: Changeset.Collection.Children.Element) -> ObjectTreeNode {
- var newNode = ObjectTreeNode(node)
- for index in node.breadthFirst.indices.dropFirst() {
- newNode.insert(ObjectTreeNode(node[childAt: index]), at: index)
+ private func clone(_ node: Changeset.Collection.Children.Element) -> ObjectTreeNode {
+ var newNode = ObjectTreeNode(node)
+ for index in node.breadthFirst.indices.dropFirst() {
+ newNode.insert(ObjectTreeNode(node[childAt: index]), at: index)
+ }
+ return newNode
}
- return newNode
}
-}
-private var OutlineViewBinderDataSourceAssociationKey = "OutlineViewBinderDataSource"
+ private var OutlineViewBinderDataSourceAssociationKey = "OutlineViewBinderDataSource"
-extension OutlineViewBinder {
-
- public class ReloadingBinder: OutlineViewBinder {
- public override func applyChangeset(_ changeset: Changeset) {
- rootNode = clone(changeset.collection)
- outlineView?.reloadData()
+ extension OutlineViewBinder {
+ public class ReloadingBinder: OutlineViewBinder {
+ public override func applyChangeset(_ changeset: Changeset) {
+ rootNode = clone(changeset.collection)
+ outlineView?.reloadData()
+ }
}
}
-}
-extension SignalProtocol where Element: OutlineChangesetConvertible, Error == Never {
- /// Binds the signal of data source elements to the given outline view.
- ///
- /// - parameters:
- /// - outlineView: An outline view that should display the data from the data source.
- /// - animated: Animate partial or batched updates. Default is `true`.
- /// - rowAnimation: Row animation for partial or batched updates. Relevant only when `animated` is `true`. Default is `[.effectFade, .slideUp]`.
- /// - objectValueForItem: A closure that returns object value for the given node to be used in NSOutlineViewDataSource.
- /// - returns: A disposable object that can terminate the binding. Safe to ignore - the binding will be automatically terminated when the table view is deallocated.
- @discardableResult
- public func bind(to outlineView: NSOutlineView, animated: Bool = true, rowAnimation: NSOutlineView.AnimationOptions = [.effectFade, .slideUp], objectValueForItem: @escaping (Element.Changeset.Collection.Children.Element) -> Any?) -> Disposable {
- if animated {
- let binder = OutlineViewBinder(objectValueForItem: objectValueForItem)
- binder.itemInsertionAnimation = rowAnimation
- binder.itemDeletionAnimation = rowAnimation
- binder.objectValueForItem = objectValueForItem
- return bind(to: outlineView, using: binder)
- } else {
- let binder = OutlineViewBinder.ReloadingBinder(objectValueForItem: objectValueForItem)
- return bind(to: outlineView, using: binder)
+ extension SignalProtocol where Element: OutlineChangesetConvertible, Error == Never {
+ /// Binds the signal of data source elements to the given outline view.
+ ///
+ /// - parameters:
+ /// - outlineView: An outline view that should display the data from the data source.
+ /// - animated: Animate partial or batched updates. Default is `true`.
+ /// - rowAnimation: Row animation for partial or batched updates. Relevant only when `animated` is `true`. Default is `[.effectFade, .slideUp]`.
+ /// - objectValueForItem: A closure that returns object value for the given node to be used in NSOutlineViewDataSource.
+ /// - returns: A disposable object that can terminate the binding. Safe to ignore - the binding will be automatically terminated when the table view is deallocated.
+ @discardableResult
+ public func bind(to outlineView: NSOutlineView, animated: Bool = true, rowAnimation: NSOutlineView.AnimationOptions = [.effectFade, .slideUp], objectValueForItem: @escaping (Element.Changeset.Collection.Children.Element) -> Any?) -> Disposable {
+ if animated {
+ let binder = OutlineViewBinder(objectValueForItem: objectValueForItem)
+ binder.itemInsertionAnimation = rowAnimation
+ binder.itemDeletionAnimation = rowAnimation
+ binder.objectValueForItem = objectValueForItem
+ return bind(to: outlineView, using: binder)
+ } else {
+ let binder = OutlineViewBinder.ReloadingBinder(objectValueForItem: objectValueForItem)
+ return bind(to: outlineView, using: binder)
+ }
}
- }
- /// Binds the signal of data source elements to the given outline view.
- ///
- /// - parameters:
- /// - outlineView: An outline view that should display the data from the data source.
- /// - binder: A `OutlineViewBinder` or its subclass that will manage the binding.
- /// - returns: A disposable object that can terminate the binding. Safe to ignore - the binding will be automatically terminated when the outline view is deallocated.
- @discardableResult
- public func bind(to outlineView: NSOutlineView, using binder: OutlineViewBinder) -> Disposable {
- binder.outlineView = outlineView
- return bind(to: outlineView) { (_, changeset) in
- binder.changeset = changeset.asTreeArrayChangeset
+ /// Binds the signal of data source elements to the given outline view.
+ ///
+ /// - parameters:
+ /// - outlineView: An outline view that should display the data from the data source.
+ /// - binder: A `OutlineViewBinder` or its subclass that will manage the binding.
+ /// - returns: A disposable object that can terminate the binding. Safe to ignore - the binding will be automatically terminated when the outline view is deallocated.
+ @discardableResult
+ public func bind(to outlineView: NSOutlineView, using binder: OutlineViewBinder) -> Disposable {
+ binder.outlineView = outlineView
+ return bind(to: outlineView) { _, changeset in
+ binder.changeset = changeset.asTreeArrayChangeset
+ }
}
}
-}
-
-extension SignalProtocol where Element: OutlineChangesetConvertible, Element.Changeset.Collection.Children.Element: TreeNodeWithValueProtocol, Error == Never {
- /// Binds the signal of data source elements to the given outline view.
- ///
- /// - parameters:
- /// - outlineView: An outline view that should display the data from the data source.
- /// - animated: Animate partial or batched updates. Default is `true`.
- /// - rowAnimation: Row animation for partial or batched updates. Relevant only when `animated` is `true`. Default is `[.effectFade, .slideUp]`.
- /// - returns: A disposable object that can terminate the binding. Safe to ignore - the binding will be automatically terminated when the table view is deallocated.
- @discardableResult
- public func bind(to outlineView: NSOutlineView, animated: Bool = true, rowAnimation: NSOutlineView.AnimationOptions = [.effectFade, .slideUp]) -> Disposable {
- return bind(to: outlineView, animated: animated, rowAnimation: rowAnimation, objectValueForItem: { $0.value })
+ extension SignalProtocol where Element: OutlineChangesetConvertible, Element.Changeset.Collection.Children.Element: TreeNodeWithValueProtocol, Error == Never {
+ /// Binds the signal of data source elements to the given outline view.
+ ///
+ /// - parameters:
+ /// - outlineView: An outline view that should display the data from the data source.
+ /// - animated: Animate partial or batched updates. Default is `true`.
+ /// - rowAnimation: Row animation for partial or batched updates. Relevant only when `animated` is `true`. Default is `[.effectFade, .slideUp]`.
+ /// - returns: A disposable object that can terminate the binding. Safe to ignore - the binding will be automatically terminated when the table view is deallocated.
+ @discardableResult
+ public func bind(to outlineView: NSOutlineView, animated: Bool = true, rowAnimation: NSOutlineView.AnimationOptions = [.effectFade, .slideUp]) -> Disposable {
+ return bind(to: outlineView, animated: animated, rowAnimation: rowAnimation, objectValueForItem: { $0.value })
+ }
}
-}
#endif
diff --git a/Sources/Bond/AppKit/NSOutlineView.swift b/Sources/Bond/AppKit/NSOutlineView.swift
index e9101bc8..35d164f8 100644
--- a/Sources/Bond/AppKit/NSOutlineView.swift
+++ b/Sources/Bond/AppKit/NSOutlineView.swift
@@ -23,47 +23,47 @@
//
#if canImport(AppKit)
-import AppKit
-import ReactiveKit
+ import AppKit
+ import ReactiveKit
-extension ReactiveExtensions where Base: NSOutlineView {
- public var delegate: ProtocolProxy {
- return protocolProxy(for: NSOutlineViewDelegate.self, keyPath: \.delegate)
- }
+ extension ReactiveExtensions where Base: NSOutlineView {
+ public var delegate: ProtocolProxy {
+ return protocolProxy(for: NSOutlineViewDelegate.self, keyPath: \.delegate)
+ }
- public var dataSource: ProtocolProxy {
- return protocolProxy(for: NSOutlineViewDataSource.self, keyPath: \.dataSource)
- }
+ public var dataSource: ProtocolProxy {
+ return protocolProxy(for: NSOutlineViewDataSource.self, keyPath: \.dataSource)
+ }
- public var selectionIsChanging: SafeSignal {
- return NotificationCenter.default.reactive.notification(name: NSOutlineView.selectionIsChangingNotification, object: base).eraseType()
- }
+ public var selectionIsChanging: SafeSignal {
+ return NotificationCenter.default.reactive.notification(name: NSOutlineView.selectionIsChangingNotification, object: base).eraseType()
+ }
- public var selectionDidChange: SafeSignal {
- return NotificationCenter.default.reactive.notification(name: NSOutlineView.selectionDidChangeNotification, object: base).eraseType()
- }
+ public var selectionDidChange: SafeSignal {
+ return NotificationCenter.default.reactive.notification(name: NSOutlineView.selectionDidChangeNotification, object: base).eraseType()
+ }
- public var selectedRowIndexes: Bond {
- return bond { $0.selectRowIndexes($1, byExtendingSelection: false) }
- }
+ public var selectedRowIndexes: Bond {
+ return bond { $0.selectRowIndexes($1, byExtendingSelection: false) }
+ }
- public var selectedColumnIndexes: Bond {
- return bond { $0.selectColumnIndexes($1, byExtendingSelection: false) }
- }
+ public var selectedColumnIndexes: Bond {
+ return bond { $0.selectColumnIndexes($1, byExtendingSelection: false) }
+ }
- public var selectedItems: DynamicSubject<[Any]> {
- return dynamicSubject(
- signal: self.selectionDidChange,
- triggerEventOnSetting: false,
- get: { outlineView in
- outlineView.selectedRowIndexes.compactMap { outlineView.item(atRow: $0) }
- },
- set: { outlineView, items in
- let indexes = IndexSet(items.map { outlineView.row(forItem: $0) })
- outlineView.selectRowIndexes(indexes, byExtendingSelection: false)
- }
- )
+ public var selectedItems: DynamicSubject<[Any]> {
+ return dynamicSubject(
+ signal: selectionDidChange,
+ triggerEventOnSetting: false,
+ get: { outlineView in
+ outlineView.selectedRowIndexes.compactMap { outlineView.item(atRow: $0) }
+ },
+ set: { outlineView, items in
+ let indexes = IndexSet(items.map { outlineView.row(forItem: $0) })
+ outlineView.selectRowIndexes(indexes, byExtendingSelection: false)
+ }
+ )
+ }
}
-}
#endif
diff --git a/Sources/Bond/AppKit/NSPopUpButton.swift b/Sources/Bond/AppKit/NSPopUpButton.swift
index 0645c500..d76c6191 100644
--- a/Sources/Bond/AppKit/NSPopUpButton.swift
+++ b/Sources/Bond/AppKit/NSPopUpButton.swift
@@ -24,49 +24,47 @@
#if os(macOS)
-import AppKit
-import ReactiveKit
+ import AppKit
+ import ReactiveKit
-extension ReactiveExtensions where Base: NSPopUpButton {
+ extension ReactiveExtensions where Base: NSPopUpButton {
+ public var selectedItem: DynamicSubject {
+ return dynamicSubject(
+ signal: controlEvent.eraseType(),
+ get: { $0.selectedItem },
+ set: { $0.select($1) }
+ )
+ }
- public var selectedItem: DynamicSubject {
- return dynamicSubject(
- signal: controlEvent.eraseType(),
- get: { $0.selectedItem },
- set: { $0.select($1) }
- )
- }
+ public var indexOfSelectedItem: DynamicSubject {
+ return dynamicSubject(
+ signal: controlEvent.eraseType(),
+ get: { $0.indexOfSelectedItem },
+ set: { $0.selectItem(at: $1 ?? -1) }
+ )
+ }
- public var indexOfSelectedItem: DynamicSubject {
- return dynamicSubject(
- signal: controlEvent.eraseType(),
- get: { $0.indexOfSelectedItem },
- set: { $0.selectItem(at: $1 ?? -1) }
- )
- }
+ public var titleOfSelectedItem: DynamicSubject {
+ return dynamicSubject(
+ signal: controlEvent.eraseType(),
+ get: { $0.titleOfSelectedItem },
+ set: { $0.selectItem(withTitle: $1 ?? "") }
+ )
+ }
- public var titleOfSelectedItem: DynamicSubject {
- return dynamicSubject(
- signal: controlEvent.eraseType(),
- get: { $0.titleOfSelectedItem },
- set: { $0.selectItem(withTitle: $1 ?? "") }
- )
+ public var tagOfSelectedItem: DynamicSubject {
+ return dynamicSubject(
+ signal: controlEvent.eraseType(),
+ get: { $0.selectedItem?.tag },
+ set: { $0.selectItem(withTag: $1 ?? -1) }
+ )
+ }
}
- public var tagOfSelectedItem: DynamicSubject {
- return dynamicSubject(
- signal: controlEvent.eraseType(),
- get: { $0.selectedItem?.tag },
- set: { $0.selectItem(withTag: $1 ?? -1) }
- )
- }
-}
-
-extension NSPopUpButton {
-
- public func bind(signal: Signal) -> Disposable {
- return reactive.selectedItem.bind(signal: signal)
+ extension NSPopUpButton {
+ public func bind(signal: Signal) -> Disposable {
+ return reactive.selectedItem.bind(signal: signal)
+ }
}
-}
#endif
diff --git a/Sources/Bond/AppKit/NSProgressIndicator.swift b/Sources/Bond/AppKit/NSProgressIndicator.swift
index a2965a01..e52bb86e 100644
--- a/Sources/Bond/AppKit/NSProgressIndicator.swift
+++ b/Sources/Bond/AppKit/NSProgressIndicator.swift
@@ -24,59 +24,57 @@
#if os(macOS)
-import AppKit
-import ReactiveKit
+ import AppKit
+ import ReactiveKit
-extension ReactiveExtensions where Base: NSProgressIndicator {
-
- public var isAnimating: Bond {
- return bond {
- if $1 {
- $0.startAnimation(nil)
- } else {
- $0.stopAnimation(nil)
+ extension ReactiveExtensions where Base: NSProgressIndicator {
+ public var isAnimating: Bond {
+ return bond {
+ if $1 {
+ $0.startAnimation(nil)
+ } else {
+ $0.stopAnimation(nil)
+ }
}
}
- }
- public var doubleValue: Bond {
- return bond { $0.doubleValue = $1 }
- }
+ public var doubleValue: Bond {
+ return bond { $0.doubleValue = $1 }
+ }
- public var isIndeterminate: Bond {
- return bond { $0.isIndeterminate = $1 }
- }
+ public var isIndeterminate: Bond {
+ return bond { $0.isIndeterminate = $1 }
+ }
- public var minValue: Bond {
- return bond { $0.minValue = $1 }
- }
+ public var minValue: Bond {
+ return bond { $0.minValue = $1 }
+ }
- public var maxValue: Bond {
- return bond { $0.maxValue = $1 }
- }
+ public var maxValue: Bond {
+ return bond { $0.maxValue = $1 }
+ }
- public var controlTint: Bond {
- return bond { $0.controlTint = $1 }
- }
+ public var controlTint: Bond {
+ return bond { $0.controlTint = $1 }
+ }
- public var controlSize: Bond {
- return bond { $0.controlSize = $1 }
- }
+ public var controlSize: Bond {
+ return bond { $0.controlSize = $1 }
+ }
- public var style: Bond {
- return bond { $0.style = $1 }
- }
+ public var style: Bond {
+ return bond { $0.style = $1 }
+ }
- public var isDisplayedWhenStopped: Bond {
- return bond { $0.isDisplayedWhenStopped = $1 }
+ public var isDisplayedWhenStopped: Bond {
+ return bond { $0.isDisplayedWhenStopped = $1 }
+ }
}
-}
-extension NSProgressIndicator: BindableProtocol {
-
- public func bind(signal: Signal) -> Disposable {
- return reactive.doubleValue.bind(signal: signal)
+ extension NSProgressIndicator: BindableProtocol {
+ public func bind(signal: Signal) -> Disposable {
+ return reactive.doubleValue.bind(signal: signal)
+ }
}
-}
#endif
diff --git a/Sources/Bond/AppKit/NSSegmentedControl.swift b/Sources/Bond/AppKit/NSSegmentedControl.swift
index 7929e76d..c7aed9c0 100644
--- a/Sources/Bond/AppKit/NSSegmentedControl.swift
+++ b/Sources/Bond/AppKit/NSSegmentedControl.swift
@@ -24,44 +24,42 @@
#if os(macOS)
-import AppKit
-import ReactiveKit
+ import AppKit
+ import ReactiveKit
-extension ReactiveExtensions where Base: NSSegmentedControl {
+ extension ReactiveExtensions where Base: NSSegmentedControl {
+ public var segmentCount: Bond {
+ return bond { $0.segmentCount = $1 }
+ }
- public var segmentCount: Bond {
- return bond { $0.segmentCount = $1 }
- }
+ public var selectedSegment: Bond {
+ return bond { $0.selectedSegment = $1 }
+ }
- public var selectedSegment: Bond {
- return bond { $0.selectedSegment = $1 }
- }
+ public var segmentStyle: Bond {
+ return bond { $0.segmentStyle = $1 }
+ }
- public var segmentStyle: Bond {
- return bond { $0.segmentStyle = $1 }
- }
+ @available(macOS 10.10.3, *)
+ public var isSpringLoaded: Bond {
+ return bond { $0.isSpringLoaded = $1 }
+ }
- @available(macOS 10.10.3, *)
- public var isSpringLoaded: Bond {
- return bond { $0.isSpringLoaded = $1 }
- }
+ @available(macOS 10.10.3, *)
+ public var trackingMode: Bond {
+ return bond { $0.trackingMode = $1 }
+ }
- @available(macOS 10.10.3, *)
- public var trackingMode: Bond {
- return bond { $0.trackingMode = $1 }
+ @available(macOS 10.12.2, *)
+ public var selectedSegmentBezelColor: Bond {
+ return bond { $0.selectedSegmentBezelColor = $1 }
+ }
}
- @available(macOS 10.12.2, *)
- public var selectedSegmentBezelColor: Bond {
- return bond { $0.selectedSegmentBezelColor = $1 }
- }
-}
-
-extension NSSegmentedControl {
-
- public func bind(signal: Signal) -> Disposable {
- return reactive.selectedSegment.bind(signal: signal)
+ extension NSSegmentedControl {
+ public func bind(signal: Signal) -> Disposable {
+ return reactive.selectedSegment.bind(signal: signal)
+ }
}
-}
#endif
diff --git a/Sources/Bond/AppKit/NSSlider.swift b/Sources/Bond/AppKit/NSSlider.swift
index 68b86142..26933800 100644
--- a/Sources/Bond/AppKit/NSSlider.swift
+++ b/Sources/Bond/AppKit/NSSlider.swift
@@ -24,51 +24,49 @@
#if os(macOS)
-import AppKit
-import ReactiveKit
+ import AppKit
+ import ReactiveKit
-extension ReactiveExtensions where Base: NSSlider {
+ extension ReactiveExtensions where Base: NSSlider {
+ public var minValue: Bond {
+ return bond { $0.minValue = $1 }
+ }
- public var minValue: Bond {
- return bond { $0.minValue = $1 }
- }
+ public var maxValue: Bond {
+ return bond { $0.maxValue = $1 }
+ }
- public var maxValue: Bond {
- return bond { $0.maxValue = $1 }
- }
+ public var altIncrementValue: Bond {
+ return bond { $0.altIncrementValue = $1 }
+ }
- public var altIncrementValue: Bond {
- return bond { $0.altIncrementValue = $1 }
- }
+ @available(macOS 10.12, *)
+ public var isVertical: Bond {
+ return bond { $0.isVertical = $1 }
+ }
- @available(macOS 10.12, *)
- public var isVertical: Bond {
- return bond { $0.isVertical = $1 }
- }
+ @available(macOS 10.12.2, *)
+ public var trackFillColor: Bond {
+ return bond { $0.trackFillColor = $1 }
+ }
- @available(macOS 10.12.2, *)
- public var trackFillColor: Bond {
- return bond { $0.trackFillColor = $1 }
- }
+ public var numberOfTickMarks: Bond {
+ return bond { $0.numberOfTickMarks = $1 }
+ }
- public var numberOfTickMarks: Bond {
- return bond { $0.numberOfTickMarks = $1 }
- }
-
- public var tickMarkPosition: Bond {
- return bond { $0.tickMarkPosition = $1 }
- }
+ public var tickMarkPosition: Bond {
+ return bond { $0.tickMarkPosition = $1 }
+ }
- public var allowsTickMarkValuesOnly: Bond {
- return bond { $0.allowsTickMarkValuesOnly = $1 }
+ public var allowsTickMarkValuesOnly: Bond {
+ return bond { $0.allowsTickMarkValuesOnly = $1 }
+ }
}
-}
-
-extension NSSlider {
- public func bind(signal: Signal) -> Disposable {
- return reactive.doubleValue.bind(signal: signal)
+ extension NSSlider {
+ public func bind(signal: Signal) -> Disposable {
+ return reactive.doubleValue.bind(signal: signal)
+ }
}
-}
#endif
diff --git a/Sources/Bond/AppKit/NSStatusBarButton.swift b/Sources/Bond/AppKit/NSStatusBarButton.swift
index e023ad54..66edf5b8 100644
--- a/Sources/Bond/AppKit/NSStatusBarButton.swift
+++ b/Sources/Bond/AppKit/NSStatusBarButton.swift
@@ -24,14 +24,13 @@
#if os(macOS)
-import AppKit
-import ReactiveKit
+ import AppKit
+ import ReactiveKit
-extension ReactiveExtensions where Base: NSStatusBarButton {
-
- public var appearsDisabled: Bond {
- return bond { $0.appearsDisabled = $1 }
+ extension ReactiveExtensions where Base: NSStatusBarButton {
+ public var appearsDisabled: Bond {
+ return bond { $0.appearsDisabled = $1 }
+ }
}
-}
#endif
diff --git a/Sources/Bond/AppKit/NSTableView+DataSource.swift b/Sources/Bond/AppKit/NSTableView+DataSource.swift
index 48715318..0e28f393 100644
--- a/Sources/Bond/AppKit/NSTableView+DataSource.swift
+++ b/Sources/Bond/AppKit/NSTableView+DataSource.swift
@@ -24,144 +24,142 @@
#if canImport(AppKit)
-import AppKit
-import ReactiveKit
-
-private var TableViewBinderDataSourceAssociationKey = "TableViewBinderDataSource"
-
-open class TableViewBinderDataSource: NSObject, NSTableViewDataSource {
-
- open var rowInsertionAnimation: NSTableView.AnimationOptions = [.effectFade, .slideLeft]
- open var rowRemovalAnimation: NSTableView.AnimationOptions = [.effectFade, .slideLeft]
-
- /// Local clone of the bound collection
- public var collection: [Changeset.Collection.Item] = []
-
- public var changeset: Changeset? = nil {
- didSet {
- if let changeset = changeset {
- if oldValue != nil {
- apply(changeset: changeset)
+ import AppKit
+ import ReactiveKit
+
+ private var TableViewBinderDataSourceAssociationKey = "TableViewBinderDataSource"
+
+ open class TableViewBinderDataSource: NSObject, NSTableViewDataSource {
+ open var rowInsertionAnimation: NSTableView.AnimationOptions = [.effectFade, .slideLeft]
+ open var rowRemovalAnimation: NSTableView.AnimationOptions = [.effectFade, .slideLeft]
+
+ /// Local clone of the bound collection
+ public var collection: [Changeset.Collection.Item] = []
+
+ public var changeset: Changeset? {
+ didSet {
+ if let changeset = changeset {
+ if oldValue != nil {
+ apply(changeset: changeset)
+ } else {
+ collection = clone(changeset.collection)
+ tableView?.reloadData()
+ }
} else {
- collection = clone(changeset.collection)
+ collection = []
tableView?.reloadData()
}
- } else {
- collection = []
- tableView?.reloadData()
}
}
- }
- public weak var tableView: NSTableView? = nil {
- didSet {
- guard let tableView = tableView else { return }
- associateWithTableView(tableView)
+ public weak var tableView: NSTableView? {
+ didSet {
+ guard let tableView = tableView else { return }
+ associateWithTableView(tableView)
+ }
}
- }
- // MARK: - NSTableViewDataSource
+ // MARK: - NSTableViewDataSource
- @objc(numberOfRowsInTableView:)
- open func numberOfRows(in tableView: NSTableView) -> Int {
- return collection.count
- }
+ @objc(numberOfRowsInTableView:)
+ open func numberOfRows(in _: NSTableView) -> Int {
+ return collection.count
+ }
- @objc(tableView:objectValueForTableColumn:row:)
- open func tableView(_ tableView: NSTableView, objectValueFor tableColumn: NSTableColumn?, row: Int) -> Any? {
- return collection[row]
- }
+ @objc(tableView:objectValueForTableColumn:row:)
+ open func tableView(_: NSTableView, objectValueFor _: NSTableColumn?, row: Int) -> Any? {
+ return collection[row]
+ }
- open func apply(changeset: Changeset) {
- guard let tableView = tableView else { return }
- let patch = changeset.patch
- if patch.isEmpty {
- collection = clone(changeset.collection)
- tableView.reloadData()
- } else {
- tableView.beginUpdates()
- patch.forEach(apply)
- tableView.endUpdates()
+ open func apply(changeset: Changeset) {
+ guard let tableView = tableView else { return }
+ let patch = changeset.patch
+ if patch.isEmpty {
+ collection = clone(changeset.collection)
+ tableView.reloadData()
+ } else {
+ tableView.beginUpdates()
+ patch.forEach(apply)
+ tableView.endUpdates()
+ }
}
- }
- open func apply(operation: Changeset.Operation) {
- guard let tableView = tableView else { return }
-
- switch operation.asOrderedCollectionOperation {
- case .insert(let element, let index):
- collection.apply(.insert(element, at: index.asFlatDataIndex))
- tableView.insertRows(at: IndexSet([index.asFlatDataIndex]), withAnimation: rowInsertionAnimation)
- case .delete(let index):
- collection.apply(.delete(at: index.asFlatDataIndex))
- tableView.removeRows(at: IndexSet([index.asFlatDataIndex]), withAnimation: rowRemovalAnimation)
- case .update(let index, let element):
- collection.apply(.update(at: index.asFlatDataIndex, newElement: element))
- let allColumnIndexes = IndexSet(tableView.tableColumns.enumerated().map { $0.0 })
- tableView.reloadData(forRowIndexes: IndexSet([index.asFlatDataIndex]), columnIndexes: allColumnIndexes)
- case .move(let from, let to):
- collection.apply(.move(from: from.asFlatDataIndex, to: to.asFlatDataIndex))
- tableView.moveRow(at: from.asFlatDataIndex, to: to.asFlatDataIndex)
+ open func apply(operation: Changeset.Operation) {
+ guard let tableView = tableView else { return }
+
+ switch operation.asOrderedCollectionOperation {
+ case let .insert(element, index):
+ collection.apply(.insert(element, at: index.asFlatDataIndex))
+ tableView.insertRows(at: IndexSet([index.asFlatDataIndex]), withAnimation: rowInsertionAnimation)
+ case let .delete(index):
+ collection.apply(.delete(at: index.asFlatDataIndex))
+ tableView.removeRows(at: IndexSet([index.asFlatDataIndex]), withAnimation: rowRemovalAnimation)
+ case let .update(index, element):
+ collection.apply(.update(at: index.asFlatDataIndex, newElement: element))
+ let allColumnIndexes = IndexSet(tableView.tableColumns.enumerated().map { $0.0 })
+ tableView.reloadData(forRowIndexes: IndexSet([index.asFlatDataIndex]), columnIndexes: allColumnIndexes)
+ case let .move(from, to):
+ collection.apply(.move(from: from.asFlatDataIndex, to: to.asFlatDataIndex))
+ tableView.moveRow(at: from.asFlatDataIndex, to: to.asFlatDataIndex)
+ }
}
- }
- private func associateWithTableView(_ tableView: NSTableView) {
- objc_setAssociatedObject(tableView, &TableViewBinderDataSourceAssociationKey, self, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
- if tableView.reactive.hasProtocolProxy(for: NSTableViewDataSource.self) {
- tableView.reactive.dataSource.forwardTo = self
- } else {
- tableView.dataSource = self
+ private func associateWithTableView(_ tableView: NSTableView) {
+ objc_setAssociatedObject(tableView, &TableViewBinderDataSourceAssociationKey, self, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
+ if tableView.reactive.hasProtocolProxy(for: NSTableViewDataSource.self) {
+ tableView.reactive.dataSource.forwardTo = self
+ } else {
+ tableView.dataSource = self
+ }
}
- }
- private func clone(_ collection: Changeset.Collection) -> [Changeset.Collection.Item] {
- return (0.. [Changeset.Collection.Item] {
+ return (0 ..< collection.numberOfItems).map { collection.item(at: $0) }
+ }
}
-}
-extension TableViewBinderDataSource {
- public class ReloadingBinder: TableViewBinderDataSource {
- public override func apply(changeset: Changeset) {
- tableView?.reloadData()
+ extension TableViewBinderDataSource {
+ public class ReloadingBinder: TableViewBinderDataSource {
+ public override func apply(changeset _: Changeset) {
+ tableView?.reloadData()
+ }
}
}
-}
-
-extension SignalProtocol where Element: FlatDataSourceChangesetConvertible, Error == Never {
-
- /// Binds the signal of data source elements to the given table view.
- ///
- /// - parameters:
- /// - tableView: A table view that should display the data from the data source.
- /// - animated: Animate partial or batched updates. Default is `true`.
- /// - rowAnimation: Row animation for partial or batched updates. Relevant only when `animated` is `true`. Default is `[.effectFade, .slideLeft]`.
- /// - returns: A disposable object that can terminate the binding. Safe to ignore - the binding will be automatically terminated when the table view is deallocated.
- @discardableResult
- public func bind(to tableView: NSTableView, animated: Bool = true, rowAnimation: NSTableView.AnimationOptions = [.effectFade, .slideLeft]) -> Disposable {
- if animated {
- let binder = TableViewBinderDataSource()
- binder.rowInsertionAnimation = rowAnimation
- binder.rowRemovalAnimation = rowAnimation
- return bind(to: tableView, using: binder)
- } else {
- let binder = TableViewBinderDataSource.ReloadingBinder()
- return bind(to: tableView, using: binder)
+
+ extension SignalProtocol where Element: FlatDataSourceChangesetConvertible, Error == Never {
+ /// Binds the signal of data source elements to the given table view.
+ ///
+ /// - parameters:
+ /// - tableView: A table view that should display the data from the data source.
+ /// - animated: Animate partial or batched updates. Default is `true`.
+ /// - rowAnimation: Row animation for partial or batched updates. Relevant only when `animated` is `true`. Default is `[.effectFade, .slideLeft]`.
+ /// - returns: A disposable object that can terminate the binding. Safe to ignore - the binding will be automatically terminated when the table view is deallocated.
+ @discardableResult
+ public func bind(to tableView: NSTableView, animated: Bool = true, rowAnimation: NSTableView.AnimationOptions = [.effectFade, .slideLeft]) -> Disposable {
+ if animated {
+ let binder = TableViewBinderDataSource()
+ binder.rowInsertionAnimation = rowAnimation
+ binder.rowRemovalAnimation = rowAnimation
+ return bind(to: tableView, using: binder)
+ } else {
+ let binder = TableViewBinderDataSource.ReloadingBinder()
+ return bind(to: tableView, using: binder)
+ }
}
- }
- /// Binds the signal of data source elements to the given table view.
- ///
- /// - parameters:
- /// - tableView: A table view that should display the data from the data source.
- /// - binder: A `TableViewBinder` or its subclass that will manage the binding.
- /// - returns: A disposable object that can terminate the binding. Safe to ignore - the binding will be automatically terminated when the table view is deallocated.
- @discardableResult
- public func bind(to tableView: NSTableView, using binder: TableViewBinderDataSource) -> Disposable {
- binder.tableView = tableView
- return bind(to: tableView) { (_, changeset) in
- binder.changeset = changeset.asFlatDataSourceChangeset
+ /// Binds the signal of data source elements to the given table view.
+ ///
+ /// - parameters:
+ /// - tableView: A table view that should display the data from the data source.
+ /// - binder: A `TableViewBinder` or its subclass that will manage the binding.
+ /// - returns: A disposable object that can terminate the binding. Safe to ignore - the binding will be automatically terminated when the table view is deallocated.
+ @discardableResult
+ public func bind(to tableView: NSTableView, using binder: TableViewBinderDataSource) -> Disposable {
+ binder.tableView = tableView
+ return bind(to: tableView) { _, changeset in
+ binder.changeset = changeset.asFlatDataSourceChangeset
+ }
}
}
-}
#endif
diff --git a/Sources/Bond/AppKit/NSTableView.swift b/Sources/Bond/AppKit/NSTableView.swift
index 94980b04..0aa783c0 100644
--- a/Sources/Bond/AppKit/NSTableView.swift
+++ b/Sources/Bond/AppKit/NSTableView.swift
@@ -8,44 +8,43 @@
#if os(macOS)
-import AppKit
-import ReactiveKit
-
-extension ReactiveExtensions where Base: NSTableView {
-
- /// A `ProtocolProxy` for the table view delegate.
- ///
- /// - Note: Accessing this property for the first time will replace table view's current delegate
- /// with a protocol proxy object (an object that is stored in this property).
- /// Current delegate will be used as `forwardTo` delegate of protocol proxy.
- public var delegate: ProtocolProxy {
- return protocolProxy(for: NSTableViewDelegate.self, keyPath: \.delegate)
+ import AppKit
+ import ReactiveKit
+
+ extension ReactiveExtensions where Base: NSTableView {
+ /// A `ProtocolProxy` for the table view delegate.
+ ///
+ /// - Note: Accessing this property for the first time will replace table view's current delegate
+ /// with a protocol proxy object (an object that is stored in this property).
+ /// Current delegate will be used as `forwardTo` delegate of protocol proxy.
+ public var delegate: ProtocolProxy {
+ return protocolProxy(for: NSTableViewDelegate.self, keyPath: \.delegate)
+ }
+
+ /// A `ProtocolProxy` for the table view data source.
+ ///
+ /// - Note: Accessing this property for the first time will replace table view's current data source
+ /// with a protocol proxy object (an object that is stored in this property).
+ /// Current data source will be used as `forwardTo` data source of protocol proxy.
+ public var dataSource: ProtocolProxy {
+ return protocolProxy(for: NSTableViewDataSource.self, keyPath: \.dataSource)
+ }
+
+ public var selectionIsChanging: SafeSignal {
+ return NotificationCenter.default.reactive.notification(name: NSTableView.selectionIsChangingNotification, object: base).eraseType()
+ }
+
+ public var selectionDidChange: SafeSignal {
+ return NotificationCenter.default.reactive.notification(name: NSTableView.selectionDidChangeNotification, object: base).eraseType()
+ }
+
+ public var selectedRowIndexes: Bond {
+ return bond { $0.selectRowIndexes($1, byExtendingSelection: false) }
+ }
+
+ public var selectedColumnIndexes: Bond {
+ return bond { $0.selectColumnIndexes($1, byExtendingSelection: false) }
+ }
}
- /// A `ProtocolProxy` for the table view data source.
- ///
- /// - Note: Accessing this property for the first time will replace table view's current data source
- /// with a protocol proxy object (an object that is stored in this property).
- /// Current data source will be used as `forwardTo` data source of protocol proxy.
- public var dataSource: ProtocolProxy {
- return protocolProxy(for: NSTableViewDataSource.self, keyPath: \.dataSource)
- }
-
- public var selectionIsChanging: SafeSignal