NSFetchedResultsControllerDelegate не срабатывает при добавлении первого элемента в DB. Срабатывает только при добавлении второго элемента - PullRequest
0 голосов
/ 15 апреля 2020

Мое приложение имеет две панели вкладок. Первая представляет список игр, добавленных на контроллере представления, и сохраняет их в базе данных ядра. Включение второй вкладки / представления считывает из базы данных и представляет ее в виде таблицы. Я реализовал NSFetchedResultsControllerDelegate с методом выборки. Когда я добавляю первый элемент в контекст на первой вкладке и переключаюсь на вторую вкладку, FR C методы делегата (controllerWillChangeContent(_:), controller(_:didChange:at:for:newIndexPath:), controllerDidChangeContent(_:)) не вызываются, и представление таблицы пустое, в то время как я вижу arrayOfGamesCount = 1. Но когда я добавляю второй элемент, я вижу, что все методы делегата FR C получают вызов, когда я переключаюсь на вторую панель вкладок. И TableView отображает одну строку, в то время как arrayOfGamesCount = 2

Первая панель вкладок имеет 2 контроллера представления. (AddGameViewController и WelcomeViewController) AddGameV C используется для получения данных из текстовых полей и отправки их в welcomeV C.

import UIKit
import CoreData
    class WelcomeViewController: UIViewController,SendGameDataDelegate, UIAdaptivePresentationControllerDelegate {


        var games : [Game]? = []
        var gamesMo: [GameMo]? = []
        var gamed: GameMo?
        var game : Game?



        func ShouldSendGame(game: Game) {
            self.game = game
            print("\(game)")
            games?.append(game)

        }

        @IBAction func endWLButton(_ sender: UIButton) {


            saveDataToCoreData()
            print("number of games from gamesMoCount is \(gamesMo?.count ?? 0)")

            games?.removeAll()
            reloadCollectionViewData()

        }


        func saveDataToCoreData (){
            if  let appDelegate = UIApplication.shared.delegate as? AppDelegate {
                gamed = GameMo(context: appDelegate.persistentContainer.viewContext)
                if games != nil {
                    for game in games! {
                        gamed?.goal = Int32(game.goal ?? 0 )
                        gamed?.rivalGoal = Int32(game.rivalGoal ?? 0)
                        gamed?.shot = Int32(game.shots ?? 0)
                        gamed?.rivalShot = Int32(game.rivalGoal ?? 0)
                        gamed?.rivalCorners = Int32(game.rivalsCorner ?? 0)
                        gamed?.corners = Int32(game.corners ?? 0)
                        gamesMo?.append(gamed!)
                    }

                    print("Saving data to context ....")
                    appDelegate.saveContext()
                }
            }
        }
    }
    extension WelcomeViewController: UICollectionViewDelegate, UICollectionViewDataSource {

        func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
            return games?.count ?? 0
        }

        func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
            if let gameIndex = games?[indexPath.row] {
                let userGameScore = gameIndex.goal
                let rivalGameScore = gameIndex.rivalGoal
                if let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "FormCell", for: indexPath) as? FormCollectionViewCell {
                    cell.setCell(userScores: userGameScore!, rivalScores: rivalGameScore! )

                    return cell
                }

            }
            return UICollectionViewCell ()

        }
    }

