Parse: Updating array - ios

I'm trying to upload an array with the following code, but I am getting an error on the second to last line which says Cannot assign to value: function call returns immutable array
for object in objects! {
let newstring = NSString(format: ".0f", self.slider.value)
var newarray = [object.objectForKey("times")]
newarray.append(newstring)
object.objectForKey("times") = newarray
object.saveInBackground()
}
"times" is of type array in parse by the way.

Rather than requesting the array and trying to set it you should be using setObject:forKey: to replace the existing value.
object.setObject(newarray, forKey: "times")

May I suggest using the addObject:forKey: method instead?
for object in objects! {
let newstring = NSString(format: ".0f", self.slider.value)
object.addObject(newstring, forKey: "times")
object.saveInBackground()
}
It is much cleaner and doesn't require retrieval :)

Related

iOS Swift Updating Dictionary In An Array

I have an array of dictionaries inside a dictionary. I initialize it like this:
var fillups:[NSMutableDictionary] = []
Then I load it like this:
fillups = userDefaults.object(forKey: car) as! NSArray as! [NSMutableDictionary]
Then when I try to update a dictionary element in the array I get the "mutating method sent to immutable object" error. Here's my code to update the record:
let dict=fillups[row]
dict.setValue(odometerField.text, forKey: "odometer")
dict.setValue(gallonsField.text, forKey: "gallons")
fillups[row]=dict
The error occurs in my first setValue line.
Objects that you retrieve from NSUserDefaults are immutable even if they were mutable when they were inserted. You need to take the immutable objects you get from defaults and create mutable versions of them. You also shouldn't force unwrap everywhere if you don't want your app to crash.
if let array = userDefaults.object(forKey: car) as? [NSDictionary] {
fillups = array.map { ($0.mutableCopy() as! NSMutableDictionary) }
}
You also don't need the fillips[row] = dict line since NSMutableDictionary is a reference type and editing the reference you pull out of the array is already editing the one inside the array.
If you want to mutate your dict, you need to declare it with 'var' not with 'let'; 'let' is for constants. Also fix the unwrapping problems pointed out by the comment
let dict=fillups[row]
should be
var dict=fillups[row]

Get json results in NSuserDefault and display result for annotation in MapKit in Swift

I want to get result from json and save this result in NSUserDefault, after I want to use the json array saved in NSUserDefault to add multiple annotation on the MapKit.
Currently to get json result, I use this : ( Swift 2.x )
let defaults = NSUserDefaults.standardUserDefaults()
//Get user content//
let url = NSURL(string: "http://www.example.com/folder/coordonate.php")
let request = NSMutableURLRequest(URL: url!)
// modify the request as necessary, if necessary
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) {
data, response, error in
if data == nil {
print("request failed \(error)")
return
}
do {
if let json = try NSJSONSerialization.JSONObjectWithData(data!, options: []) as? NSDictionary {
print(json)
// print : {tab = "[london,paris]";}
var test = json["tab"]
defaults.setObject(test, forKey: "annotation") as? NSArray
}
} catch {
print(error)
}
}
task.resume()
NSUserDefaults.standardUserDefaults().synchronize()
And to get the NSUserdefault in another view, I try this code :
let defaults = NSUserDefaults.standardUserDefaults()
let test = NSUserDefaults.standardUserDefaults().objectForKey("annotation") as! NSArray
map.addAnnotations(test as! [MKAnnotation])
print("We saved this data: \(test)")
//No print, error before
But I have an error when I try with this method.
Could not cast value of type '__NSCFString' (0x1971a3958) to 'NSArray' (0x1971a4308).
If the "tab" property JSON actually being returned is:
[london, paris]
Then the property is not an array. What you're looking for would be:
['london', 'paris']
But additionally I can tell you that even if the "tab" property is a properly formatted JSON array your code will fail when it attempts to convert it to [MKAnnotation] anyway. That's because iOS's JSON library does not know how to convert a generic NSArray into it's typed equivalent. The NSJSONSerialization documentation indicates all the types that JSON will convert to. Best case scenario the "tab" property is an array of items with the same structure as MKAnnotation and are being converted to an array of dictionaries that you will have to convert to MKAnnotation yourself. But the JSON provided currently evaluates as a string. With my suggested change it will instead evaluate to an array of strings- still not sufficient to create an MKAnnotation from.
Your JSON data has to be one of the valid NSUserDefault types (String, NSArray, NSDictionary, NSData).
The quickest fix would be to store the JSON in NSUserDefaults as the NSData that comes back from the server. Then deserialize the NSData on the reading of NSUserDefaults.
If storing a subset of the JSON from the server is really needed, I would use Dictionaries, Arrays and validate the data before storing it. As a general Swift rule, I avoid using NSDictionary and NSArray to ensure the types are what I expect and won't cause a runtime crash.
change your php code to
$results = Array("tab" => ["london","paris"]);
instead of
$results = Array("tab" => "[london,paris]");
p.s
if using php earlier than 5.5 (or 5.4 not quite remember) then use:
$results = Array("tab" => Array("london","paris"));
==========================
you are casting the setObject func to NSArray and not the test object
defaults.setObject(test, forKey: "annotation") as? NSArray
should be
if let arrayTest = test as? NSArray{
defaults.setObject(arrayTest, forKey: "annotation")
}

