0

I have been trying to set up, what seems like it should be, a simple app that allows a user to update a food item with a price and store where the price was found. The main issue I know is trying to blend Swift with Objective-C even though Apple hasn't worked out the kinks yet for Swift and it's ever changing.

Anyways I have set up my AllowedTableViewController as follows

class AllowedTableViewController: UITableViewController {

    var myAllowedList : Array<AnyObject> = []

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    override func viewDidAppear(animated: Bool) {

        let appDel: AppDelegate = UIApplication.sharedApplication().delegate as AppDelegate
        let context: NSManagedObjectContext = appDel.managedObjectContext!
        let freq = NSFetchRequest(entityName: "FoodAllowed")

        myAllowedList = context.executeFetchRequest(freq, error: nil)!
        tableView.reloadData()
    }

    override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {

        if segue.identifier == "update" {

            var indexPath: NSIndexPath = self.tableView.indexPathForSelectedRow()!
            var selectedItem: NSManagedObject = myAllowedList[indexPath.row] as NSManagedObject
            let IVC: AllowedViewController = segue.destinationViewController as AllowedViewController

            IVC.name = selectedItem.valueForKey("name")as String
            IVC.store = selectedItem.valueForKey("store")as String
            IVC.price = selectedItem.valueForKey("price")as String
            IVC.existingItem = selectedItem
            }
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    // 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 {
        // #warning Incomplete method implementation.
        // Return the number of rows in the section.
        return myAllowedList.count
    }

    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        //Configure the Cell

        let CellID: NSString = "Allowed"

        var cell: UITableViewCell = tableView.dequeueReusableCellWithIdentifier(CellID) as UITableViewCell


        var data: NSManagedObject = myAllowedList[indexPath.row] as NSManagedObject
            cell.textLabel.text = (data.valueForKeyPath("name")as String)

        var pri = data.valueForKeyPath("price")as String
        var str = data.valueForKeyPath("store")as String
        cell.detailTextLabel?.text = "\(pri) name/s - \(str)"
        return cell
    }

    //Override to support conditional editing of the table view
    override func tableView(tableView: UITableView?, canEditRowAtIndexPath indexPath: NSIndexPath?) -> Bool {
        //Return NO if you do not want the specified item to be editable
        return true
    }

    //Override to support editing the table view
    override func tableView(tableView: UITableView?, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath?) {

        let appDel: AppDelegate = UIApplication.sharedApplication().delegate as AppDelegate
        let context: NSManagedObjectContext = appDel.managedObjectContext!

        if editingStyle == UITableViewCellEditingStyle.Delete {

            if let tv = tableView {
            context.deleteObject(myAllowedList[indexPath!.row] as NSManagedObject)
            myAllowedList.removeAtIndex(indexPath!.row)
            tv.deleteRowsAtIndexPaths([indexPath!.row], withRowAnimation: UITableViewRowAnimation.Fade)
            }

            var error: NSError? = nil
            if !context.save(&error) {
                abort()
            }
        }       
    }
}

My user editable text field code in the view controller are as follows.

{

class AllowedViewController: UIViewController {

    @IBOutlet var textFieldname: UITextField!
    @IBOutlet var textFieldstore: UITextField!
    @IBOutlet var textFieldprice: UITextField!


    var name: String = ""
    var store: String = ""
    var price: String = ""

    var existingItem: NSManagedObject!

    override func viewDidLoad() {
        super.viewDidLoad()

        if existingItem == nil {
            textFieldname.text = name
            textFieldstore.text = store
            textFieldprice.text = price

        }
     // Do any additional setup after loading the view.
    }

    func saveTapped(sender: AnyObject) {

        //Reference to our app delegate

        let appDel: AppDelegate = UIApplication.sharedApplication().delegate as AppDelegate

        //Reference NS Managed Object Context

        let contxt: NSManagedObjectContext = appDel.managedObjectContext!
        let en = NSEntityDescription.entityForName("FoodAllowed", inManagedObjectContext: contxt)!
         //Check if item exists

        if (existingItem != nil) {
            existingItem.setValue(textFieldname.text as String, forKey: "name")
            existingItem.setValue(textFieldstore.text as String, forKey: "store")
            existingItem.setValue(textFieldprice.text as String, forKey: "price")

        }else {

        //Create instance of pur data model and intialize

        var newItem = DataModel(entity: en, insertIntoManagedObjectContext: contxt)

        //Map our properties

        newItem.name = [textFieldname.text]
        newItem.store = [textFieldstore.text]
        newItem.price = [textFieldprice.text]


        //Save our content

        contxt.save(nil)
        println(newItem)

        //Navigate back to root view controll

        self.navigationController?.popToRootViewControllerAnimated(true)
        }
    }

     func cancelTapped(sender: AnyObject) {

        //Navigate back to root view controll

        self.navigationController?.popToRootViewControllerAnimated(true)
       }

      override func didReceiveMemoryWarning() {

        super.didReceiveMemoryWarning()

        // Dispose of any resources that can be recreated.
        }
   }

}

My NSManaged DataModel is

{

@objc(DataModel)
class DataModel: NSManagedObject {

    //properties feeding the attributes in our entity
    //must match the entity attributes

    @NSManaged var name: [String]
    @NSManaged var store: [String]
    @NSManaged var price: [String]

}

}

When I run the app in the simulator I get the following error

'NSInvalidArgumentException', reason: 'Unacceptable type of value for attribute: property = "name"; desired type = NSString; given type = Swift._NSSwiftArrayImpl; value = (Apples).'

What am I doing wrong?

Dharmesh Kheni
  • 71,228
  • 33
  • 160
  • 165
jmuir31
  • 17
  • 1
  • 5

1 Answers1

1

The error message indicates that "name", "store" and "price" are String properties of your Core Data entity, but you have defined them as [String], i.e. an array of strings. It should be

@objc(DataModel)
class DataModel: NSManagedObject {

    @NSManaged var name: String
    @NSManaged var store: String
    @NSManaged var price: String

}

And consequently

newItem.name = textFieldname.text // not: [textFieldname.text]
// ...

Better yet, let Xcode generate the managed object subclasses (Editor -> Create NSManagedObject Subclass ... in the Xcode menu).

Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
  • Does Core Data with Swift ensure that non-optionals are always appropriate? Certainly a Core Data attribute declared as 'optional' should have a Swift optional type aka '?'. What about '!' being required for any other type? (As it is for IBOutlets because of the awakeFromNIB process) – GoZoner Oct 28 '14 at 22:21
  • @GoZoner: Unfortunately, Xcode does (still) does not generate proper code for optional properties, you have to add ! or ? manually. Compare http://stackoverflow.com/questions/25661120/check-if-property-is-set-in-core-data. – Martin R Oct 29 '14 at 06:17
  • Thank you so much that worked! Must have missed that when reviewing! – jmuir31 Oct 29 '14 at 21:39