Swift completion block - ios

I'm having a hard time understanding a problem I'm having.
To simplify, I'll use UIView method.
Basically, if I write the method
UIView.animateWithDuration(1, animations: {() in
}, completion:{(Bool) in
println("test")
})
it works fine.
Now, if I do the same method, but creating a string like so:
UIView.animateWithDuration(1, animations: {() in
}, completion:{(Bool) in
String(23)
})
It stops working. Compiler error: Missing argument for parameter 'delay' in call
Now, here's the strange part. If I do the exact same code as the one that fails, but just add a print command like so:
UIView.animateWithDuration(1, animations: {() in
}, completion:{(Bool) in
String(23)
println("test")
})
it starts to work again.
My problem is basically the same thing. My code:
downloadImage(filePath, url: url) { () -> Void in
self.delegate?.imageDownloader(self, posterPath: posterPath)
}
Doesn't work. But if I change to.
downloadImage(filePath, url: url) { () -> Void in
self.delegate?.imageDownloader(self, posterPath: posterPath)
println("test")
}
or even:
downloadImage(filePath, url: url) { () -> Void in
self.delegate?.imageDownloader(self, posterPath: posterPath)
self.delegate?.imageDownloader(self, posterPath: posterPath)
}
It works fine.
I can't understand why this is happening. I'm close to accept that it's a compiler bug.

Closures in Swift have implicit returns when they are only made up of a single expression. This allow for succinct code such as this:
reversed = sorted(names, { s1, s2 in s1 > s2 } )
In your case, when you create your string here:
UIView.animateWithDuration(1, animations: {() in }, completion:{(Bool) in
String(23)
})
you end up returning that string and that makes the signature of your closure:
(Bool) -> String
That no longer matches what's required by animateWithDuration's signature (which translates to Swift's cryptic Missing argument for parameter 'delay' in call error because it can't find an appropriate signature to match).
An easy fix is to add an empty return statement at the end of your closure:
UIView.animateWithDuration(1, animations: {() in}, completion:{(Bool) in
String(23)
return
})
Which makes your signature what it should be:
(Bool) -> ()
Your last example:
downloadImage(filePath, url: url) { () -> Void in
self.delegate?.imageDownloader(self, posterPath: posterPath)
self.delegate?.imageDownloader(self, posterPath: posterPath)
}
works because there are two expressions there, not just one; implicit returns only happen when the closure contains a single expression. So, that closure isn't returning anything and that matches its signature.

Related

Add function after completion ((bool) -> void)

I want to call this function with a completion handler:
progressView.animate(fromAngle: 0, toAngle: 360, duration: 5, completion:
print("go to next lvl")
)
However I get the error: print produces () not the expected contextual result type ((bool) -> Void)?
I do not know what this error means. I just want to execute a function when the duration is over in the completion handler. I already tried adding (Bool) -> Void in after the completion handler but this does not work. Thank you.
The corresponding closure of the signature (Bool) -> Void) is
{ (result) -> Void in ... }
So you have to write (redundant inferred syntax is omitted)
progressView.animate(fromAngle: 0, toAngle: 360, duration: 5, completion: { result in
print("go to next lvl", result)
})
or with trailing closure syntax
progressView.animate(fromAngle: 0, toAngle: 360, duration: 5) { result in
print("go to next lvl", result)
}
Why don't you use code completion to let Xcode offer you the proper syntax?

error after migration from swift 1.2 to swift 2

UIView.animateWithDuration(0.2, animations: { () -> Void in
self.layoutIfNeeded()
}) { (finished) -> Void in
.......
.......
}
this code throws this error:
Cannot convert value of type '(_) throws -> Void' to expected argument type '((Bool) -> Void)?'
It seem that you are not closing correctly the parentheses and missing the completion argument.
The most easy way to detect this kind of errors is try to rewrite the method.
Try this:
UIView.animateWithDuration(0.2, animations: { () -> Void in
self.layoutIfNeeded()
}, completion: { (finished) -> Void in
///
})
Write like below code, this will work bcz its working at my side,
UIView.animateWithDuration(0.2, animations: { () -> Void in
//your code here
self.layoutIfNeeded()
}) { (flag : Bool) -> Void in
///
}
This is the code that generates the same xcode, try this.
UIView.animateWithDuration(0.2, animations: {
// Code for animation
}) { (finished: Bool) in
// Code for completion
}

Custom Function's CompletionHandler for Dummies