iOS: how to convert NSUserDefaults stored AnyObject type to array

I have put an array into NSUserDefaults() like so:
NSUserDefaults.standardUserDefaults().setObject(users, forKey: "usersArray")
NSUserDefaults.standardUserDefaults().synchronize()
Then I pull it out like so:
fetchedUserArray = NSUserDefaults.standardUserDefaults().objectForKey("usersArray")
The problem I am facing is that once it is removed from NSUserDefaults it is of type NSArray, preventing me from manipulating it like a Swift array. I have tried this to convert the type, however the compiler does not recognize the variable "castedUsersArray" when it is used later in the code despite not raising any errors upon type casting:
var fetchedArray = NSUserDefaults.standardUserDefaults().objectForKey("usersArray") as? NSArray
var castedUsersArray = fetchedArray as AnyObject as [String]
I have spent a very long time on this with no success. The type constraints of Swift are driving me nuts.
Thank you,
Nick
You almost had it. Don't cast the objectForKey to an Array but rather an Array containing a certain type like you did with castedUsersArray. Don't throw away type information like you did with fetchedArray.
let users = ["Amy", "Bill", "Cindy"]
NSUserDefaults.standardUserDefaults().setObject(users, forKey: "usersArray")
NSUserDefaults.standardUserDefaults().synchronize()
let fetched = NSUserDefaults.standardUserDefaults().objectForKey("usersArray") as? [String] ?? []
The nil coalescing at the end of the line handles the empty NSUserDefaults case.
NSUserDefaults has a specific method to get your stored string arrays called stringArrayForKey:
let stringArray = ["Hello","playground"]
store your string array
NSUserDefaults.standardUserDefaults().setObject(stringArray, forKey: "stringArray")
load it when needed
if let loadedStringArray = NSUserDefaults.standardUserDefaults().stringArrayForKey("stringArray") {
print(loadedStringArray) // ["Hello", "playground"]
}

how to save and read dictionary of touples to NSUserDefaults?

