Why will func closures sometimes delete its paramter name in Swift? - ios

I have a function of two closures
testNetworkAvailability(reachableBlock:, unreachableBlock:)
But when I hit enter for the autocompletion of closure placeholder, the second one unreachableBlock will delete the variable name along with it and causes an error.
For example, if I open up this closure placeholder by hitting enter, it will look like:
testNetworkAvailability(reachableBlock: { () -> Void in
<#code#>
}) { () -> Void in
<#code#>
}
As a matter of fact, as I copy this function to stackoverflow, the placeholder for these blocks reads as <#(() -> Void)?##() -> Void#>. It is so strange as it should be #() -> Void# only, shouldn't it?
Why is this and how to fix it?

As long as the last argument is a closure, Swift allows you to omit the parameter name and treat it as an inline block.
autoreleasepool {
// ...
}
See the documentation on trailing closures.
Should XCode's autocomplete prefer trailing closures than not is a topic for debate however.

Related

This is one thing I do not understand in Swift

Consider these lines:
I create a NSButton based class with this:
typealias onClickHandler = (NSTextfieldSuper)->Void
var onClick: onClickHandler?
When the user clicks on an instance of that button, I do this:
if (self.onClick != nil) {
onClick?(self)
}
I use that button later, from another class, with this:
let button = SuperButton()
button.onClick = { (textField: NSTextfieldSuper)->Void in
}
I am not sure if this is the correct syntax. I would like to process the button sent from the first closure on the parent class, where the button is created.
This was the only form I was able to type this without Xcode complaining. If this is correct, what is the purpose of this ->Void there? What could this possibly returning?
I just want to process that button sent.
By the way, as a bonus, I have to initialize several buttons with this, all running the same function. It would be nice to do something like
func doSomething () {
}
and then
let button = SuperButton()
button.onClick = doSomething
any ideas?
This was the only form I was able to type this without Xcode complaining. If this is correct, what is the purpose of this ->Void there? What could this possibly returning?
It is the same as in your typealias, in Swift a function type has the form:
(parameter definitions) -> return type
and functions which return nothing have a return type of Void (similar to C). The full form off a closure expression is:
{ (parameter definitions) ->return typeinbody}
Without any inference this expression provides the full type of the closure, and the -> Void Return type in your example specifies that your closure returns nothing. In your assignment this full type will be checked at compile time to conform to the type of onClick.
Now Swift will infer lots of stuff and there are various shorthands available for closure expressions, you will find that Swift accepts:
button.onClick = { textField in }
as well here with both the argument and return types of the closure being inferred.
By the way, as a bonus, [...] any ideas?
Just make the types match:
func doSomething(textField : NSTextfieldSuper) { }
button.onClick = doSomething
Unlike in (Objective-)C functions and closures (blocks in C) are interchangeable (as they are in plenty of other languages, C is the oddfellow here)
HTH

Closure cannot implicitly capture a mutating self parameter

