如果sectionNameKeyPath不是nil我无法启动我的应用

时间:2017-10-19 01:11:23

标签: ios swift uitableview core-data nsfetchedresultscontroller

每当我尝试运行我的应用程序时sectionNameKeyPath不是nil,它就会因错误而崩溃

  

*由于未捕获的异常'NSRangeException'而终止应用程序,原因:'* - [__ NSArray0 objectAtIndex:]:索引0超出空NSArray的边界'

但每当我将其设置为nil时,它就会起作用并添加一些数据,退出应用程序,将 sectionNameKeyPath 设置回我的Core Data属性,现在它可以正常工作!这是今天的第7个小时我正在与这个bug斗争,我无法解决它。我的代码有什么问题?

  //
//  ViewController.swift
//  Expense Manager
//
//  Created by Andrei Vataselu on 10/3/17.
//  Copyright © 2017 Andrei Vataselu. All rights reserved.
//

import UIKit
import SideMenu
import CoreData
import SwipeCellKit

let green = UIColor(red:0.00, green:0.62, blue:0.45, alpha:1.0)
let red = UIColor(red:0.95, green:0.34, blue:0.34, alpha:1.0)
let appDelegate = UIApplication.shared.delegate as? AppDelegate
var userMoney : [UserMoney] = []
var managedObjectContext: NSManagedObjectContext? = appDelegate?.persistentContainer.viewContext


class ViewController: UIViewController, NSFetchedResultsControllerDelegate {


    @IBOutlet weak var sumTextField: UITextField!
    @IBOutlet weak var userBudgetLabel: UILabel!
    @IBOutlet var tap: UITapGestureRecognizer!
    @IBOutlet weak var topView: UIView!
    @IBOutlet weak var plusButton: UIButton!
    @IBOutlet weak var tableView: UITableView!
    @IBOutlet weak var moreBtn: UIButton!

    func userBudgetCount(_ section: Int) -> Int{
        return fetchedResultsController.sections![section].numberOfObjects

    }

    func getUserBudgetAtIndexPath(indexPath : IndexPath) -> Budget {
        return fetchedResultsController.object(at: indexPath) as Budget
    }


    override func viewDidLoad() {
        super.viewDidLoad()
        self.hideKeyboard()

        tableView.delegate = self
        tableView.dataSource = self
        self.tableView.tableFooterView = UIView()


    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        fetchCoreDataObject()
    }

    func fetchCoreDataObject() {
        self.fetch { (complete) in
            if complete {
                if userBudgetCount(0) >= 1 {
                    userBudgetLabel.text = replaceLabel(number: userMoney[userMoney.count - 1].userMoney)
                    tableView.isHidden = false
                    plusButton.isHidden = false
                    moreBtn.isHidden = false
                } else {
                    tableView.isHidden = true
                    userBudgetLabel.text = "Bugetul tau"
                    plusButton.isHidden = true
                    moreBtn.isHidden = true
                }
            }
        }
    }


    var fetchedResultsController: NSFetchedResultsController<Budget> {
        if _fetchedResultsController != nil {
            return _fetchedResultsController!
        }

        let fetchRequest = NSFetchRequest<Budget>(entityName: "Budget")

        // Set the batch size to a suitable number.
        fetchRequest.fetchBatchSize = 20

        // Edit the sort key as appropriate.
        let sortDescriptor = NSSortDescriptor(key: "dateSubmitted" , ascending: false)

        fetchRequest.sortDescriptors = [sortDescriptor]

        // Edit the section name key path and cache name if appropriate.
        // nil for section name key path means "no sections".


        let aFetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: managedObjectContext!, sectionNameKeyPath: "dateSection", cacheName: nil)

        aFetchedResultsController.delegate = self
        _fetchedResultsController = aFetchedResultsController

        do {
            try _fetchedResultsController!.performFetch()

        } catch {
            // Replace this implementation with code to handle the error appropriately.
            // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
            let nserror = error as NSError
            fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
        }


