3
votes

I thought I'd ask here first before I sent my laptop flying out of the window. I am very new to iOS development and have been toying around with Core Data trying to create an expense manager app. I have a single entity named "Account" which has two non-optional attributes (name and initBalance) as follows:

enter image description here

I have also created an NSManagedObject subclass as follows:

//Account.swift

class Account: NSManagedObject {

    override func awakeFromInsert() {
        super.awakeFromInsert()
        initBalance = NSNumber(double: 0.0)
        name = ""
    }    
}

//Account+CoreDataProperties.swift

extension Account {

    @NSManaged var initBalance: NSNumber
    @NSManaged var name: String
}

The Account entities are presented in the AccountsViewController (a subclass of UITableViewController conforming to the NSFetchedResultsControllerDelegate protocol) using an NSFetchedResultsController. To add a new Account I use a segue from AccountsViewController to direct me to the AccountInfoViewController (shown below) which has two textFields, one for the name and one for the balance of the account. When the latter is about to disappear a new Account is inserted in the context with the name and balance derived from the textFields. Once the parent controller appears (AccountsViewController) it tries to save the changes and updates the tableView.

Now, if I insert a new Account for which the balance is an integer number life is good; the context is able to save the changes, tableView updates its rows by showing the newly inserted account, and I am happy. When I try to insert an Account for which the balance is a decimal number, the app crashes at the point where the context tries to save the changes. I get no useful error at all as to why it happens. Here is the code for the controller managing the textFields:

class AccountInfoViewController: UIViewController {

    @IBOutlet weak var nameField: UITextField!
    @IBOutlet weak var balanceField: UITextField!

    var store: DataStore! // set by the parent viewController everytime this controller appears

    let numberFormatter = MyFormatter()


    // insert the new account into the context before the controller disappears
    override func viewWillDisappear(animated: Bool) {
        super.viewWillDisappear(animated)

        store.insertAccount(nameField.text!, balance: numberFormatter.numberFromString(balanceField.text!)!)
    }
}

The MyFormatter is this:

func MyFormatter() -> NSNumberFormatter {
    let formatter = NSNumberFormatter()
    formatter.numberStyle = .DecimalStyle
    formatter.minimumFractionDigits = 2
    formatter.maximumFractionDigits = 2
    return formatter    
}

and the DataStore shown above contains a simple Core Data Stack and is responsible for inserting into the context:

// DataStore.swift

class DataStore {

    let coreDataStack = CoreDataStack(modelName: "SmartMoney")

    // inserts a new account into the context
    func insertAccount(name: String, balance: NSNumber) -> Account {
        let account = NSEntityDescription.insertNewObjectForEntityForName("Account",
            inManagedObjectContext: coreDataStack.mainQueueContext) as! Account

        account.name = name
        account.initBalance = balance
        return account
    }
}

Is there any reason why the context fails to save the changes when the balanceField contains a non-integer number? Thank you for reading and please let me know if you would like me to post any other parts of the code.

1
What is MyFormatter? Have you verified that it's not returning junk? - Tom Harrington
@TomHarrington: I added the MyFormatter code above. Yes, I have verified it but it is not included in the code shown above. - linuxfever
Have you printed back out account.initBalance before you return it? - sschale
@sschale: Just tried, it prints out the correct decimal value. Once it tries to save the context though, it crashes.... - linuxfever
Oh, since you're doing money, you should really use Decimal numbers in your data model and use the methods associated with them to store amounts. This might solve your problem as well just implementing them. - sschale

1 Answers

5
votes

I was finally able to figure out what's going on. Changing my attribute name from initBalance to startingBalance makes everything work again. In fact, changing the attribute's name to anything that does not start with new or init works just fine. I got the idea from this post.

It seems that when using ARC, your property's name should not start with the word new. It turns out that initBalance (or newBalance for that matter) produces the same issue. Hope it helps the next poor soul running in a similar problem.