3

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(){

    }

}
3
  • 5
    From the error it seems like itemsArray is immutable. Is it defined with let or var? Commented Jun 9, 2014 at 20:29
  • 3
    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? Commented Jun 9, 2014 at 21:13
  • I have updated my answer with the full code: Commented Jun 10, 2014 at 8:43

6 Answers 6

8

This works fine:

var itemsArray = ["one", "two"]
func testSave(item : NSString, date : NSString){

    itemsArray.append(item)
}

testSave("three", "today")
itemsArray
Sign up to request clarification or add additional context in comments.

2 Comments

I have updated my question with full code. Can you explain where I have made the error?
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.
3

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.

Comments

2

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

Comments

1

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.

Comments

1

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.

Comments

-1

Even if you declare something as var it still needs a type to be anything other than AnyObject. Var I believe is just a keyword declaring that that the following identifier (the variable name) is to be inferred by it's assignment

Since you want a Mutable Array try this

var itemsArray: NSMutableArray

or

var itemsArray = NSMutableArray()

Edit: Don't know why I keep getting thumbs down. Now that that the question's been updated it seems that I was right. When you called

NSUserDefaults.standardUserDefaults().arrayForKey("items")

The return type is NSArray. The type inferred for the variable itemsArray is therefore NSArray, which is not mutable. Therefore wrap it in a mutable array

var itemsArray: NSMutableArray = NSMutableArray(array:NSUserDefaults.standardUserDefaults().arrayForKey("items"))

10 Comments

This is Objective-C array. It is not wrong but Swing got different arrays. So unless you need them you should use Swift arrays.
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?
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.
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 - Yes obviously they are classes, my question is how sure are you that that these classes weren't rebuilt using swift arrays?
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.