Change boolean value on Parse via UIButton? - ios

How can I change the boolean value of my Parse class named "shoppingCart" by clicking a button?
#IBAction func addToCart(sender: AnyObject) {
var cartObjectStatus = itemObject.valueForKey("shoppingCart") as! Bool!
cartObjectStatus = false
}

itemObject.setBool(false, forKey: "shoppingCart")
The problem with your original attempt is that you're setting the value of the local variable to false. Using the above setBool method will alter the object's variable.
Edit - If PFObject doesn't support setBool, you'll have to go with setValue:forKey:
itemObject.setValue(false, forKey: "shoppingCart")
This is a method on NSObject, which PFObject has to derive from.

You need to call setValueForKey on the itemObject. When you get the bool with valueForKey it's passed by value, not reference, so changing it like you are will not update itemObject's version of it.
I suggest reading both Parse documentation and some tutorial on Swift basics.

Related

NSUserDefaults boolForKey

I'm trying to get NSUserDefaults to work in my app. The code below is supposed to check if there is a bool value in the NSUserDefaults called "iCloudOn". If there is, it then assigns the value of a UISwitch to the NSUserDefault. If there is not, it goes ahead and assigns false to the NSUserDefault.
I have marked the line that I am getting the error on. The error I receive is "Bound value in a conditional binding must be of Optional type." I can't figure out why I am getting this error and what I need to do to make this work. Can anyone help shed some light?
class SettingsTableViewController: UITableViewController{
#IBOutlet weak var iCloudUISwitch: UISwitch!
let appSettings = NSUserDefaults.standardUserDefaults()
override func viewDidLoad() {
super.viewDidLoad()
//THIS IS THE LINE I AM GETTING AN ERROR ON
if let iCloudOn = appSettings.boolForKey("iCloudOn") {
//iCloud is on
iCloudUISwitch.on = appSettings.boolForKey("iCloudOn")
}
else {
//Nothing stored in NSUserDefaults yet. Set a value.
appSettings.setValue(false, forKey: "iCloudOn")
}
}
The function boolForKey does not return an optional. It always returns true or false. If the key doesn't exist in user defaults, it returns false.
You should use objectForKey, which returns AnyObject?, then cast it to a Bool.
Edit:
If this function was being written today for Swift it would almost certainly return a Bool? type (Optional Bool) This would be a perfect use-case for an optional. However, NSUserDefaults was defined and written a LONG time before Swift (It was part of NextStep)
You can just use appSettings.boolForKey('iCloudOn') without the if let to get your value. According to the documentation false is returned if no value is associated to the key.
NSUserDefaults.boolForKey

SWIFT: "removeAtIndex" don't work with (sender:UIButton)

#IBOutlet var items: [UIButton]
#IBAction func itemsHidden(sender: UIButton) {
sender.hidden = true
items.removeAtIndex(sender)
}
Hello.
For example, i have array of items.
The code has the error: "Cannot invoke 'removeAtIndex' with an argument list of type (UIButton)".
What i need to do, that "removeAtIndex" works?
Thanks...
A removeAtIndex method expects to get an index as a parameter.
If you want to remove an object use func removeObject(_ anObject: AnyObject)
EDIT
There's no removeObject in a swift's array (only in NSMutableArray).
In order to remove an element, you need to figure out it's index first:
if let index = find(items, sender) {
items.removeAtIndex(index)
}
You don't tell us the class of your items object.
I assume it's an Array. If not, please let us know.
As Artem points out in his answer, removeAtIndex takes an integer index and remove the object at that index. The index must be between zero and array.count-1
There is no removeObject(:) method for Swift Array objects because Arrays can contain the same entry at more than one index. You could use the NSArray method indexOfObject(:) to find the index of the first instance of your object, and then removeAtIndex.
If you're using Swift 2, you could use the indexOf(:) method, passing in a closure to detect the same object:
//First look for first occurrence of the button in the array.
//Use === to match the same object, since UIButton is not comparable
let indexOfButton = items.indexOf{$0 === sender}
//Use optional binding to unwrap the optional indexOfButton
if let indexOfButton = indexOfButton
{
items.removeAtIndex(indexOfButton)
}
else
{
print("The button was not in the array 'items'.");
}
(I'm still getting used to reading Swift function definitions that include optionals and reference protocols like Generator so the syntax of the above may not be perfect.)

How to know the class of an object at runtime?

I don't understand why I could not find this question somewhere as I think it's a pretty common one so maybe I'm not well awake. Sorry for that if it's the case.
I have my prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) function and I cannot find out what class is sender. I don't want to try every classes of the Foundation framework so is there a way to know it at runtime.
When I use the debugger I only get (AnyObject!) sender = (instance_type = Builtin.RawPointer = ... which doesn't really help.
Instead of asking the object for its class, I find it more "Swifty" to use "if let" to check if it is what I am looking for.
func inputUnknown(sender : AnyObject) {
if let tableView = sander as? UITableView {
// now you have an object with a defined class
}
else {
// check for other classes or proceed with a default action
}
}
Every object has an underlying dynamicType property, which, in theory, should reveal the actual type of the object, when printed to the console. However, Swift doesn't yet have support for printable metatypes, so you'll get ExistentialMetatype for an instance's dynamicType and MetaType for a class's type. In order to get around this, you can cast your Type variable to AnyObject, which, when printed to your console will print the Objective C metatype.
if let object = sender {
println(object.dynamicType as AnyObject)
}

Strange behaviour when naming variable in lowerCamelCase

I came across a strange behaviour in Swift while programming a Master-Detail application.
Here's the scenario:
It's a simple Task Manager application. I have two text controls (TaskName, TaskDescription) on the TaskDetailView and two string variables with the same name but in lowerCamelCase (taskName, taskDescription) declared in the TaskDetailViewController.
#IBOutlet var TaskName:UITextField! //UpperCamelCase
#IBOutlet var TaskDescription:UITextView! //UpperCamelCase
var taskName:String? //lowerCamelCase
var taskDescription:String? //lowerCamelCase
I am setting the values of Text controls on ViewDidLoad() as usual:
override func viewDidLoad() {
super.viewDidLoad()
TaskName.text = taskName
TaskDescription.text = taskDescription
}
And I am passing the data in prepareForSegue (from TaskListViewController) as usual:
override func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!) {
if(segue.identifier == "TaskListSegue"){
let detailViewController = segue.destinationViewController as ToDoTaskViewController
let (task, desc) = m_ToDoListManager.GetTask(TaskListView.indexPathForSelectedRow().row)
println("selected \(task) \(desc)")
detailViewController.taskName = task
detailViewController.taskDescription = desc
}
}
The way everything is implemented is correct.
But now when you run the application, the values of text controls are not set.
In fact, the values of the variables also are not set.
What must be happening here?
I have already investigated this problem and also came up with a solution (see my answer below). Please also see Martin R's answer below for a detailed explanation. I just wanted to share this with everyone. I am not sure if anyone has come across this issue.
Update:
Here's the actual code:https://github.com/Abbyjeet/Swift-ToDoList
Here is an explanation:
Your Swift class is (ultimately) a subclass of NSObject.
Therefore the properties are Objective-C properties with getter and setter method.
The name of the setter method for a property is built by capitalizing the first
letter of the property name, e.g. property "foo" has the setter method setFoo:
As a consequence, the setter method for both properties TaskName and taskName is called setTaskName:.
In an Objective-C file, you would get a compiler error
synthesized properties 'taskName' and 'TaskName' both claim setter 'setTaskName:' - use of this setter will cause unexpected behavior
but the Swift compiler does not notice the conflict.
A small demo of the problem:
class MyClass : NSObject {
var prop : String?
var Prop : String?
}
let mc = MyClass()
mc.prop = "foo"
mc.Prop = "bar"
println(mc.prop) // bar
println(mc.Prop) // nil
In your case
TaskName.text = ...
sets the "taskName" property, not the "TaskName". The properties have different type,
so that the behavior is undefined.
Note that the problem does only occur for "Objective-C compatible" properties. If you remove the
NSObject superclass in above example, the output is as expected.
Conclusion: You cannot have two Objective-C properties that differ only in the
case of the first letter. The Swift compiler should fail with an error here (as the
Objective-C compiler does).
The problem you were facing with was not connected to the swift language. Method prepareForSegue is called before loadView. That mean UITextField and UITextView are not initialized yet. That's why fields were not initialized.
You also asked: Why compiler doesn't show any error? That's because any selector performed on nil object doesn't throw an exception. So for example (sorry for obj-c):
UITextField *tf = nil;
[tf setText:#"NewText"];
Will not show any error.
As you said on your own answer to solve your problem you need to add additional fields to your destination controller (copy-paste):
var tAskName:String? //cUstomCamelCase
var tAskDescription:String? //cUstomCamelCase
Why is it happening?
I believe that internally Swift is using lowerCamelCase for text controls names which are not yet initialized and thus failing to set the values. But it is also strange that I didn't get any kind of error.
How did I solve it?
I know that the Swift is case-sensitive. So that was not the issue. So I just changed the case of one letter and named the variables as (tAskName, tAskDescription) and the values were set as expected.
#IBOutlet var TaskName:UITextField! //UpperCamelCase
#IBOutlet var TaskDescription:UITextView! //UpperCamelCase
var tAskName:String? //cUstomCamelCase
var tAskDescription:String? //cUstomCamelCase
So the conclusion is that if I have a control named TaskName, I cannot have a variable named as taskName

Swift + CoreData: Can not set a Bool on NSManagedObject subclass - Bug?

I have a little strange issue which I can't seem to figure out, I have a simple entity with a custom NSManagedObject subclass:
#objc(EntityTest) class EntityTest: NSManagedObject {
#NSManaged var crDate: NSDate
#NSManaged var name: String
#NSManaged var completed: Bool
#NSManaged var completedOn: NSDate
}
This is the problem, I can create the object fine and set the all the values and store in in an array. However late on, when I try to retrieve the same object, I can set all the values EXCEPT the "completed" field. I get a run-time error saying "EXC_BAD_ACCESS", I can read the value, just can not set it.
The debugger points to:
0x32d40ae: je 0x32d4110 ; objc_msgSend + 108
0x32d40b0: movl (%eax), %edx
Maybe some issues due to it being treated as an Objective-C class and trying to send a message to set boolean which I know is a bit funny with CoreData originally representing them as NSNumbers.
Any ideas? I created the class myself, it is not generated.
EDIT:
entity.crDate = NSDate() // succeeds
entity.completed = false // fails
entity.completed.setValue(false, forKey: "completed") //succeeds
So for setting the bool, using the setValue of NSManagedObject works but not the direct setters, though for the non-bool properties, I can set it using the setters.
UPDATE:
While checking this a bit more, it seems like the first time I set the value after getting from NSEntityDescription, it uses normal Swift accessor methods. Later on when I try to access the same object (which was stored in an array) it attempts to treat it as a Objective-C style object and sends a message for method named "setCompleted". I guess it makes sense since I use the dot notation to access it and I used the #objc directive.
I tested this by creating a "setCompleted" method, however in the method I set the value using "completed = newValue" which makes a recursive call back to "setCompleted" causing it to crash... Strange, so at this moment still can't don't have a proper fix. It seems to only happen with Bools.
Only workaround is use the "setValueForKey" method of NSManagedObject. Perhaps file this as a bug report?
If you let Xcode 6 Beta 3 create the Swift files for your entities, it will create NSNumber properties for CoreDatas Boolean type.
You can however just use Bool as a Swift type instead of NSNumber, that worked for me without using the dot syntax though.
It will set the Swift Bool with a NSNumber, that maybe leads to a bug in the dot syntax.
To make it explicit you should use the type NSNumber for attributes in the entity with the Boolean type. Then create a computed property (in iBook The Swift programming language under Language Guide -> Properties -> Computed Properties) to return you a Swift Bool. So would never really store a Bool.
Like so:
#NSManaged var snack: NSNumber
var isSnack: Bool {
get {
return Bool(snack)
}
set {
snack = NSNumber(bool: newValue)
}
}
Of course it would be cool to hide the other (NSNumber attribute), but be patient and Apple will implement private attributes in the future.
Edit:
If you check the box create skalar types it will even use the type Bool in the automatically created Swift file!
So I think it is a bug.
Following on from CH Buckingham who is entirely correct. You are attempting to store a primitive type in core data where it is expecting an NSNumber.
The correct usage would be entity.completed = NSNumber.numberWithBool(false)
This is also why you cannot retrieve this completed value as a bool directly and thus you would need to write:
var: Bool? = entity.completed.boolValue()
You can downcast your property from NSNumber to Bool type like this:
var someBoolVariable = numberValue as Bool
It works for me in this way:
self.twoFactorAuthEnabledSwitch.enabled = userProfile?.twoFactorEnabled as Bool
In XCode 8 Just set :
user.isLoggedIn = true
its works like a charm
I haven't touched swift, but in Objective-C, a BOOL is not an object, and cannot be an object. It's a primitive, and it looks like you are attempting to tell an Objective-C class to treat a BOOL like an object. But I could be making that up, as I'm not familiar with what #NSManaged does under the hood.
https://developer.apple.com/library/ios/documentation/cocoa/conceptual/ProgrammingWithObjectiveC/FoundationTypesandCollections/FoundationTypesandCollections.html
I found that it works fine if you specify the class name and module in the data model (instead of leaving the default NSManagedObject).
Once I set that, I can use Bool, Int16, Int32, etc., without any problems.

Resources