I'm trying to get the value passed to a completion handler to test against for unit testing. It is not a normal setup for a completion handler since the completion handler is a var:
typealias ResultType = Result<Data>
typealias CompletionType = (ResultType) -> ()
var onCompletion: CompletionType// this is the completion handler
open override func finished(_ errors: [NSError]) {
self.urlSession.invalidateAndCancel()
self.onCompletion(Result.failure(EComError.networkError(.notReachable)))
}
}
I'm trying to compare the var: onCompletion with the
self.onCompletion(Result.failure(EComError.networkError(.notReachable)))
but when I try and print the var: onCompletion after the function has been it returns:
Optional((Function))
I'm trying to write a test case like this:
func test_finishedResult(){
sut?.finished([NSError(code: OperationErrorCode.executionFailed)])
XCTAssertEqual(sut?.onCompletion(Result.failure(EComError.networkError(.notReachable))), sut?.onCompletion)
}
but it returns the error:
Cannot convert value of type 'DownloadOperation.CompletionType?' (aka
'Optional<(Result) -> ()>') to expected argument type '()?'
where
DownloadOperation
is the name of the class the function is in.
Whats the best way to unit test the that the var:
onCompletion
is equal toself.onCompletion(Result.failure(EComError.networkError(.notReachable)))?
With your test code you are not comparing the value passed to the completion, but instead the function pointers of your callbacks.
What you probably want is something like:
func test_finishedResult() {
// Set your callback here
sut?.onCompletion = { result in
// Compare the result here
XCTAssertEqual(Result.failure(EComError.networkError(.notReachable)), result)
}
// Trigger the callback with the finished method
sut?.finished([NSError(code: OperationErrorCode.executionFailed)])
}
Related
I have the following code
class Child {
var onClosureFromParent: ((Int) -> Void)?
// Ok.
func setupEscaping(onClosureFromParent: #escaping (Int) -> Void) {
self.onClosureFromParent = onClosureFromParent
}
// Error: Assigning non-escaping parameter 'onClosureFromParent' to an #escaping closure
func setupNonEscaping(onClosureFromParent: (Int) -> Void) {
// FIXME:
self.onClosureFromParent = onClosureFromParent
}
}
I was wondering, how can I make a reference to non escaping closure?
From the Official documentation:
A closure is said to escape a function when the closure is passed as
an argument to the function, but is called after the function returns.
When you declare a function that takes a closure as one of its
parameters, you can write #escaping before the parameter’s type to
indicate that the closure is allowed to escape.
Since the closure argument in the function, you are saving its reference and not executing it, it makes it implicit that this closure will not be executed within the scope of the function. Thus, you should allow it to escape.
func setupNonEscaping(onClosureFromParent: (Int) -> Void) {
// Saving the reference, not executing it.
// ie, It will be executed later on...
self.onClosureFromParent = onClosureFromParent
}
Somewhere in the code...
// The closure is executed later on, thus it should outlive the function lifescope.
let param = 10
self.onClosureFromParent(param)
I am using Firebase on iOS with Swift 3.
When I use
FIRDatabase.database().reference().child("child").setValue("value") {
(error: Error?, databaseReference: FIRDatabaseReference) in
print("Error while setting value \(error)")
}
The app crashes on runtime with the following log:
*** Terminating app due to uncaught exception 'InvalidFirebaseData', reason: '(nodeFrom:priority:) Cannot store object of type _SwiftValue
at . Can only store objects of type NSNumber, NSString, NSDictionary,
and NSArray.'
I tried to use the same function but without the trailing closure and for some reason, it works!
FIRDatabase.database().reference().child("child").setValue("value",
withCompletionBlock: {
(error: Error?, databaseReference: FIRDatabaseReference) in
print("Error while setting value \(error)")
})
Is there something special about trailing closures and Swift 3?
tl;dr: Firebase provides a setValue(_ value: Any?, andPriority priority: Any?) which is incorrectly matched when using a trailing closure with setValue(_ value: Any?, withCompletionBlock: (Error?, FIRDatabaseReference) -> Void).
Solution: When using an API that has many varieties, avoid using trailing closures. In this case, prefer setValue(myValue, withCompletionBlock: { (error, dbref) in /* ... */ }); do not use setValue(myValue) { (error, dbref) in /* ... */ }.
Explanation
This appears to be a Swift bug. As in other languages, such as Java, Swift generally chooses the most specific overload. E.g.,
class Alpha {}
class Beta : Alpha {}
class Charlie {
func charlie(a: Alpha) {
print("\(#function)Alpha")
}
func charlie(a: Beta) {
print("\(#function)Beta")
}
}
Charlie().charlie(a: Alpha()) // outputs: charlie(a:)Alpha
Charlie().charlie(a: Beta() as Alpha) // outputs: charlie(a:)Alpha
Charlie().charlie(a: Beta()) // outputs: charlie(a:)Beta
However, when overloaded functions match a trailing closure, Swift (at least, sometimes) selects the more general type. E.g.,
class Foo {
func foo(completion: () -> Void) {
print(#function)
}
func foo(any: Any?) {
print(#function)
}
}
func bar() {}
Foo().foo(completion: bar) // outputs: foo(completion:)
Foo().foo(any: bar) // outputs: foo(any:)
Foo().foo() { () in } // outputs: foo(any:)
// ^---- Here lies the problem
// Foo().foo(bar) will not compile; can't choose between overrides.
Any? is a more general type than () -> Void -- i.e., "anything, even null" is more broad than "a function receiving 0 parameters and returning something of type Void". However, the trailing closure matches Any?; this is the opposite of what you would expect from a language that matches the most specific type.
While there is an accepted answer, which provides some clarity, explaining that it's a Swift bug is not really accurate. That being said, the explanation is accurate but not for this issue.
Allowing the closure to be added to setValue in the first place is the real bug.
A more accurate answer is that there is no completion block/closure for the setValue function, which is why it fails.
The specific function -setValue: does NOT have a closure, which is why it's crashing. i.e. it's an incorrect implementation in your code. Per the docs:
func setValue(_ value: Any?)
note that the setValue function does NOT have a closure and if you add one the function will have no idea what to do with that data.
To use a completion block/closure, you must call the correct function which is
-setValue:withCompletionBlock:
Bottom line is you can't randomly add a parameter or call to a function that's not designed to accept it.
This is obviously not valid but conceptually it's the same error.
let a = "myString"
a.append("x") { (error: Error?) }
In this case the compiler knows the the string.append function doesn't have a closure option and catches it before compiling.
So go a tad further, this code complies & runs but also generates an error
ref.child("child").setValue("value") { }
Again, setValue doesn't have a closure so this code is improperly implemented.
To clarify, given a class
class MyClass {
var s = ""
var compHandler = {}
func setValue(aString: String) {
self.s = aString
}
func setValue(aString: String, someCompletionHandler: completionHandler) {
self.s = aString
self.compHandler = someCompletionHandler
}
}
Note that setValue:aString is a totally different function than setValue:aString:someCompletionHandler
The only parameter that can be based to setValue:aString is a String as the first and only parameter.
The setValue:aString:someCompletionHandler will be passed two parameters, a String in the first position and a completionHandler in the second position.
The actual completion block is the second parameter passed.
param1, param2
------, ---------------
string, completionBlock
This is why
setValue(value) {}
is improperly formatted whereas
setValue(value, withBlock: {})
is properly formatted.
In the following code, I’m trying to create a closure that is passed a single parameter that will be used in the closure, where no value is returned:
The code is called from another Swift class in my app via runit()
Cannot invoke value of type ‘(CheckerOperation) -> ()’ with argument
list ‘(CheckerOperation)’ at the line
“runTimerProcess(runitProcess(customOperation))
I don’t understand how to invoke when the closure will not be returning a value.
(CheckerOperation is just a custom NSOperation class)
class Checker {
var queue = NSOperationQueue()
let customOperation : CheckerOperation = CheckerOperation()
var runitProcess: (CheckerOperation) -> () = {op in
NSOperationQueue.mainQueue().addOperationWithBlock({
let operationQueue = NSOperationQueue.mainQueue()
operationQueue.addOperation(op)
})
}
func runTimerProcess(closureblock: ClosureBlock){
let queue = dispatch_queue_create(“myqueue”, DISPATCH_QUEUE_SERIAL)
dispatch_async(queue,closureblock)
}
func runit(){
runTimerProcess(runitProcess(customOperation))
}
}
You're mixing two core syntaxes: (1) computed properties, and (2) functions/methods.
Computed properties cannot accept arguments. For your case, you should be simply defining a function with an argument op with a type of CheckerOperation. I say this because you don't appear to need to return or check a value on runitProcess which is really what a computed property is intended for.
Use this:
func runitProcess(op: CheckerOperation) {
NSOperationQueue.mainQueue().addOperationWithBlock({
let operationQueue = NSOperationQueue.mainQueue()
operationQueue.addOperation(op)
}
I'd like to implement method chaining in my swift code, likely to Alamofire methods. For example, if I have to use my function like below
getListForID(12).Success {
// Success block
}. Failure {
// Failure block
}
How would I create the function getListForID?
To expand on the great points #dasblinkenlight and #Sulthan have made – here's a small example of how you could achieve your request function to take a success and failure closure, in the convenient syntax that you want.
First, you'll have to define a new class to represent the 'result handler'. This is what your success and failure functions will pass around, allowing you to add multiple trailing closures to make up your completion block logic. You'll want it to look something like this:
class ResultHandler {
typealias SuccessClosure = RequestHandler.Output->Void
typealias FailureClosure = Void->Void
// the success and failure callback arrays
private var _successes = [SuccessClosure]()
private var _failures = [FailureClosure]()
/// Invoke all the stored callbacks with a given callback result
func invokeCallbacks(result:RequestHandler.Result) {
switch result {
case .Success(let output): _successes.forEach{$0(output)}
case .Failure: _failures.forEach{$0()}
}
}
// remove all callbacks – could call this from within invokeCallbacks
// depending on the re-usability of the class
func removeAllCallbacks() {
_successes.removeAll()
_failures.removeAll()
}
/// appends a new success callback to the result handler's successes array
func success(closure:SuccessClosure) -> Self {
_successes.append(closure)
return self
}
/// appends a new failure callback to the result handler's failures array
func failure(closure:FailureClosure) -> Self {
_failures.append(closure)
return self
}
}
This will allow you to define multiple success or failure closures to be executed on completion. If you don't actually need the capacity for multiple closures, then you can simplify the class down by stripping out the arrays – and just keeping track of the last added success and failure completion blocks instead.
Now all you have to do is define a function that generates a new ResultHandler instance and then does a given asynchronous request, with the invokeCallbacks method being invoked upon completion:
func doRequest(input:Input) -> ResultHandler {
let resultHandler = ResultHandler()
doSomethingAsynchronous(resultHandler.invokeCallbacks)
return resultHandler
}
Now you can call it like this:
doRequest(input).success {result in
print("success, with:", result)
}.failure {
print("fail :(")
}
The only thing to note is your doSomethingAsynchronous function will have to dispatch its completion block back to the main thread, to ensure thread safety.
Full project (with added example on usage): https://github.com/hamishknight/Callback-Closure-Chaining
In order to understand what is going on, it would help to rewrite your code without the "convenience" syntax, which lets you omit parentheses when a closure is the last parameter of a function:
getListForID(12)
.Success( { /* Success block */ } )
.Failure( { /* Failure block */ } )
This makes the structure of the code behind this API more clear:
The return value of getListForID must be an object
The object must have two function called Success and Failure*
Both Success and Failure need to take a single parameter of closure type
Both Success and Failure need to return self
* The object could have only Success function, and return a different object with a single Failure function, but then you wouldn't be able to re-order the Success and Failure handlers, or drop Success handler altogether.
How can I invoke a field which has a function assigned in Class level?
This is the code snippet i am using:
class Foo {
let alias = whatever
func whatever() -> Int {
return 3
}
}
print(Foo().alias) // print out this is a function
print(Foo().alias()) // compilation error about missing argument
First of all, I know I can use Foo.whatever() instead.
but let me explain why I ask this question.
So normally if I am using playground, it is working as I expect if I have a function and then I assign it to a variable. It has exact same code but it is just out of class scope.
let alias = whatever
func whatever() -> Int {
return 3
}
print(alias) // print out function
print(alias()) // print 3 so it is working as expected
Dont know why I cant invoke it in the Class level, if it is impossible to invoke it, why swift doesnt have compilation error about let alias = whatever in class level
You can not invoke it in the Class level because alias is not an instance method, but a function which returns another function (whatever in this case). And Swift compiler does provide Missing argument for parameter #1 in call compilation error, as you've mentioned in your first code block:
print(Foo().alias()) // compilation error about missing argument
You may invoke whatever function via alias like this:
let instance = Foo()
instance.alias(instance)()
It means that alias function has a single argument, a Foo instance, and returns another function. This latter function takes nothing and returns Int, that is type of alias is:
(Foo) -> () -> Int
Basically, an instance method in Swift is simply a type method that takes the instance as an argument and returns a function which will then be applied to the instance:
Foo.whatever(instance)()
Check the actual alias type with the following code:
println(Foo().alias.dynamicType)
To do that you need a computed property, this will work:
class Foo {
var alias : () -> Int { get{ return whatever}}
func whatever() -> Int {
return 3
}
}
print(Foo().alias) // print out this is a function
print(Foo().alias()) // 3