Why isn't this array of dictionaries being copied/ assigned? - swift

I am trying to fill a dictionary in a completion block but the dictionary is always empty when I print it. I have tried just assigning it, and also using a loop to append each element individually...Any insight is appreciated! The original dictionary (returned in the completion block is populated)
the data is an array of dictionaries.
var friendsList : [[String : AnyObject]] = [[:]]
fetchFriends(username: username) { (listOfFriends) in
print("data set is:") <-----This is working, the data set is full
print(listOfFriends)
//self.friendsList = listOfFriends <---this code didn't work
for person in listOfFriends { <-------this code didn't work
for (key,value) in person {
let friend = [ key : value ]
self.friendsList.append(friend)
}
}
}
I changed the code inside the completion block to this (Don't see why this wouldn't work either...)
for person in listOfFriends {
print(person) <-----this prints the correct information
self.friendsList.append(person) <----this is working here
print(self.friendsList) <---prints as expected
}
}
Then when I print friendsList in viewDidLoad AFTER calling this function to fill it, it just prints [[:]] to the console

The reason you find it empty in viewDidLoad is that the call fetchFriends is asynchronous, so it's completed after viewDidLoad is called.
What happens is:
you call ferchFriends
controller is loaded and viewDidLoad is called
the property is still empty
fetchFriends completes and property array is filled properly

Related

Unable to access variable outside of query function even though variable is declared after class

