Adding item to array in iOS with Swift
Solution 1
This works fine:
var itemsArray = ["one", "two"]
func testSave(item : NSString, date : NSString){
itemsArray.append(item)
}
testSave("three", "today")
itemsArray
Solution 2
Problem is here:
var itemsArray = NSUserDefaults.standardUserDefaults().arrayForKey("items")
See the Apple's document:
func arrayForKey(_ defaultName: String!) -> AnyObject[]!
- The returned array and its contents are immutable, even if the values you originally set were mutable.
But if you are 100% sure that "items" is not an empty NSArray
, then you can downcast it to Array then .append()
will work:
var itemsArray = NSUserDefaults.standardUserDefaults().arrayForKey("items") as Array
If the return object from .arrayForKey()
is nil
or can't be cast to Array (e.g. it's objects can't be casted into Swift type), error will rise as it can't unwrap the optional.
Solution 3
Put this code in the sandbox and you will see that the value "hello" is appended properly. Figuring out what is different between my code and yours will be a great learning experience for you:
class ToDoListTableViewController: UITableViewController /*...*/ {
var itemsArray: Array<AnyObject>!
override func viewDidLoad() {
super.viewDidLoad()
if let savedItems = NSUserDefaults.standardUserDefaults().arrayForKey("items") {
itemsArray = savedItems
}
else {
itemsArray = []
}
}
func testSave(item : NSString, date : NSString) {
itemsArray.append(item)
}
}
let list = ToDoListTableViewController()
list.viewDidLoad()
list.testSave("hello", date: "")
list.itemsArray
Solution 4
If you are doing Mix and Match (gradually migrating from objc to swift)
Then use
nsMutableArray.addObject(nsMutableDictionary)
The replacement of append
you are looking for is addObject
Hope this helps someone in future.
Solution 5
It is not that your code is syntactically wrong, it is just that the method "arrayForKey" always gives back an immutable array which you can not modify.
You are trying to append, which modifies the length, hence it is not allowed.
You can verify this in the documentation here is an extract for the return value:
arrayForKey: Returns the array associated with the specified key.
...
Return Value The array associated with the specified key, or nil if the key does not exist or its value is not an NSArray object.
Special Considerations The returned array and its contents are immutable, even if the values you originally set were mutable.
Jack Solomon
Founder and CEO of 99Cents App Development. Proficiant iOS and Android Developer. Find out more at 99centsappdevelopment.com
Updated on July 09, 2022Comments
-
Jack Solomon almost 2 years
I am trying to add an item to my array (which was declared as a var), using anything that might work (+=, append, insert), however I keep getting the error 'Immutable value of type 'AnyObject[]' only has mutating members named 'append''.
Here is where the error occurs:
func testSave(item : NSString, date : NSString){ itemsArray.append(item)
UPDATE: HERE IS THE FULL CODE:
import UIKit class ToDoListTableViewController: UITableViewController, UITableViewDelegate, UITableViewDataSource, UIAlertViewDelegate { var itemsArray = NSUserDefaults .standardUserDefaults().arrayForKey("items") var dateArray = NSUserDefaults .standardUserDefaults().arrayForKey("dates") override func viewDidLoad() { super.viewDidLoad() NSUserDefaults .standardUserDefaults().setObject("test", forKey: "items") NSUserDefaults .standardUserDefaults().setObject("test", forKey: "dates") self.itemsArray = NSUserDefaults .standardUserDefaults().arrayForKey("items") self.dateArray = NSUserDefaults .standardUserDefaults().arrayForKey("dates") // Uncomment the following line to preserve selection between presentations // self.clearsSelectionOnViewWillAppear = false // Uncomment the following line to display an Edit button in the navigation bar for this view controller. // self.navigationItem.rightBarButtonItem = self.editButtonItem } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } // #pragma mark - Table view data source override func numberOfSectionsInTableView(tableView: UITableView?) -> Int { // #warning Potentially incomplete method implementation. // Return the number of sections. return 1 } override func tableView(tableView: UITableView?, numberOfRowsInSection section: Int) -> Int { // Return the number of rows in the section. if itemsArray{ return itemsArray.count} else{ return 0} } override func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell!{ //variable type is inferred /*var cell = tableView.dequeueReusableCellWithIdentifier("Cell") as? UITableViewCell if !cell { cell = UITableViewCell(style: UITableViewCellStyle.Subtitle, reuseIdentifier: "Cell") } //we know that cell is not empty now so we use ! to force unwrapping cell!.textLabel.text = self.itemsArray[indexPath.row] as String cell!.detailTextLabel.text = self.dateArray[indexPath.row] as String */ let cell = UITableViewCell(style: UITableViewCellStyle.Subtitle, reuseIdentifier:"Cell") if itemsArray{ println("Working") cell.textLabel.text = itemsArray[indexPath.row] as String cell.detailTextLabel.text = dateArray[indexPath.row] as String } return cell } @IBAction func addItem(sender : UIBarButtonItem) { var alert = UIAlertController(title: "Alert", message: "Message", preferredStyle: UIAlertControllerStyle.Alert) alert.addAction(UIAlertAction(title: "Ok", style: UIAlertActionStyle.Default, handler: {(action: UIAlertAction!) in var stringText = alert.textFields[0].text var dateText = alert.textFields[0].text self .testSave(stringText, date: dateText) })) alert.addTextFieldWithConfigurationHandler(nil) self.presentViewController(alert, animated: true, completion: nil) } func testSave(item : NSString, date : NSString){ itemsArray.append(item) /* NSUserDefaults .standardUserDefaults().setObject(item, forKey: "items") /* NSUserDefaults .standardUserDefaults().setObject(stringText, forKey: "items") NSUserDefaults .standardUserDefaults().setObject(dateText, forKey: "dates") NSUserDefaults.standardUserDefaults().synchronize() */ self.dateArray = NSUserDefaults .standardUserDefaults().arrayForKey("dates") self.tableView .reloadData() */ } func alertviewClick(){ } }
-
Jack almost 10 yearsFrom the error it seems like
itemsArray
is immutable. Is it defined withlet
orvar
? -
Paulw11 almost 10 yearsIt seems that the cause of your problem lies outside code shown. Can you show more code, in particular how itemsArray comes into scope - is it an iVar? received from another function? Declared locally?
-
Jack Solomon almost 10 yearsI have updated my answer with the full code:
-
-
Tomáš Linhart almost 10 yearsThis is Objective-C array. It is not wrong but Swing got different arrays. So unless you need them you should use Swift arrays.
-
Literphor almost 10 yearsI'm not sure what you mean by Objective-C array. NSMutableArray is part of the iOS framework, so I would imagine the swift adaptation of the framework would use swift arrays?
-
Tomáš Linhart almost 10 yearsBy Objective-C array I mean NSArray or NSMutableArray that are part of Foundation framework. Swift arrays has type Array<T> and they are not related to arrays found in Foundation framework.
-
Literphor almost 10 yearsOk, I understand what you're saying. Just for clarification, are you sure that the foundation framework hasn't been rebuilt with swift and using swift arrays?
-
Paulw11 almost 10 yearsNSAarray and NSMutableArray are classes. swift arrays are an intrinsic type within Swift. It is similar to the difference between a Vector or ArrayList in Java and a Java array
-
Jack Solomon almost 10 yearsI have updated my question with full code. Can you explain where I have made the error?
-
Literphor almost 10 years@Paulw11 - Yes obviously they are classes, my question is how sure are you that that these classes weren't rebuilt using swift arrays?
-
Literphor almost 10 yearsWhy am I getting thumbs down? After the original poster updated his question it seems that I have the correct solution. Some explanation would be nice.
-
Jack Solomon almost 10 years@Literphor I have done what you suggested for NSMutableArray, and that hasn't caused errors, however I am still getting that same error when I try to add an item to the array. Not sure why you are getting thumbs down, the answer was helpful, just hasn't got rid of the errors. Thanks for your help though! :)
-
Jack Solomon almost 10 years@Literphor, in addition to my previous comment, here is the new error message: 'NSMutableArray' does not have a member named 'append'...
-
Literphor almost 10 years@JackSolomon Well yes, NSMutableArray doesn't have a member named append. To add an object to it you use the method addObject: or insterObject:atIndex:
-
Dash almost 9 yearsNSUserDefaults.standardUserDefaults().arrayForKey("items") is a non-mutable array. You need to instantiate itemsArray as a mutable array in order to be allowed to append items to it.