На второй панели вкладок есть только один V C: AllWLeagueController, используемый для отображения элементов из базы данных.

 import UIKit
    import CoreData

    class AllWLeagueController : UITableViewController {

        var fetchRequestController : NSFetchedResultsController<GameMo>!
        var arrayOfGamesModel : [[GameMo]]? = []
        var gameMo: GameMo?
        var gamesMo: [GameMo] = []


        override func viewDidLoad() {
            validation(object: arrayOfGamesModel)

        }

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



        override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
             print("arrayOfGamesModelcount est \(arrayOfGamesModel?.count ?? 0)")

            return arrayOfGamesModel?.count ?? 0
        }
        override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {



            if let weekL = arrayOfGamesModel?[indexPath.row] {
                if let cell = tableView.dequeueReusableCell(withIdentifier: "WL") as? AllWLeaguesTableViewCell {
                    let winCounts = WLManager.winCountMethod(from: weekL)
                    let lossCounts = WLManager.lossCountMethod(from:weekL)
                    cell.setOulet(win: winCounts, loss: lossCounts, rankName: rankString)

                    cellLayer(with: cell)
                    return cell
                }
            }

   }



  extension AllWLeagueController: NSFetchedResultsControllerDelegate {

        func fetchRequest ()  {
            let fetchRequest = NSFetchRequest<GameMo>(entityName: "Game")

            fetchRequest.sortDescriptors = [NSSortDescriptor(key: "win", ascending: true)]

            if let appDelegate = (UIApplication.shared.delegate as? AppDelegate){
                let context = appDelegate.persistentContainer.viewContext

                // fetch result controller
                fetchRequestController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: context, sectionNameKeyPath: nil, cacheName: nil)
                fetchRequestController.delegate = self

                do{
                    try fetchRequestController.performFetch()

                    if let fetchedObjects = fetchRequestController.fetchedObjects {
                        gamesMo = fetchedObjects


                        print("Fetech Request Activated")
                        print(gamesMo)
                    }
                }catch{
                    fatalError("Failed to fetch entities: \(error)")
                }
            }

        }


        func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
            print("TableView beginupdates")
            tableView.beginUpdates()
        }

        func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
            switch type {
            case .insert:
                if let newIndexPath = newIndexPath {
                    print("insert")
                    tableView.insertRows(at: [newIndexPath], with: .fade)

                }
            case .delete:
                if let indexPath = indexPath {
                     print("delete")
                    tableView.deleteRows(at: [indexPath], with: .fade)
                }
            case .update:
                if let indexPath = indexPath {
                     print("update")
                    tableView.reloadRows(at: [indexPath], with: .fade)

                }

            default:
                tableView.reloadData()
            }

            if let fetchedObjects = controller.fetchedObjects {
                gamesMo = fetchedObjects as! [GameMo]
                print("we are about to append arrayOfGamesModel")
                   arrayOfGamesModel?.append(gamesMo)

            }
        }


        func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
            print("TableView endupdates")
            tableView.endUpdates()
        }
     }

1 Ответ

0 голосов
/ 15 апреля 2020

Вы совершаете роковую ошибку. В saveDataToCoreData создается только один экземпляр, а затем он перезаписывается игровыми данными на каждой итерации массива. Таким образом, ваш массив gamesMo может содержать несколько элементов, но это всегда один и тот же экземпляр, и только один экземпляр сохраняется в контексте.

Заменить saveDataToCoreData на

func saveDataToCoreData (){
   let appDelegate = UIApplication.shared.delegate as! AppDelegate 
   guard let games = games else { return }
   for game in games {
       let newGame = GameMo(context: appDelegate.persistentContainer.viewContext)
        newGame.goal = Int32(game.goal ?? 0 )
        newGame.rivalGoal = Int32(game.rivalGoal ?? 0)
        newGame.shot = Int32(game.shots ?? 0)
        newGame.rivalShot = Int32(game.rivalGoal ?? 0)
        newGame.rivalCorners = Int32(game.rivalsCorner ?? 0)
        newGame.corners = Int32(game.corners ?? 0)
        gamesMo.append(newGame)
    }
    print("Saving data to context ....")
    appDelegate.saveContext()
}

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

lazy var context : NSManagedObjectContext = {
    let appDelegate = UIApplication.shared.delegate as! AppDelegate
    return appDelegate.persistentContainer.viewContext
}()


lazy var fetchRequestController : NSFetchedResultsController<GameMo> = {
    let fetchRequest = NSFetchRequest<GameMo>(entityName: "Game")
    fetchRequest.sortDescriptors = [NSSortDescriptor(key: "win", ascending: true)]

    // fetch result controller
    let frc = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: context, sectionNameKeyPath: nil, cacheName: nil)
    frc.delegate = self

    do {
       try frc.performFetch()
       if let fetchedObjects = frc.fetchedObjects {
          self.gamesMo = fetchedObjects
          print("Fetech Request Activated")
          print(gamesMo)
       }
   } catch{
       fatalError("Failed to fetch entities: \(error)")
   }
   return frc
}()

Принудительное развертывание AppDelegate прекрасно. Ваше приложение даже не запустится, если отсутствует AppDelegate.

Я также рекомендую использовать менее неоднозначные имена переменных. games, gamesMo, gamed и game выглядят очень похоже и могут вызвать путаницу.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...