I am using Firebase to observe event and then setting an image inside completion handler
FirebaseRef.observeSingleEvent(of: .value, with: { (snapshot) in
if let _ = snapshot.value as? NSNull {
self.img = UIImage(named:"Some-image")!
} else {
self.img = UIImage(named: "some-other-image")!
}
})
However I am getting this error
Closure cannot implicitly capture a mutating self parameter
I am not sure what this error is about and searching for solutions hasn't helped
The short version
The type owning your call to FirebaseRef.observeSingleEvent(of:with:) is most likely a value type (a struct?), in which case a mutating context may not explicitly capture self in an #escaping closure.
The simple solution is to update your owning type to a reference once (class).
The longer version
The observeSingleEvent(of:with:) method of Firebase is declared as follows
func observeSingleEvent(of eventType: FIRDataEventType,
with block: #escaping (FIRDataSnapshot) -> Void)
The block closure is marked with the #escaping parameter attribute, which means it may escape the body of its function, and even the lifetime of self (in your context). Using this knowledge, we construct a more minimal example which we may analyze:
struct Foo {
private func bar(with block: #escaping () -> ()) { block() }
mutating func bax() {
bar { print(self) } // this closure may outlive 'self'
/* error: closure cannot implicitly capture a
mutating self parameter */
}
}
Now, the error message becomes more telling, and we turn to the following evolution proposal was implemented in Swift 3:
SE-0035: Limiting inout capture to #noescape contexts
Stating [emphasis mine]:
Capturing an inout parameter, including self in a mutating
method, becomes an error in an escapable closure literal, unless the
capture is made explicit (and thereby immutable).
Now, this is a key point. For a value type (e.g. struct), which I believe is also the case for the type that owns the call to observeSingleEvent(...) in your example, such an explicit capture is not possible, afaik (since we are working with a value type, and not a reference one).
The simplest solution to this issue would be making the type owning the observeSingleEvent(...) a reference type, e.g. a class, rather than a struct:
class Foo {
init() {}
private func bar(with block: #escaping () -> ()) { block() }
func bax() {
bar { print(self) }
}
}
Just beware that this will capture self by a strong reference; depending on your context (I haven't used Firebase myself, so I wouldn't know), you might want to explicitly capture self weakly, e.g.
FirebaseRef.observeSingleEvent(of: .value, with: { [weak self] (snapshot) in ...
Sync Solution
If you need to mutate a value type (struct) in a closure, that may only work synchronously, but not for async calls, if you write it like this:
struct Banana {
var isPeeled = false
mutating func peel() {
var result = self
SomeService.synchronousClosure { foo in
result.isPeeled = foo.peelingSuccess
}
self = result
}
}
You cannot otherwise capture a "mutating self" with value types except by providing a mutable (hence var) copy.
Why not Async?
The reason this does not work in async contexts is: you can still mutate result without compiler error, but you cannot assign the mutated result back to self. Still, there'll be no error, but self will never change because the method (peel()) exits before the closure is even dispatched.
To circumvent this, you may try to change your code to change the async call to synchronous execution by waiting for it to finish. While technically possible, this probably defeats the purpose of the async API you're interacting with, and you'd be better off changing your approach.
Changing struct to class is a technically sound option, but doesn't address the real problem. In our example, now being a class Banana, its property can be changed asynchronously who-knows-when. That will cause trouble because it's hard to understand. You're better off writing an API handler outside the model itself and upon finished execution fetch and change the model object. Without more context, it is hard to give a fitting example. (I assume this is model code because self.img is mutated in the OP's code.)
Adding "async anti-corruption" objects may help
I'm thinking about something among the lines of this:
a BananaNetworkRequestHandler executes requests asynchronously and then reports the resulting BananaPeelingResult back to a BananaStore
The BananaStore then takes the appropriate Banana from its inside by looking for peelingResult.bananaID
Having found an object with banana.bananaID == peelingResult.bananaID, it then sets banana.isPeeled = peelingResult.isPeeled,
finally replacing the original object with the mutated instance.
You see, from the quest to find a simple fix it can become quite involved easily, especially if the necessary changes include changing the architecture of the app.
If someone is stumbling upon this page (from search) and you are defining a protocol / protocol extension, then it might help if you declare your protocol as class bound. Like this:
protocol MyProtocol: class {
...
}
You can try this! I hope to help you.
struct Mutating {
var name = "Sen Wang"
mutating func changeName(com : #escaping () -> Void) {
var muating = self {
didSet {
print("didSet")
self = muating
}
}
execute {
DispatchQueue.global(qos: .background).asyncAfter(deadline: .now() + 15, execute: {
muating.name = "Wang Sen"
com()
})
}
}
func execute(with closure: #escaping () -> ()) { closure() }
}
var m = Mutating()
print(m.name) /// Sen Wang
m.changeName {
print(m.name) /// Wang Sen
}
Another solution is to explicitly capture self (since in my case, I was in a mutating function of a protocol extension so I couldn't easily specify that this was a reference type).
So instead of this:
functionWithClosure(completion: { _ in
self.property = newValue
})
I have this:
var closureSelf = self
functionWithClosure(completion: { _ in
closureSelf.property = newValue
})
Which seems to have silenced the warning.
Note this does not work for value types so if self is a value type you need to be using a reference type wrapper in order for this solution to work.

Swift - CompletionHandler templated as parameter in function (UsingObjectMapper)

I'm trying to pass a completionHandler as a parameter in a function (no problem here).
My problem is that I have multiple precise Types possible that I can recieve in my completionHandler function.
So I thought, "Let's use templates", and I tried.
This is the scheme I want to use:
FuncA(completionHandler as MyType?)
-> FuncB(..){completionHandler(Mappable?)}
-> FuncC(sender: T?){performSegueWithIdentifier("segue", sender)}
Problem:
Func A is printing me an error
Func B seems to be ok
Func C seems to be ok
Do you guy know how to do that, I'm not used to templates yet ??
Thanks for any help :)
I don't believe you can cast completionHandler like that in a method signature. You're going to need to do your typecasting inside the method body. e.g.
typealias handler = () -> Array<AnyObject>
funcA(handler)
func funcA<T>(completion: T?) -> funcB {
if let completion = completion as? handler {
let array = completion()
//do whatever you want here
}
}

Trouble with basic callback blocks in Swift

brand new to Swift here. Trying to figure out how to do a very simple call back block from any asynchronous function I write.
For example:
func downloadData(completion: (success: Bool) -> Void){
let success: Bool
//Some asynchronous task here
success = true
//Asynchronous task finished
//Now I want to pass this back
completion(success)
}
I want to be able to call this function and get the value of the success variable in the block. However I'm getting an error "Missing argument label success in call". Don't understand what is going on here. Why would I need to include the argument label? Any pointers on this would be greatly appreciated!
You have the choice:
Either you add the label in the call
completion(success: success)
or you omit the label in the declaration
func downloadData(completion: (Bool) -> Void){
The rule is: all declared labels must be passed.

Translating ObjC-Blocks to Swift Closures

I am trying to translate some objective-C Code into Swift. I added the Cocoapod "Masonry" for Autolayout to my project and added a Bridging-Header in order to able to use Objective-C Methods in Swift.
This ObjC Method:
[_tableView mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(self.view);
}];
should be something like the following Closure:
tableView.mas_makeConstraints({ (make : MASConstraintMaker!) -> Void? in
make.edges.equalTo(self.view)
})
But I am getting an "Could not find member 'mas_makeConstraints'" which is not the error, as the method is indexed and autocomletion gives me the following:
tableView.mas_makeConstraints(block: ((MASConstraintMaker!) -> Void)?)
?!
Am I doing something wrong here?
Just my 2 cents if anyone encounter this case:
This objc
[productView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.view.mas_left).with.offset(15);
make.right.equalTo(self.view.mas_right).with.offset(15);
make.top.equalTo(self.view.mas_top).with.offset(15);
make.height.equalTo(productView.mas_width);
}];
will turn into
productView.mas_makeConstraints{ make in
make.left.equalTo()(self.view.mas_left).with().offset()(15)
make.right.equalTo()(self.view.mas_right).with().offset()(-15)
make.top.equalTo()(self.view.mas_top).with().offset()(15)
make.height.equalTo()(productView.mas_width)
return ()
}
This piece here in the method signature:
(block: ((MASConstraintMaker!) -> Void)?)
...is telling you that the block argument is Optional, not the return value, which should be Void (not Void?, where you write (make: MASConstraintMaker!) -> Void?)
also: because Swift Type Inference you don't need to put the types in the block
also also: because Swift Trailing Closures you don't need to put a closure that is the final argument to a method inside of the argument list in parens (and since here it's the only argument, you can leave off the parens entirely)
so really your entire method call with block argument could be re-written as:
tableView.mas_makeConstraints { make in
make.edges.equalTo(self.view)
}
finally: it looks like the instance method you are calling on make.edges returns a block, and thanks to the Swift convenience feature of 'implicit return of single expression blocks' it's may be the case that your block is implicitly returning the value of that expression when it is expecting Void - so in the end, if the above doesn't work you may still need to explicitly return Void by writing your method call as:
tableView.mas_makeConstraints { make in
make.edges.equalTo(self.view)
return ()
}

Resources