Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Set the dispatch queue explicitly #20

Merged
merged 4 commits into from
Dec 7, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,15 @@ networkLoader.onProgress.listen(self) { (progress) in
}.queueAndDelayBy(1.0)
```

If you don't like the double quotes that you have to use since Swift 2.0 when you fire singlas that take tuples, you can use a special operator to fire the data:
A signal dispatches listener calls synchronously on the posting thread by default. To define the thread explicitly, you should use the `dispatchOnQueue` method. In this way you will receive listener calls asynchronously on the specified queue:

```swift
networkLoader.onProgress.listen(self) { (progress) in
// This fires on the main queue
}.dispatchOnQueue(dispatch_get_main_queue())
```

If you don't like the double quotes that you have to use since Swift 2.0 when you fire signals that take tuples, you can use a special operator to fire the data:

```swift
// If you don't like the double quotes when firing signals that have tuples
Expand Down
27 changes: 23 additions & 4 deletions Signals/Signal.swift
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ public class SignalListener<T> {
private var queuedData: T?
private var filter: ((T) -> Bool)?
private var callback: (T) -> Void
private var dispatchQueue: dispatch_queue_t?

private init (listener: AnyObject, callback: (T) -> Void) {
self.listener = listener
Expand All @@ -169,8 +170,9 @@ public class SignalListener<T> {
} else {
// Set up queue
queuedData = data
let dispatchQueue = self.dispatchQueue == nil ? dispatch_get_main_queue() : self.dispatchQueue
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(delay! * Double(NSEC_PER_SEC))),
dispatch_get_main_queue()) { [weak self] () -> Void in
dispatchQueue) { [weak self] () -> Void in
if let definiteSelf = self {
let data = definiteSelf.queuedData!
definiteSelf.queuedData = nil
Expand All @@ -181,7 +183,13 @@ public class SignalListener<T> {
}
}
} else {
callback(data)
if let queue = self.dispatchQueue {
dispatch_async(queue) {
self.callback(data)
}
} else {
callback(data)
}
}

return listener != nil
Expand All @@ -195,7 +203,7 @@ public class SignalListener<T> {
/// returns true.
///
/// - parameter filter: A closure that can decide whether the Signal fire should be dispatched to its listener.
/// :return: Returns self so you can chain calls.
/// - returns: Returns self so you can chain calls.
public func filter(filter: (T) -> Bool) -> SignalListener {
self.filter = filter
return self
Expand All @@ -204,11 +212,22 @@ public class SignalListener<T> {
/// Tells the listener to queue up all signal fires until the elapsed time has passed and only once dispatch the last received
/// data. A delay of 0 will wait until the next runloop to dispatch the signal fire to the listener.
/// - parameter delay: The number of seconds to delay dispatch
/// :return: Returns self so you can chain calls.
/// - returns: Returns self so you can chain calls.
public func queueAndDelayBy(delay: NSTimeInterval) -> SignalListener {
self.delay = delay
return self
}

/// Assigns a dispatch queue to the SignalListener. The queue is used for scheduling the listener calls. If not nil,
/// the callback is fired asynchronously on the specified queue. Otherwise, the block is run synchronously on the
/// posting thread (default behaviour).
///
/// - parameter queue: A queue for performing the listener's calls.
/// - returns: Returns self so you can chain calls.
public func dispatchOnQueue(queue: dispatch_queue_t) -> SignalListener {
self.dispatchQueue = queue
return self
}

/// Cancels the listener. This will detach the listening object from the Signal.
public func cancel() {
Expand Down
48 changes: 48 additions & 0 deletions SignalsTests/SingalQueueTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -133,4 +133,52 @@ class SignalQueueTests: XCTestCase {
listener2 = nil
XCTAssertEqual(emitter.onInt.listeners.count, 0, "Should have zero listener")
}

func testListeningOnDispatchQueue() {
let firstQueueLabel = "com.signals.queue.first";
let firstQueue = dispatch_queue_create(firstQueueLabel, DISPATCH_QUEUE_SERIAL)
let secondQueueLabel = "com.signals.queue.second";
let secondQueue = dispatch_queue_create(secondQueueLabel, DISPATCH_QUEUE_CONCURRENT)

let firstListener = NSObject()
let secondListener = NSObject()

let firstExpectation = expectationWithDescription("firstDispatchOnQueue")
emitter.onInt.listen(firstListener, callback: { (argument) in
let currentQueueLabel = String(UTF8String: dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL))
XCTAssertTrue(firstQueueLabel == currentQueueLabel)
firstExpectation.fulfill()
}).dispatchOnQueue(firstQueue)
let secondExpectation = expectationWithDescription("secondDispatchOnQueue")
emitter.onInt.listen(secondListener, callback: { (argument) in
let currentQueueLabel = String(UTF8String: dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL))
XCTAssertTrue(secondQueueLabel == currentQueueLabel)
secondExpectation.fulfill()
}).dispatchOnQueue(secondQueue)

emitter.onInt.fire(10)

waitForExpectationsWithTimeout(0.2, handler: nil)
}

func testUsesCurrentQueueByDefault() {
let queueLabel = "com.signals.queue";
let queue = dispatch_queue_create(queueLabel, DISPATCH_QUEUE_CONCURRENT)

let listener = NSObject()
let expectation = expectationWithDescription("receivedCallbackOnQueue")

emitter.onInt.listen(listener, callback: { (argument) in
let currentQueueLabel = String(UTF8String: dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL))
XCTAssertTrue(queueLabel == currentQueueLabel)
expectation.fulfill()
})

dispatch_async(queue) {
self.emitter.onInt.fire(10)
}

waitForExpectationsWithTimeout(0.2, handler: nil)
}

}