I have a few queries running to load variables with data from a Parse server into variables that are declared right below class. When I print the variable to the console within the query function, it prints correctly, but when I call it outside of the function or print it, it is empty. Any idea where I'm going wrong?
Variable declaration:
class AddTaskViewController: UIViewController, UITextFieldDelegate, UIPickerViewDataSource, UIPickerViewDelegate {
var varCost = ""
Query loading variable:
let varCostQuery = PFQuery(className: "Class1")
varCostQuery.findObjectsInBackground(block: { (objects, error) in
if error != nil {
print(error!)
} else {
varCostQuery.whereKey("Header", equalTo: self.HeaderSelection.text)
varCostQuery.findObjectsInBackground(block: { (objects, error) in
for object in objects! {
self.varCost = object["pricePerUnit"] as! String
print(self.varCost) //Option1
}
})
}
})
print(varCost) //Option2
When I print Option1, I get the data exactly like I'm looking for, but when I print Option2 or try to do anything with the varCost variable at this level, I get "" like the variable has never been updated.
This occurs because the code being passed to the findObjectsInBackground method is code that is run only once all the objects have been found.
Because this code will only be called when the objects have been found, this may take a bit of time, so this code is send to a background queue to wait for the objects to be found.
We don't want the rest of our code to pause and wait for this though! That would slow our program down a lot. So the program continues past this block until it completes. That's why Option 2 is empty, because findObjectsInBackground hasn't had time to get the objects yet so the code has jumped straight to where Option 2 is.
When the objects have finally been found, the block of code is called and Option 1 is printed.
This means that you can only be sure self.varCost will have the right value from within this block (or closure, as it is called in Swift).

Setter for dictionary property - OR: get last added item from dictionary

I have a custom class with different computed properties. One of them is a Dictionary of [String: String]. The getter is no problem, but I don't know how to use the setter: How can I figure out, what was the last value added to the dictionary? Obviously newValue.last doesn't exists (.first does!).
EDIT:
This seems to work:
var myProp: [String: String] {
get { ... }
set {
let lastVal = newValue[newValue.startIndex.advancedBy(newValue.count-1)]
...
}
BUT: will this always return the last added value?
EDIT 2
The first edit is wrong. A dictionary is unordered and with this way it's not sure, if it really returns the last added key and value. See my answer below.
As you point out, a Dictionary is an unorderd collection of key-value pairs, so there is no last getter (first is just a convenience for what in Objective-C was more appropriately called anyObject) . The Dictionary also does not keep track of the order items were added.
To get the last item, there are two possibilities. You could refactor to use an array, e.g. of tuples (key, value); or you could keep track of the last item added in a separate variable.
But maybe there is a misunderstanding about the "setter". A setter sets the entire object.
set { myProp = newValue }
So if you have a myProp = ["foo": "bar"], the entire dictionary in myProp is overwritten with this data.
What you want is to add a key to the property. In Swift, this is done by subscripting.
myProp["foo"] = "bar"
You do not have to implement anything special in the get closure.
Note that you have to remember two things, though: first, the dictionary has to be properly initialized; second, any existing item will be overwritten if the new value uses the identical key.
I understand now... the dictionary is unordered. To really get the last added value, I have to compare the value itself with the newValue. The working code:
var myProp: [String: String] {
get { // doing things to read the things and add them to a dictionary }
set {
var new = newValue
for (key, value) in myProp {
if new[key] == value {
new.removeValueForKey(key)
}
}
// now 'new' should only have one key and one value, that one, that just was added
}
}

Swift Looping persistence of variables / scoping

So this will be very abstract code however what i am doing it looping through an array of objects, calling a helper function to parse the complex object from json and insert it into an array. The issue I am having is that the object I am parsing into does not get cleared each time through the loop:
for item in parsedEvolutionList {
if let abilityKey: String = item.allKeys[0] as? String{
if let abilityData = item[abilityKey] as? NSDictionary{
if var newAbility = EvolutionAbilityStore.SharedInstance.retreaveEvolutionAbilityByKey(abilityKey) {
newAbility.parseFromJson(abilityData)
if evolutionList == nil {
evolutionList = []
}
evolutionList?.append(newAbility)
} else {
log.warning("Atempting to parse EvolutionAbility and \(item) was not found in the store")
}
} else {
log.warning("Atempting to parse EvolutionAbility and \(item) does not have data for ability to parse")
}
} else {
log.warning("Atempting to parse EvolutionAbility and \(item) is formatted incorectaly")
}
}
So the newAbility is an array, it contains the content of the first parsed object after the first loop, on second pass already has the content of the first pass and adds the content of the second pass, so on and so forth, then when adding to the evolution list it adds another pointer to the same object so all my objects in the evolutionList end up being objects to the same pointer. Why is newAbility persisting through iterations of the loop? that makes no sense to me with how that interferes with scoping
Oh oh oh, the creation of the object comes from a singleton, which does persist by designe ... has nothing to do with percectance of data in the loop!

how to avoid an object being deallocated?

static func appendMissingObjToArray(var oldArray:[AnyObject],newArray:[AnyObject]){
for n in newArray{
var isExist = false
for o in oldArray{
if(n.isEqual(o)){
//exist
isExist = true
break
}
}
if(!isExist){
oldArray.append(n)
}
}
}
The above function is the append some data from newArray to oldArray.
when this function is done. And getting data from the oldArray, I got BAD ACCESS error. so I think it is due to the newly added object in oldArray have been deallocated and the newArray is released.
Any thing I can do for avoid this?
I believe that you need to declare your oldArray parameter as an inout. As follows:
//...
static func appendMissingObjToArray(inout oldArray:[AnyObject],newArray:[AnyObject]){
//...
You can then do e.g (note the ampersand & below:
class01.appendMissingObjToArray(&myOldArray, newArray: myNewArray)
println(myOldArray) // will contain the appended result
Done like this myOldArray (the passed array) will be mutated. So you may need to pass a copy of your original if retaining the original is important.

Updating SwiftyJSON object created from an Alamofire request

I'm trying to display some JSON data pulled from a website in a table view. Initially, everything works great. I declare my JSON data as an instance varable:
var tableData:JSON!
{
didSet
{
tableView.reloadData()
}
}
And then I make my request somewhere-- viewDidLoad or viewWillAppear or via the user pulling down on the tableView to refresh or whatever, doesn't really matter-- set the data, and the table gets reloaded. Everything works great.
request(method, url, parameters: parameters, encoding: ParameterEncoding.URL).responseJSON(options: NSJSONReadingOptions.AllowFragments) { (request, response, json, error) -> Void in
self.tableData = JSON(json!)
}
However, sometimes I don't just want to display the data, but I want to let the user update the data, for example, in a textField. And here's where I'm running into problems.
So I've created a table view cell with a text field, filled it with some text pulled from my tableData JSON, so far so good. My user can then change the text field, and I want to update the tableData via the text field's delegate methods. This is not working for me.
func textFieldDidEndEditing(textField: UITextField)
{
let key = "anExampleKey"
tableData[key] = JSON(textField.text)
println(tableData[key]) // the original value, not textField.text
}
Nothing seems to happen, the tableData value for "anExampleKey" remains unchanged. So, if that cell scrolls off the screen for example and then comes back into view, any changes my user has made are lost and it reverts back to the original value.
This seems to be an issue solely with a JSON value created from an Alamofire request however. For example, if I do this:
func textFieldDidEndEditing(textField: UITextField)
{
let key = "anExampleKey"
var json = JSON([key:tableData[key].stringValue])
tableData[key] = JSON(textField.text)
json[key] = JSON(textField.text)
println(tableData[key]) // the original value, not textField.text
println(json[key]) // textField.text
}
The json variable will be updated to reflect the textField, while the tableData will still have its original value, not what's in the textField. Does anyone know why the JSON object made via the Alamofire request is immutable, whereas one created directly from a dictionary is not?
Ok, I figured it out. It has nothing to do with SwiftyJSON or Alamofire, but rather the fact that I declared my instance variable as optionally unwrapped. I'm still not entirely sure about all the rules for optional and implicitly unwrapped variables, but using regular JSON variable instead of JSON! allowed me to update it.
var tableData:JSON! /* won't update */
var tableData:JSON = JSON([:]) /* will update */
I have no idea why this is the case. I thought I had my head around the Swift optional and implicitly unwrapped rules by now, but I guess not. It's almost like every time I implicitly unwrap a JSON! variable, it creates a new copy and updates that, leaving the instance variable unchanged. I fired up a playground to see if that was true for all implicitly unwrapped structs, and it's not, I can update the instance values for an implicitly unwrapped struct just fine in there. Interesting, optional JSON? values do update.
struct Test
{
var myVar = 0
}
var myTest:Test!
myTest = Test() /* myVar = 0 */
myTest.myVar = 7 /* myVar = 7 */
myTest.myVar /* myVar = 7 still */
var implicitlyUnwrappedJSON:JSON!
implicitlyUnwrappedJSON = JSON(["Test":"This is one"])
implicitlyUnwrappedJSON["Test"] = "This is another one"
implicitlyUnwrappedJSON["Test"].string /* This is one */
implicitlyUnwrappedJSON?["Test"] = "This is another one"
implicitlyUnwrappedJSON["Test"].string /* This is another one */
implicitlyUnwrappedJSON!["Test"] = "This is another one yet"
implicitlyUnwrappedJSON["Test"].string /* This is another one yet */
var optionalJSON:JSON?
optionalJSON = JSON(["Test":"This is one"])
optionalJSON?["Test"] = "This is another one"
optionalJSON?["Test"].string /* This is another one */
optionalJSON!["Test"] = "This is another one yet"
optionalJSON!["Test"].string /* This is another one yet */
var json:JSON = JSON(["Test":"This is one"])
json["Test"] = "This is another one"
json["Test"].string /* This is another one */
So I don't have the slightest clue what is going on here, but I have it working.