Adding item to array in iOS with Swift

28,347

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.

Share:
28,347
Jack Solomon
Author by

Jack Solomon

Founder and CEO of 99Cents App Development. Proficiant iOS and Android Developer. Find out more at 99centsappdevelopment.com

Updated on July 09, 2022

Comments

  • Jack Solomon
    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
      Jack almost 10 years
      From the error it seems like itemsArray is immutable. Is it defined with let or var?
    • Paulw11
      Paulw11 almost 10 years
      It 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
      Jack Solomon almost 10 years
      I have updated my answer with the full code:
  • Tomáš Linhart
    Tomáš Linhart almost 10 years
    This is Objective-C array. It is not wrong but Swing got different arrays. So unless you need them you should use Swift arrays.
  • Literphor
    Literphor almost 10 years
    I'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
    Tomáš Linhart almost 10 years
    By 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
    Literphor almost 10 years
    Ok, 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
    Paulw11 almost 10 years
    NSAarray 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
    Jack Solomon almost 10 years
    I have updated my question with full code. Can you explain where I have made the error?
  • Literphor
    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
    Literphor almost 10 years
    Why 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
    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
    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
    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
    Dash almost 9 years
    NSUserDefaults.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.