Skip to content

Commit

Permalink
Closes #53 by adding an onCompletion function to CacheRequest
Browse files Browse the repository at this point in the history
  • Loading branch information
vittoriom committed Aug 18, 2015
1 parent d524cdc commit 75d4f74
Show file tree
Hide file tree
Showing 5 changed files with 197 additions and 17 deletions.
25 changes: 24 additions & 1 deletion Carlos/CacheRequest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -95,4 +95,27 @@ public class CacheRequest<T> {

return self
}
}

/**
Adds a listener for both success and failure events of this request
:param: completion The closure that should be called when the request completes (succeeds or fails), taking both an optional value in case the request succeeded and an optional error in case the request failed as parameters
:returns: The updated request
*/
public func onCompletion(completion: (value: T?, error: NSError?) -> Void) -> CacheRequest<T> {
if didFail || didSucceed {
completion(value: value, error: error)
} else {
onSuccess { value in
completion(value: value, error: nil)
}

onFailure { error in
completion(value: nil, error: error)
}
}

return self
}
}
4 changes: 1 addition & 3 deletions Carlos/KeyTransformation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,7 @@ public func transformKeys<A: CacheLevel, B: OneWayTransformer where A.KeyType ==
if let transformedKey = transformer.transform(key) {
return cache.get(transformedKey)
} else {
let request = CacheRequest<A.OutputType>()
request.fail(errorWithCode(FetchError.KeyTransformationFailed.rawValue))
return request
return CacheRequest<A.OutputType>(error: errorWithCode(FetchError.KeyTransformationFailed.rawValue))
}
}, setClosure: { (key, value) in
if let transformedKey = transformer.transform(key) {
Expand Down
17 changes: 5 additions & 12 deletions Carlos/PoolCache.swift
Original file line number Diff line number Diff line change
Expand Up @@ -65,19 +65,12 @@ public final class PoolCache<C: CacheLevel where C.KeyType: Hashable>: CacheLeve

Logger.log("Creating a new request \(request) for key \(key)")

let cleanRequestPool: () -> Void = {
dispatch_sync(SerialPoolQueue) {
self.requestsPool[key] = nil
}
}

request
.onSuccess({ _ in
cleanRequestPool()
})
.onFailure({ _ in
cleanRequestPool()
})
.onCompletion { _, _ in
dispatch_sync(SerialPoolQueue) {
self.requestsPool[key] = nil
}
}
}

return request
Expand Down
16 changes: 15 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,21 @@ request.onSuccess({ value in

```

**When the cache request succeeds, all its listeners are called**. And **even if you add a listener after the request already did its job, you will still get the callback**.
**When the cache request succeeds, all its listeners are called**. And **even if you add a listener after the request already did its job, you will still get the callback**.

If you are just interested in when the request completes, regardless of whether it succeeded or failed, you can use `onCompletion`:

```swift
request.onCompletion { (value, error) in
if let value = value {
println("Request succeeded with value \(value)")
} else if let error = error {
println("Request failed with code \(error.code)")
}

println("Nevertheless the request completed")
}
```

This cache is not very useful, though. It will never *actively* fetch values, just store them for later use. Let's try to make it more interesting:

Expand Down
152 changes: 152 additions & 0 deletions Tests/CacheRequestTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,97 @@ class CacheRequestTests: QuickSpec {
}
}
}

context("when calling onCompletion") {
beforeEach {
for idx in 0..<successSentinels.count {
request.onCompletion({ value, error in
failureSentinels[idx] = error
successSentinels[idx] = value
})
}
}

it("should not immediately call the closures passing an error") {
expect(failureSentinels.filter({ $0 == nil }).count).to(equal(failureSentinels.count))
}

it("should not immediately call the closures passing a value") {
expect(successSentinels.filter({ $0 == nil }).count).to(equal(successSentinels.count))
}

context("when calling fail") {
let errorCode = -1100

beforeEach {
request.fail(NSError(domain: "test", code: errorCode, userInfo: nil))
}

it("should call the closures passing an error") {
expect(failureSentinels).to(allPass({ $0!?.code == errorCode }))
}

it("should not call the closures passing a value") {
expect(successSentinels.filter({ $0 == nil }).count).to(equal(successSentinels.count))
}

context("when calling onCompletion again") {
var subsequentFailureSentinel: NSError?
var subsequentSuccessSentinel: String?

beforeEach {
request.onCompletion({ value, error in
subsequentSuccessSentinel = value
subsequentFailureSentinel = error
})
}

it("should immediately call the closure passing an error") {
expect(subsequentFailureSentinel?.code).to(equal(errorCode))
}

it("should not immediately call the closure passing a value") {
expect(subsequentSuccessSentinel).to(beNil())
}
}
}

context("when calling succeed") {
let value = "success value"

beforeEach {
request.succeed(value)
}

it("should call the closures passing a value") {
expect(successSentinels).to(allPass({ $0! == value }))
}

it("should not call the closures passing an error") {
expect(failureSentinels.filter({ $0 == nil }).count).to(equal(failureSentinels.count))
}

context("when calling onCompletion again") {
var subsequentSuccessSentinel: String?
var subsequentFailureSentinel: NSError?

beforeEach {
request.onCompletion({ result, error in
subsequentSuccessSentinel = result
subsequentFailureSentinel = error
})
}

it("should immediately call the closure passing a value") {
expect(subsequentSuccessSentinel).to(equal(value))
}

it("should not immediately call the closure passing an error") {
expect(subsequentFailureSentinel).to(beNil())
}
}
}
}
}

context("when initialized with a value") {
Expand Down Expand Up @@ -160,6 +251,25 @@ class CacheRequestTests: QuickSpec {
expect(failureSentinels.filter({ $0 == nil }).count).to(equal(failureSentinels.count))
}
}

context("when calling onCompletion") {
beforeEach {
for idx in 0..<successSentinels.count {
request.onCompletion({ value, error in
successSentinels[idx] = value
failureSentinels[idx] = error
})
}
}

it("should not call the closures passing an error") {
expect(failureSentinels.filter({ $0 == nil }).count).to(equal(failureSentinels.count))
}

it("should call the closures passing a value") {
expect(successSentinels).to(allPass({ $0! == value }))
}
}
}

context("when initialized with an error") {
Expand Down Expand Up @@ -204,6 +314,29 @@ class CacheRequestTests: QuickSpec {
expect(failureSentinels).to(allPass({ $0!!.code == error.code }))
}
}

context("when calling onCompletion") {
beforeEach {
for idx in 0..<successSentinels.count {
request.onCompletion({ value, error in
successSentinels[idx] = value
failureSentinels[idx] = error
})
}
}

it("should immediately call the closures") {
expect(failureSentinels.filter({ $0 != nil }).count).to(equal(failureSentinels.count))
}

it("should pass the right error") {
expect(failureSentinels).to(allPass({ $0!!.code == error.code }))
}

it("should not pass a value") {
expect(successSentinels.filter({ $0 == nil }).count).to(equal(successSentinels.count))
}
}
}

context("when the error is nil") {
Expand Down Expand Up @@ -243,6 +376,25 @@ class CacheRequestTests: QuickSpec {
expect(failureSentinels.filter({ $0 != nil }).count).to(equal(failureSentinels.count))
}
}

context("when calling onCompletion") {
beforeEach {
for idx in 0..<successSentinels.count {
request.onCompletion({ value, error in
failureSentinels[idx] = true
successSentinels[idx] = value
})
}
}

it("should immediately call the closures") {
expect(failureSentinels.filter({ $0 != nil }).count).to(equal(failureSentinels.count))
}

it("should not pass a value") {
expect(successSentinels.filter({ $0 == nil }).count).to(equal(successSentinels.count))
}
}
}
}
}
Expand Down

0 comments on commit 75d4f74

Please sign in to comment.