I have an dictionary = String: ([(String)], [(Int)], NSDate, Bool, [(String)]) and I attempted to deconstruct it into seperate arrays when then app calls applicationWillTerminate
var codes = [(String)]()
var messages = [[String]]()
var senders = [[Int]]()
var dates = [(NSDate)]()
var bools = [(Bool)]()
var pairs = [[String]]()
for code in self.dictionary.keys {
codes.append(code)
messages.append(self.dictionary[code]!.0)
senders.append(self.dictionary[code]!.1)
dates.append(self.dictionary[code]!.2)
bools.append(self.dictionary[code]!.3)
pairs.append(self.dictionary[code]!.4)
}
self.userDefaultsMessages.setObject(codes, forKey: "userMessagesArrays")
self.userDefaultsSenders.setObject(messages, forKey: "userSentArrays")
self.userDefaultsDates.setObject(senders, forKey: "userDatesArray")
self.userDefaultsDeletedBool.setObject(dates, forKey: "userDeletedArrays")
self.userDefaultsPairs.setObject(bools, forKey: "userPairsArrays")
self.userDefaultsCodeKeys.setObject(pairs, forKey: "userCodesArrays")
self.userDefaultsCodeKeys.synchronize()
self.userDefaultsMessages.synchronize()
self.userDefaultsSenders.synchronize()
self.userDefaultsDates.synchronize()
self.userDefaultsDeletedBool.synchronize()
self.userDefaultsPairs.synchronize()
and then I attempt to pull it all back together when the app calls applicationDidBecomeActive
//read
if let savedCodesArray : AnyObject? = self.userDefaultsCodeKeys.objectForKey("userCodesArrays") {
self.userCodes = savedCodesArray! as! [String]
if let savedMessagesArray : AnyObject? = self.userDefaultsCodeKeys.objectForKey("userMessagesArrays") {
self.Usermessages = savedMessagesArray! as! [[String]]
if let savedSendersArray : AnyObject? = self.userDefaultsCodeKeys.objectForKey("userSentArrays") {
self.Usersenders = savedSendersArray! as! [[Int]]
if let savedDatesArray : AnyObject? = self.userDefaultsCodeKeys.objectForKey("userDatesArray") {
self.Userdates = savedDatesArray! as! [NSDate]
if let savedBools : AnyObject? = self.userDefaultsCodeKeys.objectForKey("userDeletedArrays") {
self.Userbools = savedBools! as! [Bool]
if let savedPairs : AnyObject? = self.userDefaultsCodeKeys.objectForKey("userPairsArrays") {
self.Userpairs = savedPairs! as! [[String]]
var indexPath: Int = 0
for code in self.userCodes {
self.dictionary[code]! = [self.Usermessages[indexPath], self.Usermessages[indexPath], self.Userdates[indexPath], self.Userbools[indexPath], self.Userpairs[indexPath]]
}
}
}
}
}
}
}
I am fairly new to iOS development and could use help, how would i save the single dictionary = String: ([(String)], [(Int)], NSDate, Bool, [(String)]) to NSUserDefaults and then later read it.. the documentation was not very helpful since it only worked with simple dictionaries
the code looks incredibly cumbersome so I know I can't be doing it right. It should be a simple solution since I only have one variable to save to NSUserDefaults.
Short answer: You can't.
NSUserDefaults will only record "property list objects" (dictionaries, arrays, strings, numbers (integer and float), dates, binary data, and Boolean values).
You can't save any other types of data into NSUserDefaults, or into a property list. The only solution is to convert other data types into those types.
Tuples are not one of those types, so they can't be saved into user defaults.
First, your interaction with NSUserDefaults is completely wrong. You only need to interact with the shared NSUserDefaults.sharedUserDefaults() instance, not create separate instances, which seems to be what you're doing (i.e. userDefaultsMessages, userDefaultsSenders, etc.).
Second, you don't need to call synchronize() at all. There are very few conditions under which you need to call it manually, and this isn't one of them.
Third, the easiest way to store a particular tuple in NSUserDefaults is to convert it into an Array (or NSArray) and store the result. Of course, this assumes that the tuple contains only types that can be serialized, which your example seems to contain. Unfortunately there's no general solution to this, but creating an array from a tuple is straight forward, as you can just map the tuple indices to array indices.
Finally, such large tuples are usually the result of poor design somewhere along the line. Perhaps refactoring would help resolve your storage issue?

Cant set Array in NSUserDefaults

var arr = defaults.arrayForKey("kWeeks") as [WeekReport]?
if arr? {
var arr2 = arr!
arr2.append(report)
defaults.setObject(arr2, forKey: "kWeeks") // Crash
defaults.synchronize()
}
I try to set arr2 which will be the add after optional checking. But it crashes on setObject() with error: EXC_BAD_ACCESS
Ive checked, with println() and arr2 is not nil and contains the element which is appended in the sample above. And report is of type WeekReport.
var arr = defaults.arrayForKey("kWeeks") as [WeekReport]?
Is doing a forced casting to an optional array of WeekReport. Instead, you really want to do an optional casting:
if let arr = defaults.arrayForKey("kWeeks") as? [WeekReport] {
arr.append(report)
defaults.setObject(arr, forKey: "kWeeks")
defaults.synchronize()
}
This says, "if there is an array for "kWeeks" that can be cast to an array of WeekReport, do the following with it as arr"
You also need to make sure that WeekReport inherits from the NSCoding protocol so that it can be converted to data.

Resources