Merge pull request #12 from dtartaglia/develop
Create UIControl. Set to handle all possible events. Added OSXPlayground Documentation
This commit is contained in:
commit
ba947464ca
|
|
@ -0,0 +1,148 @@
|
|||
import Cocoa
|
||||
import RxSwift
|
||||
|
||||
/*:
|
||||
## Why use RxSwift?
|
||||
A vast majority of the code we write revolves around responding to external actions. When a user manipulates a control, we need to write an @IBAction to respond to that. We need to observe Notifications to detect when the keyboard changes position. We must provide blocks to execute when URL Sessions respond with data. And we use KVO to detect changes in variables.
|
||||
All of these various systems makes our code needlessly complex. Wouldn't it be better if there was one consistant system that handled all of our call/response code? Rx is such a system.
|
||||
|
||||
## Observables
|
||||
The key to understanding RxSwift is in understanding the notion of Observables. Creating them, manipulating them, and subscribing to them in order to react to changes.
|
||||
|
||||
## Creating and Subscribing to Observables
|
||||
The first step in understanding this library is in understanding how to create Observables. There are a number of functions available to make Observables.
|
||||
Creating an Observable is one thing, but if nothing subscribes to the observable, then nothing will come of it so both are expalined simultaniously.
|
||||
*/
|
||||
|
||||
/*:
|
||||
### empty
|
||||
`empty` creates an observable that contains no objects. The only message it sends is the `.Completed` message.
|
||||
*/
|
||||
|
||||
let emptyObservable: Observable<Int> = empty()
|
||||
|
||||
let emptySubscriber = emptyObservable >- subscribe { event in
|
||||
switch event {
|
||||
case .Next(let box):
|
||||
println("\(box.value)")
|
||||
case .Completed:
|
||||
println("completed")
|
||||
case .Error(let error):
|
||||
println("\(error)")
|
||||
}
|
||||
}
|
||||
|
||||
/*:
|
||||
As you can see, no values are ever sent to the subscriber of an empty observable. It just completes and is done.
|
||||
*/
|
||||
|
||||
/*:
|
||||
### never
|
||||
`never` creates an observable that contains no objects and never completes or errors out.
|
||||
*/
|
||||
|
||||
let neverObservable: Observable<String> = never()
|
||||
|
||||
let neverSubscriber = neverObservable >- subscribe { _ in
|
||||
println("This block is never called.")
|
||||
}
|
||||
|
||||
/*:
|
||||
### returnElement/just
|
||||
These two functions behave identically. They send two messages to subscribers. The first message is the value and the second message is `.Complete`.
|
||||
*/
|
||||
|
||||
let oneObservable = just(32)
|
||||
|
||||
let oneObservableSubscriber = oneObservable >- subscribe { event in
|
||||
switch event {
|
||||
case .Next(let box):
|
||||
println("\(box.value)")
|
||||
case .Completed:
|
||||
println("completed")
|
||||
case .Error(let error):
|
||||
println("\(error)")
|
||||
}
|
||||
}
|
||||
|
||||
/*:
|
||||
Here we see that the `.Next` event is sent just once, then the `.Completed` event is sent.
|
||||
*/
|
||||
|
||||
/*:
|
||||
### returnElements
|
||||
Now we are getting to some more interesting ways to create an Observable. This function creates an observable that produces a number of values before completing.
|
||||
*/
|
||||
|
||||
let multipleObservable = returnElements(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
|
||||
|
||||
let multipleObservableSubscriber = multipleObservable >- subscribe { event in
|
||||
switch event {
|
||||
case .Next(let box):
|
||||
println("\(box.value)")
|
||||
case .Completed:
|
||||
println("completed")
|
||||
case .Error(let error):
|
||||
println("\(error)")
|
||||
}
|
||||
}
|
||||
|
||||
/*:
|
||||
With the above, you will see that the `.Next` event was sent four times, once for each element. Then `.Complete` was sent.
|
||||
|
||||
-
|
||||
Now these functions are all well and good, but the really useful ones are in the RxCocoa library.
|
||||
`rx_observe` exist on every NSObject and wraps KVO.
|
||||
`rx_tap` exists on buttons and wraps @IBActions
|
||||
`rx_notification` wraps NotificationCenter events
|
||||
... and so on.
|
||||
|
||||
Take some time and search for code matching `-> Observable` in the RxCocoa framework to get a sense of how every action can be modeled as an observable. You can even create your own functions that make Observable objects.
|
||||
|
||||
## Subscribing
|
||||
Up to this point, I have only used the `subscribe` method to listen to Observables, but there are several others.
|
||||
*/
|
||||
|
||||
let nextOnlySubscriber = multipleObservable >- subscribeNext { value in
|
||||
println("\(value)")
|
||||
}
|
||||
|
||||
/*:
|
||||
With the above we only interest ourselves in the values returned by the observable without regard to whether/when it completes or errors. Many of the observables that we use have an indefinite lifespan. There is also `subscribeCompleted` and `subscribeError` for when you are looking for when an observable will stop sending.
|
||||
|
||||
Also note that you can have multiple subscribers following to the same observable (as I did in the example above.) All the subscribers will be notified when an event occurs.
|
||||
*/
|
||||
|
||||
/*:
|
||||
## Reducing a sequence
|
||||
Now that you understand how to create Observables and subscribe to them. Let's look at the various ways we can manipulate an observable sequence. First lets examine ways to reduce a sequence into fewer events.
|
||||
|
||||
### where/filter
|
||||
The most common way to reduce a sequence is to apply a filter to it and the most generic of these is `where` or `filter`. You will see in the code below that the messages containing odd numbers are being removed before the subscriber can see them.
|
||||
*/
|
||||
|
||||
var onlyEvensSubscriber = multipleObservable
|
||||
>- filter {
|
||||
$0 % 2 == 0
|
||||
}
|
||||
>- subscribeNext { value in
|
||||
println("\(value)")
|
||||
}
|
||||
|
||||
/*:
|
||||
### distinctUntilChanged
|
||||
This filter tracks the last value emitted and removes like values. This function is good for reducing noise in a sequence.
|
||||
*/
|
||||
let debugSubscriber = returnElements(1, 2, 3, 1, 1, 4)
|
||||
>- distinctUntilChanged
|
||||
>- subscribeNext { value in
|
||||
println("\(value)")
|
||||
}
|
||||
|
||||
/*:
|
||||
In the example above, the values 1, 2, 3, 1, 4 will be printed. The extra 1 will be filtered out.
|
||||
*/
|
||||
|
||||
/*:
|
||||
To be continued...
|
||||
*/
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
//
|
||||
// This file (and all other Swift source files in the Sources directory of this playground) will be precompiled into a framework which is automatically made available to OSXPlayground.playground.
|
||||
//
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<playground version='5.0' target-platform='osx'>
|
||||
<timeline fileName='timeline.xctimeline'/>
|
||||
</playground>
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "group:../../Projects/OSXPlayground.playground">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Timeline
|
||||
version = "3.0">
|
||||
<TimelineItems>
|
||||
</TimelineItems>
|
||||
</Timeline>
|
||||
|
|
@ -7,6 +7,9 @@
|
|||
<FileRef
|
||||
location = "group:README.md">
|
||||
</FileRef>
|
||||
<FileRef
|
||||
location = "group:OSXPlayground.playground">
|
||||
</FileRef>
|
||||
<FileRef
|
||||
location = "group:RxSwift/RxSwift.xcodeproj">
|
||||
</FileRef>
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
653C8BF41B114EB600983087 /* UIControl+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = 653C8BF31B114EB600983087 /* UIControl+Rx.swift */; };
|
||||
C8092BBB1AF50436008D9A2C /* CoreData.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C8092BBA1AF50436008D9A2C /* CoreData.framework */; };
|
||||
C81553E41A98AB4A00C63152 /* RxCocoa.h in Headers */ = {isa = PBXBuildFile; fileRef = C81553E31A98AB4A00C63152 /* RxCocoa.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
C8633A981B08FD0600375D60 /* NSTextField+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8633A971B08FD0600375D60 /* NSTextField+Rx.swift */; };
|
||||
|
|
@ -40,6 +41,7 @@
|
|||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
653C8BF31B114EB600983087 /* UIControl+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIControl+Rx.swift"; sourceTree = "<group>"; };
|
||||
C8092BBA1AF50436008D9A2C /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = System/Library/Frameworks/CoreData.framework; sourceTree = SDKROOT; };
|
||||
C81553DE1A98AB4A00C63152 /* RxCocoa.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RxCocoa.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
C81553E21A98AB4A00C63152 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
|
|
@ -152,11 +154,12 @@
|
|||
isa = PBXGroup;
|
||||
children = (
|
||||
C88BB8E01B07F2BE0064D411 /* UIButton+Rx.swift */,
|
||||
C88BB8E11B07F2BE0064D411 /* UICollectionView+Rx.swift */,
|
||||
653C8BF31B114EB600983087 /* UIControl+Rx.swift */,
|
||||
C88BB8E21B07F2BE0064D411 /* UIImageView+Rx.swift */,
|
||||
C88BB8E31B07F2BE0064D411 /* UILabel+Rx.swift */,
|
||||
C88BB8E41B07F2BE0064D411 /* UIScrollView+Rx.swift */,
|
||||
C88BB8E51B07F2BE0064D411 /* UISearchBar+Rx.swift */,
|
||||
C88BB8E11B07F2BE0064D411 /* UICollectionView+Rx.swift */,
|
||||
C88BB8E61B07F2BE0064D411 /* UITableView+Rx.swift */,
|
||||
C88BB8E71B07F2BE0064D411 /* UITextField+Rx.swift */,
|
||||
);
|
||||
|
|
@ -298,6 +301,7 @@
|
|||
C88BB8EA1B07F2BE0064D411 /* UIImageView+Rx.swift in Sources */,
|
||||
C88BB8EC1B07F2BE0064D411 /* UIScrollView+Rx.swift in Sources */,
|
||||
C8633AB01B093EDF00375D60 /* ControlTarget.swift in Sources */,
|
||||
653C8BF41B114EB600983087 /* UIControl+Rx.swift in Sources */,
|
||||
C8633AB21B093EDF00375D60 /* KVOObservable.swift in Sources */,
|
||||
C8633AB81B093EDF00375D60 /* NSURLSession+Rx.swift in Sources */,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -12,14 +12,6 @@ import UIKit
|
|||
|
||||
extension UIButton {
|
||||
public func rx_tap() -> Observable<Void> {
|
||||
return AnonymousObservable { observer in
|
||||
MainScheduler.ensureExecutingOnScheduler()
|
||||
|
||||
let observer = ControlTarget(control: self, controlEvents: UIControlEvents.TouchUpInside) { control in
|
||||
sendNext(observer, ())
|
||||
}
|
||||
|
||||
return observer
|
||||
}
|
||||
return rx_controlEvents(.TouchUpInside)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
//
|
||||
// UIControl+Rx.swift
|
||||
// RxCocoa
|
||||
//
|
||||
// Created by Daniel Tartaglia on 5/23/15.
|
||||
// Copyright (c) 2015 Krunoslav Zaher. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import RxSwift
|
||||
import UIKit
|
||||
|
||||
extension UIControl {
|
||||
public func rx_controlEvents(controlEvents: UIControlEvents) -> Observable<Void> {
|
||||
return AnonymousObservable { observer in
|
||||
MainScheduler.ensureExecutingOnScheduler()
|
||||
|
||||
let observer = ControlTarget(control: self, controlEvents: controlEvents) {
|
||||
control in
|
||||
sendNext(observer, ())
|
||||
}
|
||||
|
||||
return observer
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue