Неправильное использование FetchedResultsController Swift 3 API: Попытка сериализовать доступ к хранилищу на не принадлежащем координатору

Я пытаюсь использовать fetchedResultsController для обработки результатов в моем UITable.

Первоначально он работает при запуске программы. Затем, когда я переключаюсь обратно на вкладку инвентаря, где находится моя таблица (опять же для viewToAppear), это происходит, когда она падает.

Я получаю ошибку сбоя во время выполнения в моем методе viewWillAppear () окна, в котором есть таблица.

В частности, происходит сбой в файле Inventory + CoredataProperties.swift в этой строке.let characters = name!.characters.map { String($0) }, но я подозреваю, что ошибка в другом месте, так как это работает изначально, так почему бы не сейчас на 2-й перезагрузки?

Вот функция.

override func viewWillAppear(_ animated: Bool) {
        print("view appearing")
        //When the view appears its important that the table is updated.

        //Trigger Event on SearchBar in case returning from BarCode Scanner
//        self.searchBar:SearchBar textDidChange:recentlySearchedWord;
        //searchBar.performSelector(":textDidChange")

        //Perform another fetch again to get correct data~
        do {
            //fetchedResultsController. //this will force setter code to run again.
            print("attempting fetch again, reset to use lazy init")
            fetchedResultsController = setFetchedResultsController() //sets it again so its correct.

            try fetchedResultsController.performFetch()
        } catch {
            print("An error occurred")
        }


        inventoryTable.reloadData()//this is important to update correctly for changes that might have been made
    }

Ошибка возникает напопробуйте fetchedResultsController.performFetch () заявление. Я получаю много ошибок до того, как произойдет настоящий сбой, говоря:"Неправильное использование API: Попытка сериализовать доступ к хранилищу для не владеющего координатором (PSC = 0x170265300, PSC хранилища = 0x0), Я проводил рефакторинг своего кода для работы с новыми стандартами swift 3. У меня такое ощущение, что я сделал что-то не так или, возможно, что-то изменилось в том, как работает контроллер полученных результатов.

Любая помощь оценивается относительно того, что может быть причиной?

Если вы считаете, что я пропускаю файл, который вам нужно просмотреть, просто дайте мне знать, и я добавлю его в соответствующий исходный код ниже.

ВОЗМОЖНЫЙ СООТВЕТСТВУЮЩИЙ КОД ИСТОЧНИКА НИЖЕ:

InventoryController.swift (весь файл)

import UIKit
import CoreData
import Foundation

class InventoryController: UIViewController, UISearchBarDelegate, UITableViewDataSource, UITableViewDelegate, NSFetchedResultsControllerDelegate {
    @available(iOS 2.0, *)

    //Create fetchedResultsController to handle Inventory Core Data Operations
    lazy var fetchedResultsController: NSFetchedResultsController<Inventory> = {
        return self.setFetchedResultsController()
    }()

    //Reference to search text for filtering
    var m_searchText = ""

    func setFetchedResultsController() -> NSFetchedResultsController<Inventory>{

        let moc = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext

        let inventoryFetchRequest : NSFetchRequest<Inventory> = Inventory.fetchRequest()

        var primarySortDescriptor = NSSortDescriptor(key: "name", ascending: true)//by default assume name.

        print("primarySortDescriptor...")

        if(g_appSettings[0].indextype=="numberfront"){
            primarySortDescriptor = NSSortDescriptor(key: "barcode", ascending: true)
        }else if(g_appSettings[0].indextype=="numberback"){
            primarySortDescriptor = NSSortDescriptor(key: "barcodeReverse", ascending: true)
        }else if(g_appSettings[0].indextype=="numberfourth"){
            primarySortDescriptor = NSSortDescriptor(key: "barcodeFourth", ascending: true, selector: #selector(NSString.localizedCompare(_:)))
        }

         print("set primarySortDescriptor")

        //let secondarySortDescriptor = NSSortDescriptor(key: "barcode", ascending: true)

        inventoryFetchRequest.sortDescriptors = [primarySortDescriptor]

        print("set sort descriptors to fetch request")

        var storefilter : Store? = nil
        var predicate: NSPredicate

        //Store should never be set to nil, the first store should always be selected by default.  For fringe cases just in case ... support added so doesn't break
        if(g_appSettings[0].selectedStore != nil){

            storefilter = g_appSettings[0].selectedStore
            predicate = NSPredicate(format: "store = %@", storefilter!) //default predicate assuming store is selected

            //However if search text is present then modify predicate
            if(m_searchText != ""){
                predicate = NSPredicate(format: "store = %@ AND name contains[cd] %@ OR store = %@ AND barcode contains[cd] %@", storefilter!,m_searchText,storefilter!,m_searchText)

            }
            //This will ensure correct data relating to store is showing (and if any filters present, them as well)

            inventoryFetchRequest.predicate = predicate
        }else{
            if(m_searchText != ""){
                predicate = NSPredicate(format: "name contains[cd] %@ OR barcode contains[cd] %@",m_searchText,m_searchText)
                inventoryFetchRequest.predicate = predicate
                //This will ensure correct data relating to store is showing
            }
        }

        //default assume letter section
        var frc = NSFetchedResultsController(
            fetchRequest: inventoryFetchRequest,
            managedObjectContext: moc,
            sectionNameKeyPath: "lettersection",
            cacheName: nil)

        if(g_appSettings[0].indextype=="numberfront"){
            frc = NSFetchedResultsController(
                fetchRequest: inventoryFetchRequest,
                managedObjectContext: moc,
                sectionNameKeyPath: "numbersection",
                cacheName: nil)
        }else if(g_appSettings[0].indextype=="numberback"){
            frc = NSFetchedResultsController(
                fetchRequest: inventoryFetchRequest,
                managedObjectContext: moc,
                sectionNameKeyPath: "numberendsection",
                cacheName: nil)
        }else if(g_appSettings[0].indextype=="numberfourth"){
            frc = NSFetchedResultsController(
                fetchRequest: inventoryFetchRequest,
                managedObjectContext: moc,
                sectionNameKeyPath: "numberfourthsection",
                cacheName: nil)
        }


        print("set the frc")

        frc.delegate = self

        return frc
    }

    @IBOutlet weak var searchBar: UISearchBar!
    @IBOutlet weak var inventoryTable: UITableView!

    // Start DEMO Related Code
    var numberIndex = ["0","1","2","3","4","5","6","7","8","9"]
    var letterIndex = ["A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"]

    var previousNumber = -1 //used so we show A,A, B,B, C,C etc for proper testing of sections

    func createInventoryDummyData(number: Int) -> Inventory{
        let moc = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext

        let tempInventory = NSEntityDescription.insertNewObject(forEntityName: "Inventory", into: moc) as! Inventory
        if(number-1 == previousNumber){
            tempInventory.name = "\(letterIndex[number-2])-Test Item # \(number)"
            previousNumber = -1//reset it again
        }else{
            tempInventory.name = "\(letterIndex[number-1])-Test Item # \(number)"
            previousNumber = number //set previous letter accordingly
        }
        tempInventory.barcode = "00000\(number+1)00\(number)"

        //special exception to demo barcode reader
        if(number==5){
            tempInventory.barcode = "0051111407592"
        }

        if(number==6){
            tempInventory.barcode = "0036000291452"
        }

        tempInventory.barcodeReverse = String(tempInventory.barcode!.characters.reversed())

        //Convert barcode into array of characters and take note if its size for indexing
        let bcArraySize = Int(tempInventory.barcode!.characters.count) - 1//for correct indexing
        var bcArray = tempInventory.barcode!.characters.map { String($0) }

        print(bcArray)
        print(bcArraySize)

        //Take the digits from the 4th one at a time and convert to strings concatenating as you go.
        let fourth = "\(bcArray[bcArraySize-3])"+"\(bcArray[bcArraySize-2])"+"\(bcArray[bcArraySize-1])"+"\(bcArray[bcArraySize])"

        print(fourth)

        //Finally convert that into a number again and set to barcodeFourth
        tempInventory.barcodeFourth = fourth

        print(tempInventory.barcodeFourth!)

        //tempInventory.barcodeFourth =
        //print(tempInventory.barcodeReverse)


        tempInventory.currentCount = 0
        tempInventory.id = number as NSNumber?
        tempInventory.imageLargePath = "http://distribution.tech//uploads/inventory/7d3fe5bfad38a3545e80c73c1453e380.png"
        tempInventory.imageSmallPath = "http://distribution.tech//uploads/inventory/7d3fe5bfad38a3545e80c73c1453e380.png"
        tempInventory.addCount = 0
        tempInventory.negativeCount = 0
        tempInventory.newCount = 0
        tempInventory.store_id = 1 //belongs to same store for now

        //Select a random store to belong to 0 through 2 since array starts at 0
        let lo = 0;
        let hi = 2;
        let aRandomInt = Int.random(range:lo...hi)
        tempInventory.setValue(g_storeList[aRandomInt], forKey: "store") //assigns inventory to one of the stores we created.

        return tempInventory
    }

    func createStoreDummyData(number:Int) -> Store{
        let moc = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext

        let tempStore = NSEntityDescription.insertNewObject(forEntityName: "Store", into: moc) as! Store

        tempStore.address = "100\(number) lane, Miami, FL"
        tempStore.email = "store\(number)@centraltire.com"
        tempStore.id = number as NSNumber?
        tempStore.lat = 1.00000007
        tempStore.lng = 1.00000008
        tempStore.name = "Store #\(number)"
        tempStore.phone = "123000000\(number)"

        return tempStore
    }

    // End DEMO Related Code

    override func viewDidLoad() {
        super.viewDidLoad()

        let moc = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext

        print("InventoryController -> ViewDidLoad -> ... starting inits")

//        // Do any additional setup after loading the view, typically from a nib.
//        print("InventoryController -> ViewDidLoad -> ... starting inits")
//
        //First check to see if we have entities already.  There MUST be entities, even if its DEMO data.
        let inventoryFetchRequest = NSFetchRequest<Inventory>(entityName: "Inventory")
        //let storeFetchRequest = NSFetchRequest(entityName: "Store")

        do {
            let inventoryRecords = try moc.fetch(inventoryFetchRequest)
            //Maybe sort descriptor here? But how to organize into sectioned array?

            if(inventoryRecords.count<=0){

                g_demoMode = true
                print("No entities found for inventory.  Demo mode = True.  Creating default entities & store...")

                //Reset the Stores
                g_storeList = [Store]()

                var store : Store //define variable as Store type

                for index in 1...3 {
                    store = createStoreDummyData(number: index)
                    g_storeList.append(store)
                }

                //save changes for inventory we added
                do {
                    try moc.save()
                    print("saved to entity")
                }catch{
                    fatalError("Failure to save context: \(error)")
                }

                var entity : Inventory //define variable as Inventory type

                for index in 1...52 {
                    let indexFloat = Float(index/2)+1
                    let realIndex = Int(round(indexFloat))
                    entity = createInventoryDummyData(number: realIndex)
                    g_inventoryItems.append(entity)
                }

                //Save the changes
                (UIApplication.shared.delegate as! AppDelegate).saveContext()

                print("finished creating entities")
            }

        }catch{
            fatalError("bad things happened \(error)")
        }




//        //perform fetch we need to do.
//        do {
//            try fetchedResultsController.performFetch()
//        } catch {
//            print("An error occurred")
//        }

        print("InventoryController -> viewDidload -> ... finished inits!")
    }

    override func viewWillAppear(_ animated: Bool) {
        print("view appearing")
        //When the view appears its important that the table is updated.

        //Trigger Event on SearchBar in case returning from BarCode Scanner
//        self.searchBar:SearchBar textDidChange:recentlySearchedWord;
        //searchBar.performSelector(":textDidChange")

        //Perform another fetch again to get correct data~
        do {
            //fetchedResultsController. //this will force setter code to run again.
            print("attempting fetch again, reset to use lazy init")
            fetchedResultsController = setFetchedResultsController() //sets it again so its correct.

            try fetchedResultsController.performFetch()
        } catch {
            print("An error occurred")
        }


        inventoryTable.reloadData()//this is important to update correctly for changes that might have been made
    }

    // MARK: - Navigation

    // In a storyboard-based application, you will often want to do a little preparation before navigation
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        // Get the new view controller using segue.destinationViewController.
        // Pass the selected object to the new view controller.
        print("inventoryItemControllerPrepareForSegueCalled")

        if segue.identifier == "inventoryInfoSegue" {
            let vc = segue.destination as! InventoryItemController
            vc.hidesBottomBarWhenPushed = true //hide the tab bar.  This prevents crashing error from being on this page then syncing & returning.
            if let cell = sender as? InventoryTableViewCell{
                vc.inventoryItem = cell.inventoryItem //sets the inventory item accordingly, passing its reference along.
            }else{
                print("sender was something else")
            }
        }

    }

//    func tableView(tableView: UITableView, sectionForSectionIndexTitle title: String, atIndex index: Int) -> Int {
//        //This scrolls to correct section based on title of what was pressed.
//        return letterIndex.indexOf(title)!
//    }

    func sectionIndexTitles(for tableView: UITableView) -> [String]? {
        //This is smart and takes the first letter of known sections to create the Index Titles
        return self.fetchedResultsController.sectionIndexTitles
    }

    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, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "InventoryTableCell", for: indexPath as IndexPath) as! InventoryTableViewCell

        print("IndexPath=")
        print(indexPath)

        let inventory : Inventory = fetchedResultsController.object(at: indexPath as IndexPath)
        cell.inventoryItem = inventory

        cell.drawCell() //uses passed inventoryItem to draw it's self accordingly.

        return cell
    }


    func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {

        if let sections = fetchedResultsController.sections {
            let currentSection = sections[section]
            return currentSection.name
        }

        return nil
    }

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

        return 0
    }

    func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
        //dispatch_async(dispatch_get_main_queue()) {
        //[unowned self] in
        print("didSelectRowAtIndexPath")//does not recognize first time pressed item for some reason?
        let selectedCell = self.tableView(tableView, cellForRowAt: indexPath) as? InventoryTableViewCell

        self.performSegue(withIdentifier: "inventoryInfoSegue", sender: selectedCell)
        //}
    }


    @IBAction func BarcodeScanBarItemAction(sender: UIBarButtonItem) {
        print("test of baritem")
    }
    @IBAction func SetStoreBarItemAction(sender: UIBarButtonItem) {
        print("change store interface")
    }

    func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
        self.barcodeTextDidChange(searchText: searchText)
    }

    func barcodeTextDidChange(searchText: String){
        print("text is changing")
        //Code to change NSFetchRequest Here~ & Reload Table

        m_searchText = searchText //sets the local variable to this class so the setFetchedResultsController() will update accordingly

        //Perform another fetch again to get correct data~
        do {
            //fetchedResultsController. //this will force setter code to run again.
            print("attempting fetch again, reset to use lazy init")
            fetchedResultsController = setFetchedResultsController() //sets it again so its correct.
            try fetchedResultsController.performFetch()
        } catch {
            print("An error occurred")
        }

        inventoryTable.reloadData()//refreshes the data~

    }

    func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
        print("ended by cancel")
        searchBar.text = ""

        m_searchText = "" //set the search text accordingly back to nothing.

        //Perform another fetch again to get correct data~
        do {
            //fetchedResultsController. //this will force setter code to run again.
            print("attempting fetch again, reset to use lazy init")
            fetchedResultsController = setFetchedResultsController() //sets it again so its correct.

            try fetchedResultsController.performFetch()
        } catch {
            print("An error occurred")
        }

        inventoryTable.reloadData()//refreshes the data~

        searchBar.resignFirstResponder()
    }

    func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
        print("ended by search")
        searchBar.resignFirstResponder()
    }

    func searchBarTextDidEndEditing(_ searchBar: UISearchBar) {
        print("ended by end editing")
        searchBar.resignFirstResponder()
    }

    func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) {
        print("DidBeginEditing")
        //searchBar.keyboardType = UIKeyboardType.NamePhonePad
    }


    @IBAction func unwindBackToInventory(segue: UIStoryboardSegue) {
        print("unwind attempt")

        let barcode = (segue.source as? ScannerViewController)?.barcode
        searchBar.text = barcode!

        barcodeTextDidChange(searchText: searchBar.text!)//force it to re-run function manually.

        print("barcode="+barcode!)

        inventoryTable.reloadData()//reload the data to be safe.

    }

}

//Extention to INT to create random number in range.
extension Int
{
    static func random(range: ClosedRange<Int> ) -> Int
    {
        var offset = 0

        if range.lowerBound < 0   // allow negative ranges
        {
            offset = abs(range.lowerBound)
        }

        let mini = UInt32(range.lowerBound + offset)
        let maxi = UInt32(range.upperBound   + offset)

        return Int(mini + arc4random_uniform(maxi - mini)) - offset
    }
}

globals.swift

import Foundation
import CoreData

//Array of Inventory & Store Core Data Managed Objects
var g_inventoryItems = [Inventory]()
var g_storeList = [Store]()
var g_appSettings = [AppSettings]()
var g_demoMode = false

Inventory + CoreDataProperties.swift

import Foundation
import CoreData

extension Inventory {

    @nonobjc public class func fetchRequest() -> NSFetchRequest<Inventory> {
        return NSFetchRequest<Inventory>(entityName: "Inventory");
    }

    @NSManaged var addCount: NSNumber?
    @NSManaged var barcode: String?
    @NSManaged var barcodeReverse: String?
    @NSManaged var barcodeFourth: String?
    @NSManaged var currentCount: NSNumber?
    @NSManaged var id: NSNumber?
    @NSManaged var imageLargePath: String?
    @NSManaged var imageSmallPath: String?
    @NSManaged var name: String?
    @NSManaged var negativeCount: NSNumber?
    @NSManaged var newCount: NSNumber?
    @NSManaged var store_id: NSNumber?
    @NSManaged var store: Store?

    //This is used for A,B,C ordering...
    var lettersection: String {
        let characters = name!.characters.map { String($0) }
        return (characters.first?.uppercased())!
    }

    //This is used for 1,2,3 ordering... (using front of barcode and using barcodeReverse)
    var numbersection: String {
        let characters = barcode!.characters.map { String($0) }
        return (characters.first?.uppercased())!
    }

    //This is used for 0000000123 ordering...(uses back number of barcode)
    var numberendsection: String {
        let characters = barcodeReverse!.characters.map { String($0) }
        return (characters.first?.uppercased())!
    }

    //This is used for 0000000 -> 0123 ordering...(uses back 4th number of barcode)
    var numberfourthsection: String {
        let characters = barcodeFourth!.characters.map { String($0) }
        //print("characters")
        //print(characters)
        return (characters.first?.uppercased())!
    }

}

Inventory.Swift

import Foundation
import CoreData


class Inventory: NSManagedObject {

// Insert code here to add functionality to your managed object subclass

}

Скриншоты ошибок

Ответы на вопрос(2)

Ваш ответ на вопрос