iOS 13, Swift 5
I am trying get my head around closure syntax in Swift and almost there. I got the first three bon, and understand them; but the last no. How do I call self.theMethod4 here? the syntax shown is wrong!
struct ContentView: View {
var body: some View {
Text("Hello, World!")
.onAppear {
self.theMethod {
print("1")
}
self.the2Method("Ya ") {
print("1")
}
self.the3Method("Hello") {arg2do in
print("1 ",arg2do)
}
self.the4Method("World") { (arg2do) -> String in
print("1 ",arg2do)
}
}
}
func theMethod(_ endOfMethod: #escaping () -> Void) {
print("2")
endOfMethod()
}
func the2Method(_ arg1:String, endOfMethod: #escaping () -> Void) {
print("3")
endOfMethod()
}
func the3Method(_ arg1:String, endOfMethod: #escaping (_ arg2:String) -> Void) {
print("4")
endOfMethod("Yo")
}
func the4Method(_ arg1:String, endOfMethod: #escaping (_ arg2: String) -> String) {
print("5")
let an = endOfMethod("Pe")
print("An ",an)
}
}
The closure in method 4 is defined to return a string, so when you call it, that’s what you’d have to do:
self.the4Method("World") { arg2do -> String in
return "1 " + arg2do
}
You got the closure syntax ({ (parameters) -> ReturnValueType in statements }) right, but since the fourth method needs a closure that returns a String, you need to return a string.
For example, this:
self.the4Method("World") { (arg2do) -> String in
print("1 ",arg2do)
return "return value"
}
prints:
5
1 Pe
An return value
Explanation of the output:
First the body of the4Method gets run, so 5 gets printed. Then the4Method calls endOfMethod, which is the closure passed in, with the argument "Pe". So print("1 ", "Pe") gets run, causing the second line to be printed. After that, the closure returns "return value" and we are now back to the4Method. The return value gets assigned to an, and print("An ",an) is run, causing the last line to be printed.
Related
func Test(clo: #escaping (String)->Void)
{
clo($0)
}
it return an error that says :
anonymous closure argument not contained in a closure
I want to be able to do:
Test{ n in
var name = n
name = "Taha"
print(name)}
instead of hard coded String
func Test(clo: #escaping (String)->Void)
{
clo("Taha")
}
That is not how closures work in Swift. And the code you wrote doesn't even make sense. Why do you assign n to name then change the value of name just to print it ?
The function with the closure will call the closure with a given value. You can think of closures as lambda functions. Here is an example:
func test(_ closure: #escaping (String) -> Void) {
closure("My string")
}
test { str in
print(str) //prints " My string"
}
For what you are trying to achieve, just use a String parameter in your function:
func test(_ str: String) {
print(str)
}
test("My string") // prints "My string"
Or use a closure that takes Void:
func test(_ closure: #escaping (Void) -> Void) {
closure()
}
test {
print("My string") // prints "My string"
}
I try to write a method as below:
class ThisIsExample{
func theMethod(inside:((Error?)->Void)->Void){
//some implementation
}
}
But, when I try to call this method, I don't know how to do that.
I wrote code below:
let example = ThisIsExample()
example.theMethod { ({(err) in }) in
print("print something")
}
I try to write another closure, which is { (err) in } inside the closure
But it is not workable, I'll receive error message like
Contextual closure type '((Error?) -> Void) -> Void' expects 1
argument, but 0 were used in closure body
So...could anyone please teach me how to call this method in correct way, thank you so much.
Although not sure what is the purpose of nested closure. But if you want to use this approach then you should call the closure in this way,
example.theMethod { (closure) in
closure(NSError.init())
}
You can do some thing like this:
func theMethod(inside:(Error?) -> ()) {
print("Closure as paramater")
}
This will take Error as closure parameter and return void.
you can call this function as below:
theMethod { (error) in
}
Something Like This
class NewClass
{
class func myCustomFunc(_ parameter1: String, parameterDict : [String : AnyObject]?, success:#escaping (String) -> Void, failure:#escaping (String) -> Void)
{
/// Just example Bool
var result : Bool = false
/// Get the parameter you sent while calling this Clousure block
let myParamString = parameter1
let paramDict = parameterDict
/// Share the output in case of Success and Failure
if (result){
success("success")
}
else{
failure("Failure")
}
}
}
Usage
NewClass.myCustomFunc("demoStr", parameterDict: [:], success: { (succesString) in
if succesString == "success"{
}
}) { (failureStr) in
if failureStr == "failure"{
}
}
This Function Accepts Parameter and Also give Different blocks depend upon closures
I'm trying to update my project to Swift 3.0 but I have some difficulties.
I'm getting next error: "Escaping closures can only capture inout parameters explicitly by value".
The problem is inside this function:
fileprivate func collectAllAvailable(_ storage: inout [T], nextUrl: String, completion: #escaping CollectAllAvailableCompletion) {
if let client = self.client {
let _ : T? = client.collectionItems(nextUrl) {
(resultCollection, error) -> Void in
guard error == nil else {
completion(nil, error)
return
}
guard let resultCollection = resultCollection, let results = resultCollection.results else {
completion(nil, NSError.unhandledError(ResultCollection.self))
return
}
storage += results // Error: Escaping closures can only capture inout parameters explicitly by value
if let nextUrlItr = resultCollection.links?.url(self.nextResourse) {
self.collectAllAvailable(&storage, nextUrl: nextUrlItr, completion: completion)
// Error: Escaping closures can only capture inout parameters explicitly by value
} else {
completion(storage, nil)
// Error: Escaping closures can only capture inout parameters explicitly by value
}
}
} else {
completion(nil, NSError.unhandledError(ResultCollection.self))
}
}
Can someone help me to fix that?
Using an inout parameter exclusively for an asynchronous task is an abuse of inout – as when calling the function, the caller's value that is passed into the inout parameter will not be changed.
This is because inout isn't a pass-by-reference, it's just a mutable shadow copy of the parameter that's written back to the caller when the function exits – and because an asynchronous function exits immediately, no changes will be written back.
You can see this in the following Swift 2 example, where an inout parameter is allowed to be captured by an escaping closure:
func foo(inout val: String, completion: (String) -> Void) {
dispatch_async(dispatch_get_main_queue()) {
val += "foo"
completion(val)
}
}
var str = "bar"
foo(&str) {
print($0) // barfoo
print(str) // bar
}
print(str) // bar
Because the closure that is passed to dispatch_async escapes the lifetime of the function foo, any changes it makes to val aren't written back to the caller's str – the change is only observable from being passed into the completion function.
In Swift 3, inout parameters are no longer allowed to be captured by #escaping closures, which eliminates the confusion of expecting a pass-by-reference. Instead you have to capture the parameter by copying it, by adding it to the closure's capture list:
func foo(val: inout String, completion: #escaping (String) -> Void) {
DispatchQueue.main.async {[val] in // copies val
var val = val // mutable copy of val
val += "foo"
completion(val)
}
// mutate val here, otherwise there's no point in it being inout
}
(Edit: Since posting this answer, inout parameters can now be compiled as a pass-by-reference, which can be seen by looking at the SIL or IR emitted. However you are unable to treat them as such due to the fact that there's no guarantee whatsoever that the caller's value will remain valid after the function call.)
However, in your case there's simply no need for an inout. You just need to append the resultant array from your request to the current array of results that you pass to each request.
For example:
fileprivate func collectAllAvailable(_ storage: [T], nextUrl: String, completion: #escaping CollectAllAvailableCompletion) {
if let client = self.client {
let _ : T? = client.collectionItems(nextUrl) { (resultCollection, error) -> Void in
guard error == nil else {
completion(nil, error)
return
}
guard let resultCollection = resultCollection, let results = resultCollection.results else {
completion(nil, NSError.unhandledError(ResultCollection.self))
return
}
let storage = storage + results // copy storage, with results appended onto it.
if let nextUrlItr = resultCollection.links?.url(self.nextResourse) {
self.collectAllAvailable(storage, nextUrl: nextUrlItr, completion: completion)
} else {
completion(storage, nil)
}
}
} else {
completion(nil, NSError.unhandledError(ResultCollection.self))
}
}
If you want to modify a variable passed by reference in an escaping closure, you can use KeyPath. Here is an example:
class MyClass {
var num = 1
func asyncIncrement(_ keyPath: WritableKeyPath<MyClass, Int>) {
DispatchQueue.main.async {
// Use weak to avoid retain cycle
[weak self] in
self?[keyPath: keyPath] += 1
}
}
}
You can see the full example here.
If you are sure that your variable will be available the whole time just use a true Pointer (same what inout actually does)
func foo(val: UnsafeMutablePointer<NSDictionary>, completion: #escaping (NSDictionary) -> Void) {
val.pointee = NSDictionary()
DispatchQueue.main.async {
completion(val.pointee)
}
}
Why does this code compile (in Playground):
func text (f: String -> Void) {
f("text")
}
func foo(s: String) {
countElements(s)
}
text() {
s in
foo(s)
}
And this one doesn't:
func text (f: String -> Void) {
f("text")
}
func foo(s: String) {
countElements(s)
}
text() {
s in
countElements(s)
}
With error message:
Cannot convert the expression's type '(($T3) -> ($T3) -> $T2) -> (($T3) -> $T2) -> $T2' to type '_BuiltinIntegerLiteralConvertible'
I can tell that there is something with return type, kinda Swift thinks that I want to return int but I just want to print it
The second version of the code works if you add an explicit return statement:
text() {
s in countElements(s)
return
}
The reason for that to happens is that it uses implicit return, being a single statement closure, so it tries uses the return value of countElements, which doesn't match with the expected return type, Void. The explicit return fixes that.
As to why it behaves in a different way, in the former case foo implicitly returns Void, which matches the closure return type.
More info: Implicit Returns from Single-Expression Closures
I noticed when writing an assert in Swift that the first value is typed as
#autoclosure() -> Bool
with an overloaded method to return a generic T value, to test existence via the LogicValue protocol.
However sticking strictly to the question at hand. It appears to want an #autoclosure that returns a Bool.
Writing an actual closure that takes no parameters and returns a Bool does not work, it wants me to call the closure to make it compile, like so:
assert({() -> Bool in return false}(), "No user has been set", file: __FILE__, line: __LINE__)
However simply passing a Bool works:
assert(false, "No user has been set", file: __FILE__, line: __LINE__)
So what is going on? What is #autoclosure?
Edit: #auto_closure was renamed #autoclosure
Consider a function that takes one argument, a simple closure that takes no argument:
func f(pred: () -> Bool) {
if pred() {
print("It's true")
}
}
To call this function, we have to pass in a closure
f(pred: {2 > 1})
// "It's true"
If we omit the braces, we are passing in an expression and that's an error:
f(pred: 2 > 1)
// error: '>' produces 'Bool', not the expected contextual result type '() -> Bool'
#autoclosure creates an automatic closure around the expression. So when the caller writes an expression like 2 > 1, it's automatically wrapped into a closure to become {2 > 1} before it is passed to f. So if we apply this to the function f:
func f(pred: #autoclosure () -> Bool) {
if pred() {
print("It's true")
}
}
f(pred: 2 > 1)
// It's true
So it works with just an expression without the need to wrap it in a closure.
Here's a practical example — my print override (this is Swift 3):
func print(_ item: #autoclosure () -> Any, separator: String = " ", terminator: String = "\n") {
#if DEBUG
Swift.print(item(), separator:separator, terminator: terminator)
#endif
}
When you say print(myExpensiveFunction()), my print override overshadows Swift's print and is called. myExpensiveFunction() is thus wrapped in a closure and not evaluated. If we're in Release mode, it will never be evaluated, because item() won't be called. Thus we have a version of print that doesn't evaluate its arguments in Release mode.
Description of auto_closure from the docs:
You can apply the auto_closure attribute to a function type that has a
parameter type of () and that returns the type of an expression (see
Type Attributes). An autoclosure function captures an implicit closure
over the specified expression, instead of the expression itself. The
following example uses the auto_closure attribute in defining a very
simple assert function:
And here's the example apple uses along with it.
func simpleAssert(condition: #auto_closure () -> Bool, message: String) {
if !condition() {
println(message)
}
}
let testNumber = 5
simpleAssert(testNumber % 2 == 0, "testNumber isn't an even number.")
Basically what it means is you pass a boolean expression as that first argument instead of a closure and it automatically creates a closure out of it for you. That's why you can pass false into the method because it is a boolean expression, but can't pass a closure.
This shows a useful case of #autoclosure https://airspeedvelocity.net/2014/06/28/extending-the-swift-language-is-cool-but-be-careful/
Now, the conditional expression passed as the first parameter to until will be automatically wrapped up into a closure expression and can be called each time around the loop
func until<L: LogicValue>(pred: #auto_closure ()->L, block: ()->()) {
while !pred() {
block()
}
}
// doSomething until condition becomes true
until(condition) {
doSomething()
}
It's just a way to get rid of the curly braces in a closure call, simple example:
let nonAutoClosure = { (arg1: () -> Bool) -> Void in }
let non = nonAutoClosure( { 2 > 1} )
let autoClosure = { (arg1: #autoclosure () -> Bool) -> Void in }
var auto = autoClosure( 2 > 1 ) // notice curly braces omitted
#autoclosure
#autoclosure converts(wraps) expression inside function parameter in a closure[About]
Pros:
easy to read assert(2 == 5, "failed")
curly braces are not used
Cons
hard to read. When you pass a function inside #autoclosure it is not clear that this function will be deferred(because it is closure inside). fooWithAutoClosure(a: foo0()) - foo0() will be called not immediately as we expect reading this line
Overusing autoclosures can make your code hard to understand. The context and function name should make it clear that evaluation is being deferred.
Official doc
#autoclosure doesn't take any parameters
func foo(p: #autoclosure () -> Void)
#autoclosure accept any function with only appropriate returned type
More examples
//functions block
func foo0() -> String {
return "foo0"
}
func foo1(i1: Int) -> String {
return "foo1 " + String(i1)
}
func foo2(i1: Int, i2: Int) -> String {
return "foo2 " + String(i1 + i2)
}
//closures block
func fooWithClosure0(p: () -> String) -> String {
return "fooWithClosure0 " + p()
}
func fooWithClosure1(p: (Int) -> String) -> String {
return "fooWithClosure1 " + p(1)
}
func fooWithClosure2(p: (Int, Int) -> String) -> String {
return "fooWithClosure2 " + p(1, 2)
}
//#autoclosure
func fooWithAutoClosure(a: #autoclosure () -> String) -> String {
return "fooWithAutoClosure " + a()
}
//test closures
func testClosures() {
XCTAssertEqual("fooWithClosure0 foo0", fooWithClosure0(p: foo0))
XCTAssertEqual("fooWithClosure1 foo1 1", fooWithClosure1(p: foo1))
XCTAssertEqual("fooWithClosure2 foo2 3", fooWithClosure2(p: foo2))
XCTAssertEqual("fooWithClosure2 foo2 3", fooWithClosure2(p: { (i1, i2) -> String in
return "fooWithClosure2 " + "foo2 " + String(i1 + i2)
}))
}
//test #autoclosure
func testAutoClosures() {
XCTAssertEqual("fooWithAutoClosure HelloWorld", fooWithAutoClosure(a: "HelloWorld")) //"HelloWorld" is String as returned value of #autoclosure
XCTAssertEqual("fooWithAutoClosure foo0", fooWithAutoClosure(a: foo0()))
XCTAssertEqual("fooWithAutoClosure foo1 1", fooWithAutoClosure(a: foo1(i1: 1)))
XCTAssertEqual("fooWithAutoClosure foo2 3", fooWithAutoClosure(a: foo2(i1: 1, i2: 2)))
}