Extends `UIView` with `rx_alpha`, `rx_hidden`. `NSLayoutConstraint` with `rx_constant`.
This commit is contained in:
parent
a0c68de05d
commit
b08c1a5969
|
|
@ -0,0 +1,43 @@
|
|||
//
|
||||
// NSLayoutConstraint+Rx.swift
|
||||
// Rx
|
||||
//
|
||||
// Created by Krunoslav Zaher on 12/6/15.
|
||||
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
#if os(OSX)
|
||||
import Cocoa
|
||||
#else
|
||||
import UIKit
|
||||
#endif
|
||||
|
||||
#if !RX_NO_MODULE
|
||||
import RxSwift
|
||||
#endif
|
||||
|
||||
#if os(iOS) || os(OSX) || os(tvOS)
|
||||
extension NSLayoutConstraint {
|
||||
/**
|
||||
Bindable sink for `constant` property.
|
||||
*/
|
||||
public var rx_constant: AnyObserver<CGFloat> {
|
||||
return AnyObserver { [weak self] event in
|
||||
MainScheduler.ensureExecutingOnScheduler()
|
||||
|
||||
switch event {
|
||||
case .Next(let value):
|
||||
self?.constant = value
|
||||
case .Error(let error):
|
||||
bindingErrorToInterface(error)
|
||||
break
|
||||
case .Completed:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
//
|
||||
// NSView+Rx.swift
|
||||
// Rx
|
||||
//
|
||||
// Created by Krunoslav Zaher on 12/6/15.
|
||||
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Cocoa
|
||||
#if !RX_NO_MODULE
|
||||
import RxSwift
|
||||
#endif
|
||||
|
||||
extension NSView {
|
||||
/**
|
||||
Bindable sink for `hidden` property.
|
||||
*/
|
||||
public var rx_hidden: AnyObserver<Bool> {
|
||||
return AnyObserver { [weak self] event in
|
||||
MainScheduler.ensureExecutingOnScheduler()
|
||||
|
||||
switch event {
|
||||
case .Next(let value):
|
||||
self?.hidden = value
|
||||
case .Error(let error):
|
||||
bindingErrorToInterface(error)
|
||||
break
|
||||
case .Completed:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Bindable sink for `alphaValue` property.
|
||||
*/
|
||||
public var rx_alpha: AnyObserver<CGFloat> {
|
||||
return AnyObserver { [weak self] event in
|
||||
MainScheduler.ensureExecutingOnScheduler()
|
||||
|
||||
switch event {
|
||||
case .Next(let value):
|
||||
self?.alphaValue = value
|
||||
case .Error(let error):
|
||||
bindingErrorToInterface(error)
|
||||
break
|
||||
case .Completed:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -34,7 +34,7 @@ extension UIControl {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Reactive wrapper for target action pattern.
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,57 @@
|
|||
//
|
||||
// UIView+Rx.swift
|
||||
// Rx
|
||||
//
|
||||
// Created by Krunoslav Zaher on 12/6/15.
|
||||
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
|
||||
//
|
||||
|
||||
#if os(iOS) || os(tvOS)
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
#if !RX_NO_MODULE
|
||||
import RxSwift
|
||||
#endif
|
||||
|
||||
extension UIView {
|
||||
/**
|
||||
Bindable sink for `hidden` property.
|
||||
*/
|
||||
public var rx_hidden: AnyObserver<Bool> {
|
||||
return AnyObserver { [weak self] event in
|
||||
MainScheduler.ensureExecutingOnScheduler()
|
||||
|
||||
switch event {
|
||||
case .Next(let value):
|
||||
self?.hidden = value
|
||||
case .Error(let error):
|
||||
bindingErrorToInterface(error)
|
||||
break
|
||||
case .Completed:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Bindable sink for `alpha` property.
|
||||
*/
|
||||
public var rx_alpha: AnyObserver<CGFloat> {
|
||||
return AnyObserver { [weak self] event in
|
||||
MainScheduler.ensureExecutingOnScheduler()
|
||||
|
||||
switch event {
|
||||
case .Next(let value):
|
||||
self?.alpha = value
|
||||
case .Error(let error):
|
||||
bindingErrorToInterface(error)
|
||||
break
|
||||
case .Completed:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
//
|
||||
// ValidationService.swift
|
||||
// RxExample
|
||||
//
|
||||
// Created by Krunoslav Zaher on 12/6/15.
|
||||
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import RxSwift
|
||||
|
||||
class ValidationService {
|
||||
let API: GitHubAPI
|
||||
|
||||
init (API: GitHubAPI) {
|
||||
self.API = API
|
||||
}
|
||||
|
||||
// validation
|
||||
|
||||
let minPasswordCount = 5
|
||||
|
||||
func validateUsername(username: String) -> ValidationObservable {
|
||||
if username.characters.count == 0 {
|
||||
return just((false, nil))
|
||||
}
|
||||
|
||||
// this obviously won't be
|
||||
if username.rangeOfCharacterFromSet(NSCharacterSet.alphanumericCharacterSet().invertedSet) != nil {
|
||||
return just((false, "Username can only contain numbers or digits"))
|
||||
}
|
||||
|
||||
let loadingValue = (valid: nil as Bool?, message: "Checking availabilty ..." as String?)
|
||||
|
||||
return API.usernameAvailable(username)
|
||||
.map { available in
|
||||
if available {
|
||||
return (true, "Username available")
|
||||
}
|
||||
else {
|
||||
return (false, "Username already taken")
|
||||
}
|
||||
}
|
||||
.startWith(loadingValue)
|
||||
}
|
||||
|
||||
func validatePassword(password: String) -> ValidationResult {
|
||||
let numberOfCharacters = password.characters.count
|
||||
if numberOfCharacters == 0 {
|
||||
return (false, nil)
|
||||
}
|
||||
|
||||
if numberOfCharacters < minPasswordCount {
|
||||
return (false, "Password must be at least \(minPasswordCount) characters")
|
||||
}
|
||||
|
||||
return (true, "Password acceptable")
|
||||
}
|
||||
|
||||
func validateRepeatedPassword(password: String, repeatedPassword: String) -> ValidationResult {
|
||||
if repeatedPassword.characters.count == 0 {
|
||||
return (false, nil)
|
||||
}
|
||||
|
||||
if repeatedPassword == password {
|
||||
return (true, "Password repeated")
|
||||
}
|
||||
else {
|
||||
return (false, "Password different")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
//
|
||||
// NumbersViewController.swift
|
||||
// RxExample
|
||||
//
|
||||
// Created by Krunoslav Zaher on 12/6/15.
|
||||
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
//
|
||||
// SimpleValidation.swift
|
||||
// RxExample
|
||||
//
|
||||
// Created by Krunoslav Zaher on 12/6/15.
|
||||
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
import RxSwift
|
||||
import RxCocoa
|
||||
|
||||
let mininalUsernameLength = 5
|
||||
let mininalPasswordLength = 5
|
||||
|
||||
class SimpleValidationViewController : ViewController {
|
||||
|
||||
@IBOutlet weak var username: UITextField!
|
||||
@IBOutlet weak var usernameValid: UILabel!
|
||||
|
||||
@IBOutlet weak var password: UITextField!
|
||||
@IBOutlet weak var passwordValid: UILabel!
|
||||
|
||||
@IBOutlet weak var doSomething: UIButton!
|
||||
|
||||
func showAlert() {
|
||||
DefaultWireframe.sharedInstance.promptFor("Something wonderful has been done", cancelAction: "OK", actions: [])
|
||||
}
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
let usernameValid = username.rx_text
|
||||
.map { $0.characters.count >= mininalUsernameLength }
|
||||
.shareReplay(1) // without this map would be executed once for each binding, rx is stateless by default
|
||||
|
||||
let passwordValid = password.rx_text
|
||||
.map { $0.characters.count >= mininalPasswordLength }
|
||||
.shareReplay(1)
|
||||
|
||||
let everythingValid = combineLatest(usernameValid, passwordValid) { $0 && $1 }
|
||||
.shareReplay(1)
|
||||
|
||||
|
||||
|
||||
usernameValid
|
||||
.bindTo(password.rx_enabled)
|
||||
.addDisposableTo(disposeBag)
|
||||
|
||||
everythingValid
|
||||
.bindTo(doSomething.rx_enabled)
|
||||
.addDisposableTo(disposeBag)
|
||||
|
||||
doSomething.rx_tap
|
||||
.subscribeNext(showAlert)
|
||||
.addDisposableTo(disposeBag)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
//
|
||||
// Operators.swift
|
||||
// RxExample
|
||||
//
|
||||
// Created by Krunoslav Zaher on 12/6/15.
|
||||
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
//
|
||||
// NSLayoutConstraint+RxTests.swift
|
||||
// RxTests
|
||||
//
|
||||
// Created by Krunoslav Zaher on 12/6/15.
|
||||
//
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
//
|
||||
// NSView+RxTests.swift
|
||||
// RxTests
|
||||
//
|
||||
// Created by Krunoslav Zaher on 12/6/15.
|
||||
//
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
//
|
||||
// RxCocoaTests.swift
|
||||
// RxTests
|
||||
//
|
||||
// Created by Krunoslav Zaher on 7/6/15.
|
||||
//
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import XCTest
|
||||
import RxSwift
|
||||
import RxCocoa
|
||||
|
||||
class RxCocoaTest : RxTest {
|
||||
}
|
||||
|
|
@ -1,80 +0,0 @@
|
|||
//
|
||||
// UI+RxTests.swift
|
||||
// RxTests
|
||||
//
|
||||
// Created by Krunoslav Zaher on 5/3/15.
|
||||
//
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import RxSwift
|
||||
import RxCocoa
|
||||
import XCTest
|
||||
|
||||
class UITextFieldMock {
|
||||
|
||||
let observableText = Variable<String>("")
|
||||
|
||||
var text: String! = "" {
|
||||
didSet {
|
||||
observableText.value = self.text
|
||||
}
|
||||
}
|
||||
|
||||
func rx_text() -> Observable<String> {
|
||||
return observableText.asObservable()
|
||||
}
|
||||
}
|
||||
|
||||
extension ObservableType where E == String {
|
||||
func subscribeTextOf(label: UILabelMock) -> Disposable {
|
||||
return self.subscribeNext { t in
|
||||
label.text = t
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class UILabelMock {
|
||||
var text: String! = ""
|
||||
|
||||
}
|
||||
|
||||
class UIRxTests : RxTest {
|
||||
|
||||
func testArea() {
|
||||
}
|
||||
|
||||
func testReadmeExample() {
|
||||
|
||||
// We have some async Wolfram Alpha API that calculates is number prime.
|
||||
let WolframAlphaIsPrime: (Int) -> Observable<PrimeNumber> = { just(PrimeNumber($0, isPrime($0))) }
|
||||
|
||||
let primeTextField = UITextFieldMock()
|
||||
|
||||
let resultLabel = UILabelMock()
|
||||
|
||||
let _ = primeTextField.rx_text()
|
||||
.map { WolframAlphaIsPrime(Int($0) ?? 0) }
|
||||
.concat()
|
||||
.map { "number \($0.n) is prime? \($0.isPrime)" }
|
||||
.subscribeTextOf(resultLabel)
|
||||
.scopedDispose()
|
||||
|
||||
// this will set resultLabel.text! == "number 43 is prime? true"
|
||||
primeTextField.text = "43"
|
||||
}
|
||||
}
|
||||
|
||||
struct PrimeNumber : Equatable {
|
||||
let n: Int
|
||||
let isPrime: Bool
|
||||
|
||||
init(_ n: Int, _ isPrime: Bool) {
|
||||
self.n = n
|
||||
self.isPrime = isPrime
|
||||
}
|
||||
}
|
||||
|
||||
func == (lhs: PrimeNumber, rhs: PrimeNumber) -> Bool {
|
||||
return lhs.n == rhs.n && lhs.isPrime == rhs.isPrime
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
//
|
||||
// UIView+RxTests.swift
|
||||
// RxTests
|
||||
//
|
||||
// Created by Krunoslav Zaher on 12/6/15.
|
||||
//
|
||||
//
|
||||
|
||||
import Foundation
|
||||
Loading…
Reference in New Issue