Skip to content

Commit

Permalink
Merge branch 'master' into bond-7
Browse files Browse the repository at this point in the history
  • Loading branch information
srdanrasic committed Sep 30, 2018
2 parents ee0c618 + bb658df commit bb086e7
Show file tree
Hide file tree
Showing 18 changed files with 216 additions and 48 deletions.
2 changes: 1 addition & 1 deletion .swift-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
4.0
4.2
17 changes: 10 additions & 7 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
language: swift
osx_image: xcode9.4
osx_image: xcode10

script:
- set -o pipefail && xcodebuild clean test -project Bond.xcodeproj -scheme Bond-iOS -destination "platform=iOS Simulator,name=iPhone 7,OS=11.4" CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO ONLY_ACTIVE_ARCH=NO -quiet | xcpretty
- swift test -Xswiftc "-target" -Xswiftc "x86_64-apple-macosx10.11"

after_success:
- bash <(curl -s https://codecov.io/bash)
jobs:
include:
- stage: "Tests"
name: "Run Xcode Tests"
script:
- set -o pipefail && xcodebuild clean test -project Bond.xcodeproj -scheme Bond-iOS -destination "platform=iOS Simulator,name=iPhone 7,OS=11.4" CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO ONLY_ACTIVE_ARCH=NO -quiet | xcpretty
after_success: bash <(curl -s https://codecov.io/bash)
- script: swift test
name: "Run Swift Package Manager Tests"
2 changes: 1 addition & 1 deletion Bond-App/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {

var window: UIWindow?

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
window = UIWindow(frame: UIScreen.main.bounds)
window?.rootViewController = ViewController()
window?.makeKeyAndVisible()
Expand Down
6 changes: 3 additions & 3 deletions Bond.podspec
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Pod::Spec.new do |s|

s.name = "Bond"
s.version = "6.8.3"
s.version = "6.10.0"
s.summary = "A Swift binding framework"

s.description = <<-DESC
Expand All @@ -21,7 +21,7 @@ Pod::Spec.new do |s|
s.ios.deployment_target = "8.0"
s.osx.deployment_target = "10.11"
s.tvos.deployment_target = "9.0"
s.source = { :git => "https://github.com/SwiftBond/Bond.git", :tag => "6.8.3" }
s.source = { :git => "https://github.com/SwiftBond/Bond.git", :tag => "6.10.0" }
s.source_files = "Sources/**/*.{h,m,swift}", "Supporting Files/Bond.h"
s.ios.exclude_files = "Sources/Bond/AppKit"
s.tvos.exclude_files = "Sources/Bond/AppKit"
Expand All @@ -30,6 +30,6 @@ Pod::Spec.new do |s|
s.requires_arc = true

s.dependency "ReactiveKit", "~> 3.9"
s.dependency "Differ", "~> 1.2"
s.dependency "Differ", "~> 1.3"

end
8 changes: 6 additions & 2 deletions Bond.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
16887E3A1D744ABB00EDA099 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 16887E391D744ABB00EDA099 /* AppDelegate.swift */; };
16887E441D744ABB00EDA099 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 16887E421D744ABB00EDA099 /* LaunchScreen.storyboard */; };
75CA9E9220678E600011E5BB /* UISearchBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75CA9E9120678E600011E5BB /* UISearchBar.swift */; };
901E85A1214A125A00F03A80 /* NSGestureRecognizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 901E85A0214A125A00F03A80 /* NSGestureRecognizer.swift */; };
9025DCAB1F981693007B7689 /* Property.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC0220091F8BE898002F8380 /* Property.swift */; };
9025DCB01F981694007B7689 /* Property.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC0220091F8BE898002F8380 /* Property.swift */; };
9025DCB11F9816A7007B7689 /* UIAccessibilityIdentification.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC86E9AE1E9145CC008563B2 /* UIAccessibilityIdentification.swift */; };
Expand Down Expand Up @@ -364,6 +365,7 @@
90677C10207EE44B00BC4505 /* TreeNodeProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TreeNodeProtocol.swift; sourceTree = "<group>"; };
90677C3C207F38AE00BC4505 /* NSOutlineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSOutlineView.swift; sourceTree = "<group>"; };
90677C44207F391700BC4505 /* NSOutlineView+ObservableTree.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSOutlineView+ObservableTree.swift"; sourceTree = "<group>"; };
901E85A0214A125A00F03A80 /* NSGestureRecognizer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSGestureRecognizer.swift; sourceTree = "<group>"; };
90A3FAC21E0C8D8B0086A7F1 /* Differ.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Differ.xcodeproj; path = Carthage/Checkouts/Differ/Differ.xcodeproj; sourceTree = "<group>"; };
90C04D021E8F0A97000077C8 /* Bond-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Bond-Info.plist"; sourceTree = "<group>"; };
90C04D031E8F0A97000077C8 /* Bond.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Bond.h; sourceTree = "<group>"; };
Expand Down Expand Up @@ -674,6 +676,7 @@
90C04D261E8F0B1D000077C8 /* NSButton.swift */,
90C04D271E8F0B1D000077C8 /* NSColorWell.swift */,
90C04D281E8F0B1D000077C8 /* NSControl.swift */,
901E85A0214A125A00F03A80 /* NSGestureRecognizer.swift */,
90C04D291E8F0B1D000077C8 /* NSImageView.swift */,
90C04D2B1E8F0B1D000077C8 /* NSMenuItem.swift */,
90677C3C207F38AE00BC4505 /* NSOutlineView.swift */,
Expand Down Expand Up @@ -1073,6 +1076,7 @@
75CA9E9220678E600011E5BB /* UISearchBar.swift in Sources */,
90C04DC31E8F0B97000077C8 /* UITextView.swift in Sources */,
90A443131E9055D200D611FE /* NSTextView.swift in Sources */,
901E85A1214A125A00F03A80 /* NSGestureRecognizer.swift in Sources */,
90C04DB21E8F0B97000077C8 /* UIButton.swift in Sources */,
070FE2741F0138180031B7BD /* NSLayoutConstraint.swift in Sources */,
90C04DB61E8F0B97000077C8 /* UIGestureRecognizer.swift in Sources */,
Expand Down Expand Up @@ -1397,7 +1401,7 @@
SDKROOT = iphoneos;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG BUILDING_WITH_XCODE";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 4.0;
SWIFT_VERSION = 4.2;
SYSTEM_FRAMEWORK_SEARCH_PATHS = "";
TARGETED_DEVICE_FAMILY = "1,2";
USER_HEADER_SEARCH_PATHS = "";
Expand Down Expand Up @@ -1455,7 +1459,7 @@
SDKROOT = iphoneos;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = BUILDING_WITH_XCODE;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
SWIFT_VERSION = 4.0;
SWIFT_VERSION = 4.2;
SYSTEM_FRAMEWORK_SEARCH_PATHS = "";
TARGETED_DEVICE_FAMILY = "1,2";
USER_HEADER_SEARCH_PATHS = "";
Expand Down
2 changes: 1 addition & 1 deletion Cartfile
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
github "DeclarativeHub/ReactiveKit" ~> 3.9
github "tonyarnold/Differ" ~> 1.2
github "tonyarnold/Differ" ~> 1.3
10 changes: 5 additions & 5 deletions Documentation/Bindings.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Bindings