        return _fetchedResultsController!
    }
    var _fetchedResultsController: NSFetchedResultsController<Budget>? = nil

    func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {

        tableView.reloadData()


    }



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

    @IBAction func initialAddButtonPressed(_ sender: Any) {
        if sumTextField.text != "" {
            self.saveMoney(userMoney: (sumTextField.text! as NSString).doubleValue, completion: { (complete) in

            })
            self.save(sumText: sumTextField.text! , dataDescription: "Buget initial", dataColor: green) {
                complete in
                if complete {
                    tableView.isHidden = false
                }
            }
            userBudgetLabel.text = "\(sumTextField.text!) RON"
            self.fetchCoreDataObject()
            tableView.reloadData()
        } else {
            sumInvalidAlert()
        }
        self.dismissKeyboard()
        sumTextField.text = ""
    }
    @IBAction func plusButtonPressed(_ sender: Any) {

        let plusController = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)

        let addBudgetAction = UIAlertAction(title: "Adauga buget", style: .default) {
            (action) -> Void in

            guard let createAddBudgetVC = self.storyboard?.instantiateViewController(withIdentifier: "AddBudgetVC") else { return }
            self.presentViewController(createAddBudgetVC)
        }

        let addExpenseAction = UIAlertAction(title: "Adauga plata", style: .default) {
            (action) -> Void in

            guard let createAddExpenseVC = self.storyboard?.instantiateViewController(withIdentifier: "AddExpenseVC") else { return }
            self.presentViewController(createAddExpenseVC)

        }

        let cancelAction = UIAlertAction(title: "Anuleaza", style: .cancel, handler: nil)

        plusController.addAction(addBudgetAction)
        plusController.addAction(addExpenseAction)
        plusController.addAction(cancelAction)

        present(plusController, animated: true, completion: nil)

    }

    @IBAction func moreButtonPressed(_ sender: Any) {

    }


}

extension ViewController {
    func fetch(completion: (_ complete: Bool) -> ()){

        guard let managedContext = appDelegate?.persistentContainer.viewContext else { return }

        let fetchMoneyRequest = NSFetchRequest<UserMoney>(entityName: "UserMoney")

        do{
            userMoney = try managedContext.fetch(fetchMoneyRequest)
            print("success")
            completion(true)
        } catch {
            debugPrint("Could not fetch \(error.localizedDescription)")
            completion(false)
        }
}


    func removeCell(atIndexPath indexPath: IndexPath){
        guard let managedContext = appDelegate?.persistentContainer.viewContext else { return }


        do {
            try managedContext.save()
            debugPrint("removeCell CONTEXT SAVED")
        } catch {
            debugPrint("removeCell CONTEXT NOT SAVED \(error.localizedDescription)")
        }
    }

    func cancelCell(color: UIColor, atIndexPath indexPath: IndexPath){
        guard let managedContext = appDelegate?.persistentContainer.viewContext else { return }

        if color.description == green.description {
            // scade buget

            userMoney[userMoney.count - 1].userMoney -= (getUserBudgetAtIndexPath(indexPath: indexPath).dataSum! as NSString).doubleValue

        } else {


            userMoney[userMoney.count - 1].userMoney += (getUserBudgetAtIndexPath(indexPath: indexPath).dataSum! as NSString).doubleValue
        }

        do {
            try managedContext.save()
        } catch {
            print("cancelCell Managed Context Saving ERROR: \(error.localizedDescription)")
        }
    }

}

extension ViewController: UITableViewDelegate, UITableViewDataSource,SwipeTableViewCellDelegate {

    func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath, for orientation: SwipeActionsOrientation) -> [SwipeAction]? {
      guard orientation == .right else {
        let cancelAction = SwipeAction(style: .default, title: "Anuleaza"){
            (action, indexPath)
            in

            self.cancelCell(color: self.getUserBudgetAtIndexPath(indexPath: indexPath).dataColor as! UIColor, atIndexPath: indexPath)
            self.removeCell(atIndexPath: indexPath)
            self.fetchCoreDataObject()
            tableView.deleteRows(at: [indexPath], with: .automatic)
        }
            cancelAction.backgroundColor = UIColor(red:0.16, green:0.63, blue:0.74, alpha:1.0)
            return [cancelAction]
        }

        let deleteAction = SwipeAction(style: .destructive, title: "Sterge") { (action, indexPath) in
            self.removeCell(atIndexPath: indexPath)
            self.fetchCoreDataObject()
            tableView.deleteRows(at: [indexPath], with: .automatic)
        }
        deleteAction.backgroundColor = red

        return [deleteAction]
    }

    func numberOfSections(in tableView: UITableView) -> Int {
        if let sections = fetchedResultsController.sections {
        return sections.count 
        }
        return 0
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        guard let cell = tableView.dequeueReusableCell(withIdentifier: "expenseCell") as? ExpenseCell else { return UITableViewCell() }
        print("indexPathRow: \(indexPath.row) | indexPathSection: \(indexPath.section)")
        let budget = fetchedResultsController.object(at: indexPath) as Budget
        cell.delegate = self
        cell.configureCell(budget: budget)

        return cell
    }

    func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
        return true
    }

    func tableView(_ tableView: UITableView, editingStyleForRowAt indexPath: IndexPath) -> UITableViewCellEditingStyle {
        return UITableViewCellEditingStyle.none
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        if let sections = fetchedResultsController.sections {
            let currentSection = sections[section]
            return currentSection.numberOfObjects
        }
        return 0
    }

    func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
        if let sections = fetchedResultsController.sections {
            let currentSections = sections[section]
            return currentSections.name
        }
        return nil
    }


}

0 个答案:

没有答案