I searched everywhere on the internet but couldn't really deal with the answers I found. So if someone could help me here, that'd be appreciated.
I wrote a function that looks like this:
func setImage(imageName: String, completion: ((String) -> Void)?) {
UIView.transitionWithView(self.myImageView, duration: 0.3, options: .CurveEaseOut, animations: {
self.lockImageView.image = UIImage(named: "\(imageName).png")
}, completion: { finished in
//execute the completionBlock that was passed
})
}
I call it like this:
setImage("lockCheck", completion: { finished in
print("done")
})
Now, how do I execute whatever was passed as completion?
In the function, in the transition's completion block, I tried something like
for x in completion {self.x}
but that didn't work.
Thanks in advance :)
You could for example execute the completion handler right away in the completionHandler of the animation block like so:
func setImage(imageName: String, completion: ((Bool) -> Void)?) {
UIView.transitionWithView(self.myImageView, duration: 0.3, options: .CurveEaseOut, animations: { () -> Void in
self.lockImageView.image = UIImage(named: "\(imageName).png")
}, completion: completion)
}
You can also run an completion handler with extra parameters like the following (I hope it is clear like this):
func setImage(imageName: String, completion: ((Bool, String) -> Void)?) {
UIView.transitionWithView(self.lockImageView, duration: 0.3, options: .CurveEaseOut, animations: { () -> Void in
self.lockImageView.image = UIImage(named: "\(imageName).png")
}) { (finished) -> Void in
// Do some things for example print
print("Hi, this is the animation completion handler")
// Notice the ? because the completion handler is an optional
completion?(finished, "some string")
}
}

How to pass (optional) completion handler closure to transitionFromViewController in Swift?

In a ViewController in my app I call transitionFromViewController but always get the following error when passing in a closure to the completion: argument.
Type '() -> Void' does not conform to protocol 'NilLiteralConvertible'
Here's the function call:
self.transitionFromViewController(
self.currentVC,
toViewController: newController,
duration: 0.2,
options: UIViewAnimationOptions.TransitionCrossDissolve,
nil,
completion: { finished in
fromViewController.removeFromParentViewController()
toViewController.didMoveToParentViewController(containerViewController)
toViewController.view.frame = containerViewController.view.bounds
})
According to code completion the method signature is as follows:
transitionFromViewController(fromViewController: UIViewController, toViewController: UIViewController, duration: NSTimeInterval, options: UIViewAnimationOptions, animations: () -> Void(), completion: ((Bool) -> Void)?)
You cannot pass nil to animations paramere () -> Void() declared as not optional
Pass empty closure if you want
self.transitionFromViewController(
self.currentVC,
toViewController: newController,
duration: 0.2,
options: UIViewAnimationOptions.TransitionCrossDissolve,
animations: { () -> Void in
},
completion: { finished in
fromViewController.removeFromParentViewController()
toViewController.didMoveToParentViewController(containerViewController)
toViewController.view.frame = containerViewController.view.bounds
})
I think following code should help you :
self.transitionFromViewController(fromViewController, toViewController: toViewController, duration: 0.1, options: UIViewAnimationOptions.CurveEaseInOut, animations: { () -> Void in
// code for animations
}) { (value: Bool) -> Void in
// code after completion
}
Where, // code for animations - code you want to execute during the block.
And, // code after completion - code you want to execute after the completion of the block.

how to declare Swift C closures

I am trying to use animateWithDuration closure in Swift. I have declared the arguments in the closure as mentioned in the Apple Book for Swift. However, I am still getting an error.
Below is the code snippet:
if(!isRotating){
isRotating = true
var myImageTemp :UIImageView = self.myImage
UIView.animateWithDuration(0.5, delay: 1, options: UIViewAnimationCurve.EaseOut, animations:
{
() in myImageTemp.transform = CGAffineTransformMakeRotation(angle + M_PI_2)
},
completion:
{
(Bool finished) in self.pathAnimation() })
}
It gives me an error:
Could find an overload that accepts the supplied arguments.
And also it tells me:
Implicit use of self in closure.
Can anybody help me with this?
Just try:
UIView.animateWithDuration(0.2,
animations:
{
// your code.
},
completion:
{
(completed: Bool) in
// your code.
})
The (completed: Bool) in part indicates that the closure takes a Bool parameter labeled completed. If you are not interested in accessing the completed parameter, you can ignore it using an underscore.
UIView.animateWithDuration(0.2,
animations:
{
// your code.
},
completion:
{ _ in
// your code.
})

Resources