Inout variable set in in UIAlertAction closure not changing value - ios

I have a function with an inout paramter: enabled: Bool
The object I'm referencing (I know inout isn't technically a reference...) and setting using this method is a stored property on a UIViewController
var enabled = false
I have multiple booleans triggering different things, and I want to use one method to set them.
So I call the method:
self.determineEnabled(&self.enabled)
Below is the code, and I've used comments to explain whats happening
Code:
func determineEnabled(inout enabled: Bool) {
if enabled == false {
enabled = true
//self.enabled equals true now. This works. Its not in a closure...
} else {
let delete = UIAlertAction(title: "Disable", style: .Destructive, handler: { (action) -> Void in
enabled = false
print(self.enabled)
//This doesn't work. the inout variable equals FALSE
//self.enabled equals true
//If I set self.enabled = false.. Then it works, but I'm using a method because my app could have dozens of these enabled bools on this view controller.
let alertController = UIAlertController(title: "Change Bool", message: "", preferredStyle: UIAlertControllerStyle.Alert)
alertController.addAction(delete)
self.presentViewController(alertController, animated: true, completion: nil)
}
}
My app is obviously a more complex than this chunk of code, but I can verify this problem exists within this chuck of code.
I'll be honest that I don't thoroughly understand closures as much as I'd like..
But if I can use self.enabled to correctly change the value of enabled, what is stopping swift from changing setting the inout enabled variable?
UPDATE:
Here is a link from the docs that specifically mention my problem:
https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/doc/uid/TP40014097-CH34-ID545
"There is no copy-out at the end of closures or nested functions. This means if a closure is called after the function returns, any changes that closure makes to the in-out parameters do not get copied back to the original."
Swift evolution on the topic:
https://github.com/apple/swift-evolution/blob/master/proposals/0035-limit-inout-capture.md

In Swift, functions are closures. Closures capture state. At the time we encounter the anonymous function containing the code print(self.enabled), self.enabled is true. We know that, because if it were not, we wouldn't be here at all (we'd be in the first wing of the condition, if enabled == false). Therefore when print(self.enabled) is later actually executed, it will print true, because that was the state of things when it captured its value.

You said it yourself in your question;
I know inout isn't technically a reference
From the Apple Swift book
An in-out parameter has a value that is passed in to the function,
is modified by the function and is passed back out of the function
to replace the original value.
You are modifying the value in a closure that executes some time later when the user interacts with the alert. At this point determineEnabled has already returned and stored the value of the inout parameter.
If an inout parameter was a reference, like a C-style pointer, then enabled would be pointing to a chunk of memory that stores self.enabled and when the value was modified in the closure, self.enabled would be modified.
You can see how this works if you create a simple class with a boolean property and then pass an instance of this class to your determineEnabled function (without using inout). Since objects are passed by reference, a subsequent update to the object's property in the closure will be visible anywhere that same object reference is used;

Related

Property is accessed but result is unused

I saw there another similar one to my question but that was very old! , So I have an UIActivityIndicatorView but when i try to call the .hidesWhenStopped it just warns Property is accessed but result is unused for no reason?
What could be the problem?
I tried under viewDidLoad too but same :(
This is a part of my code:
Timer.scheduledTimer(withTimeInterval: 3, repeats: false) { [self] _ in
monitorimiTableView.reloadData()
configureTableView()
loaderMonitorimi.hidesWhenStopped // Property is accessed but result is unused
}
You need to assign a value to the property:
loaderMonitorimi.hidesWhenStopped = true
(or false if you don't want to hide it)
If you just write loaderMonitorimi.hidesWhenStopped without any assignment, this expression will result in the boolean value stored in the hidesWhenStopped property, but the result of the expression is not used, hence the warning.

Why does my Xcode compiler tell me I use a value type even though I use classes?

I have two classes: Player and Enemy that both conform to the protocol CharacterCharacteristicsProtocol:
class Enemy: CharacterCharacteristicsProtocol {...
class Player: CharacterCharacteristicsProtocol {...
They are both references and not values like structs are; still when I send in the objects as arguments like this:
viewModel.combatButtonIsClicked(attacker: self.player, defender: self.enemy) { result in...
I get the error
Passing value of type 'CharacterCharacteristicsProtocol' to an inout parameter requires explicit '&'
Why is this showing up? Isn't this only supposed to happen with structs?
If I do as the compiler wish and inset inout and & at the appropriate places things work except for in closures where the error now is
Escaping closure captures 'inout' parameter 'characterVM'
Here's where it happens (just for completion):
func enemyTurn(enemyVM: CharacterCharacteristicsProtocol, characterVM: inout CharacterCharacteristicsProtocol, completion: #escaping(_ enemyReponse: String) -> Void){
let xEnemy = enemyVM.getX()
let yEnemy = enemyVM.getY()
viewModel.enemyShouldMove = true
viewModel.proximityCheck(checkProxyForWho: .enemy, i: xEnemy, j: yEnemy, completion: {
let combat = Combat()
combat.combat(attacker: enemyVM, defender: &characterVM, completion: { result in...
I have searched on how to solve this error and the get the following suggestion: "change the struct to a class"...
Is your protocol class bound? If not, the compiler needs to assume that a struct may also implement it and needs to apply value semantics.
To make a protocol class bound you simply need to do like this:
protocol CharacterCharacteristicsProtocol: class
Then you will only be able to implement it with classess, and not structs, and the compiler will be able to assume that only reference semantics apply.

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

Anonymous closure arguments cannot be used inside a closure that has explicit argument

I am relatively new to Swift and is still grasping the concept of closures.
I have already read this post(Anonymous closure can not be used inside a closure that has explicit arguments).
However,the answer is to change the filter from () to {} but I do not know how to implement that to my function.
<<< ImageRow()
{
$0.tag = "Image"
$0.title = "Choose your profile pic"
if let tutorPic = currentuser!.objectForKey("ProfPhoto") as! PFFile!
{
tutorPic.getDataInBackgroundWithBlock({(imageData:NSData?,error:NSError?)->Void in
if(error == nil)
{
let image = UIImage(data: imageData!)
print("YOOWAHH")
print(image)
print("***********")
self.imagez = image
print(self.imagez)
$0.value = imagez
}
})
}
}
The error is at line $0.value = imagez.
I downloaded the image data from Parse and want to set it as my default value for my form.However the compiler says I already have explicit arguments so it does not know how to refer to form's arguments instead.How do I fix this?
The problem is that because each block is handled separately for dispatch and such, that it does not know how to properly make a reference back to the other block for $0. Regardless of whether you explicitly defined the enclosing block, the compiler is going to assume that that's the block you meant when you say $0.
To resolve this, simply say in your top block: let myButton = $0, and then refer to myButton in the enclosing block.
In the future, if you don't know what the form of the block should be, simply re-write out the function call, and autocomplete will bring the rest of the block format back.

IF Statement Incorrectly Evaluating to True when Using Optional Values in Swift

I have set up my view controllers so that they send a notification once their -viewDidLoad method is about to return. For example:
class MyViewController: UIViewController{
override func viewDidLoad() {
super.viewDidLoad()
//Do Stuff
var notificationCenter = NSNotificationCenter.defaultCenter();
notificationCenter.postNotificationName("AViewControllerDidLoadNotification", object: self);
}
}
My AppDelegate class is listening for this notification and implementing the method shown in this picture.
In case the picture isn't loading, the method takes the notification sent by the view controllers as it's only argument and then tests whether the UIViewController's title property has a non-nil value. If the title property is non-nil it logs the title.
However, as you can see in the debugger panel, the title property of the view controller is nil and the if statement is still evaluating to true.
I am admittedly new to optional values. But I have recreated this situation in a swift playground and the if statement evaluates to false. Any ideas?
You've gotten yourself into rather an odd situation with your very peculiar use of the expression notification.object?.title, because notification.object is not, of itself, a UIViewController. It is an AnyObject.
Now, an AnyObject has no known properties, so it has no title and your expression, it would seem, should not even compile. But, by a special dispensation coming from certain oddities of Objective-C, you are in fact allowed to ask about an AnyObject's properties anyway. But when you do, the result is itself an Optional, because no such property might exist.
Thus, you are actually testing, not the value of a view controller's title property, but whether this unknown object has a title property in the first place; and if in fact it does have a title property at all, the value of that title property is double-wrapped inside that Optional.
To see this clearly, just test this (silly) code:
let n = NSNotification(name: "Howdy", object: "Hi")
let t = n.object?.title
Look at what type t is. It is not a String?; it is a String??. That's your double-wrapped Optional. This means that it would be an Optional-wrapping-a-String in case this object turns out to have a title property, but just in case, that value has itself been wrapped in an Optional.
Thus, your test doesn't do what you want it to do. To do what you want to do, just speak much more plainly and simply. You need to cast the object to a UIViewController first, and then examine its title. Like this:
func aViewControllerDidLoad(notification:NSNotification) {
if let vc = notification.object as? UIViewController {
if vc.title != nil {
// ...
}
}
}

Resources