Binding is a connection between a signal that produces events and a *bond* object that observers events and performs certain actions (e.g. updates UI).
Binding is a connection between a signal that produces events and a *bond* object that observes events and performs certain actions (e.g. updates UI).

The producing side of bindings are signals that are defined in ReactiveKit framework on top of which Bond is built. To learn more about signals, consult [ReactiveKit documentation](https://github.com/ReactiveKit/ReactiveKit).

Expand All @@ -12,15 +12,15 @@ public struct Bond<Element>: BindableProtocol {
}
```

The only requirement is that the target must be *Deallocatable*, in other words that it provides a signal of its own deallocation.
The only requirement is that the target must be *Deallocatable*, in other words that it provides a signal for its own deallocation.

```swift
public protocol Deallocatable: class {
var deallocated: Signal<Void, NoError> { get }
}
```

All `NSObject` subclasses conform to that protocol out of the box. Let us see how we could implement a Bond for text property of a label. It is recommended to implement reactive extensions on `ReactiveExtensions` proxy protocol. That way you encapsulate extensions within the `.reactive` property.
All `NSObject` subclasses conform to that protocol out of the box. Let us see how we could implement a Bond for text property of a label. It is recommended to implement reactive extensions through the `ReactiveExtensions` proxy protocol. That way you encapsulate extensions within the `.reactive` property.

```swift
extension ReactiveExtensions where Base: UILabel {
Expand Down Expand Up @@ -72,7 +72,7 @@ presentUserProfile.observeOn(.main).observeNext { [weak self] user in

But that's ugly! We have to dispatch everything to the main queue, be cautious not to create a retain cycle and ensure that the disposable we get from the observation is handled.

Thankfully Bond provides a better way. We can create inline binding instead of the observation. Just do the following
Thankfully Bond provides a better way. We can create an inline binding instead of the observation. Just do the following

```swift
presentUserProfile.bind(to: self) { me, user in
Expand All @@ -81,4 +81,4 @@ presentUserProfile.bind(to: self) { me, user in
}
```

and stop worrying about threading, retain cycles and disposing because bindings take care of all that automatically. Just bind a signal to the target responsible for performing side effects (in our example, to the object responsible for presenting a profile view controller). The closure you provide will be called whenever the signal emits an event with both the target and the sent element as arguments.
and stop worrying about threading, retain cycles and disposing, because bindings take care of all that automatically. Just bind a signal to the target responsible for performing side effects (in our example, to the object responsible for presenting a profile view controller). The closure you provide will be called whenever the signal emits an event with both the target and the sent element as arguments.
2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ let package = Package(
],
dependencies: [
.package(url: "https://github.com/ReactiveKit/ReactiveKit.git", .upToNextMajor(from: "3.9.0")),
.package(url: "https://github.com/tonyarnold/Differ.git", .upToNextMajor(from: "1.2.0"))
.package(url: "https://github.com/tonyarnold/Differ.git", .upToNextMajor(from: "1.3.0"))
],
targets: [
.target(name: "BNDProtocolProxyBase"),
Expand Down
4 changes: 1 addition & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

[![Platform](https://img.shields.io/cocoapods/p/Bond.svg?style=flat)](http://cocoadocs.org/docsets/Bond/)
[![CI Status](https://travis-ci.org/DeclarativeHub/Bond.svg?branch=master)](https://travis-ci.org/DeclarativeHub/Bond)
[![Join Us on Gitter](https://img.shields.io/badge/GITTER-join%20chat-blue.svg)](https://gitter.im/ReactiveKit/General)
[![Twitter](https://img.shields.io/badge/[email protected]?style=flat)](https://twitter.com/srdanrasic)

<br>
Expand Down Expand Up @@ -186,8 +185,7 @@ Bond uses protocol proxies to implement table and collection view bindings and t

## Communication

* If you'd like to ask a general question, use Stack Overflow.
* If you'd like to ask a quick question or chat about the project, try [Gitter](https://gitter.im/ReactiveKit/General).
* If you'd like to ask a question, open an issue.
* If you found a bug, open an issue.
* If you have a feature request, open an issue.
* If you want to contribute, submit a pull request (include unit tests).
Expand Down
12 changes: 6 additions & 6 deletions Sources/Bond/AppKit/NSControl.swift
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ public extension ReactiveExtensions where Base: NSControl {

public var objectValue: DynamicSubject<Any?> {
return dynamicSubject(
signal: keyPath(#keyPath(NSControl.objectValue), ofType: Optional<Any>.self).eraseType(),
signal: controlEvent.eraseType(),
triggerEventOnSetting: false,
get: { $0.objectValue },
set: { $0.objectValue = $1 }
Expand All @@ -103,7 +103,7 @@ public extension ReactiveExtensions where Base: NSControl {

public var stringValue: DynamicSubject<String> {
return dynamicSubject(
signal: keyPath(#keyPath(NSControl.stringValue), ofType: String.self).eraseType(),
signal: objectValue.eraseType(),
triggerEventOnSetting: false,
get: { $0.stringValue },
set: { $0.stringValue = $1 }
Expand All @@ -112,7 +112,7 @@ public extension ReactiveExtensions where Base: NSControl {

public var attributedStringValue: DynamicSubject<NSAttributedString> {
return dynamicSubject(
signal: keyPath(#keyPath(NSControl.attributedStringValue), ofType: NSAttributedString.self).eraseType(),
signal: objectValue.eraseType(),
triggerEventOnSetting: false,
get: { $0.attributedStringValue },
set: { $0.attributedStringValue = $1 }
Expand All @@ -121,7 +121,7 @@ public extension ReactiveExtensions where Base: NSControl {

public var integerValue: DynamicSubject<Int> {
return dynamicSubject(
signal: keyPath(#keyPath(NSControl.integerValue), ofType: Int.self).eraseType(),
signal: objectValue.eraseType(),
triggerEventOnSetting: false,
get: { $0.integerValue },
set: { $0.integerValue = $1 }
Expand All @@ -130,7 +130,7 @@ public extension ReactiveExtensions where Base: NSControl {

public var floatValue: DynamicSubject<Float> {
return dynamicSubject(
signal: keyPath(#keyPath(NSControl.floatValue), ofType: Float.self).eraseType(),
signal: objectValue.eraseType(),
triggerEventOnSetting: false,
get: { $0.floatValue },
set: { $0.floatValue = $1 }
Expand All @@ -139,7 +139,7 @@ public extension ReactiveExtensions where Base: NSControl {

public var doubleValue: DynamicSubject<Double> {
return dynamicSubject(
signal: keyPath(#keyPath(NSControl.doubleValue), ofType: Double.self).eraseType(),
signal: objectValue.eraseType(),
triggerEventOnSetting: false,
get: { $0.doubleValue },
set: { $0.doubleValue = $1 }
Expand Down
135 changes: 135 additions & 0 deletions Sources/Bond/AppKit/NSGestureRecognizer.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
//
// The MIT License (MIT)
//
// Copyright (c) 2016 Tony Arnold (@tonyarnold)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//

#if os(macOS)

import AppKit
import ReactiveKit

extension NSGestureRecognizer: BindingExecutionContextProvider {
public var bindingExecutionContext: ExecutionContext { return .immediateOnMain }
}

public extension ReactiveExtensions where Base: NSGestureRecognizer {
public var isEnabled: Bond<Bool> {
return bond { $0.isEnabled = $1 }
}
}

public extension ReactiveExtensions where Base: NSView {
public func addGestureRecognizer<T: NSGestureRecognizer>(_ gestureRecognizer: T) -> SafeSignal<T> {
let base = self.base
return Signal { [weak base] observer in
guard let base = base else {
observer.completed()
return NonDisposable.instance
}
let target = BNDGestureTarget(view: base, gestureRecognizer: gestureRecognizer) { recog in
// swiftlint:disable:next force_cast
observer.next(recog as! T)
}
return BlockDisposable {
target.unregister()
}
}
.take(until: base.deallocated)
}

public func clickGesture(numberOfClicks: Int = 1, numberOfTouches: Int = 1) -> SafeSignal<NSClickGestureRecognizer> {
let gesture = NSClickGestureRecognizer()
gesture.numberOfClicksRequired = numberOfClicks

if #available(macOS 10.12.2, *) {
gesture.numberOfTouchesRequired = numberOfTouches
}

return addGestureRecognizer(gesture)
}

public func magnificationGesture(magnification: CGFloat = 1.0, numberOfTouches: Int = 1) -> SafeSignal<NSMagnificationGestureRecognizer> {
let gesture = NSMagnificationGestureRecognizer()
gesture.magnification = magnification
return addGestureRecognizer(gesture)
}

public func panGesture(buttonMask: Int = 0x01, numberOfTouches: Int = 1) -> SafeSignal<NSPanGestureRecognizer> {
let gesture = NSPanGestureRecognizer()
gesture.buttonMask = buttonMask

if #available(macOS 10.12.2, *) {
gesture.numberOfTouchesRequired = numberOfTouches
}

return addGestureRecognizer(gesture)
}

public func pressGesture(buttonMask: Int = 0x01, minimumPressDuration: TimeInterval = NSEvent.doubleClickInterval, allowableMovement: CGFloat = 10.0, numberOfTouches: Int = 1) -> SafeSignal<NSPressGestureRecognizer> {
let gesture = NSPressGestureRecognizer()
gesture.buttonMask = buttonMask
gesture.minimumPressDuration = minimumPressDuration
gesture.allowableMovement = allowableMovement

if #available(macOS 10.12.2, *) {
gesture.numberOfTouchesRequired = numberOfTouches
}

return addGestureRecognizer(gesture)
}

public func rotationGesture() -> SafeSignal<NSRotationGestureRecognizer> {
return addGestureRecognizer(NSRotationGestureRecognizer())
}
}

@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

super.init()

gestureRecognizer.target = self
gestureRecognizer.action = #selector(actionHandler(recogniser:))
view.addGestureRecognizer(gestureRecognizer)
}

@objc private func actionHandler(recogniser: NSGestureRecognizer) {
observer(recogniser)
}

fileprivate func unregister() {
view?.removeGestureRecognizer(gestureRecognizer)
}

deinit {
unregister()
}
}

#endif
Loading

0 comments on commit bb086e7

Please sign in to comment.