Fetch Core Data Issue

I have an issue fetching objects from Core Data in my favourites section. I can save it with a button but I can not seem to retrieve the objects when I restart the app. I get it to print over 40000 (I have no idea why) and I see no favourites in my favourites section when I added them before.

   override func viewDidLoad() {
    super.viewDidLoad()
    if currentFav == nil {
    //display nil
    fetchSave()
    self.tableView.separatorStyle = UITableViewCell.SeparatorStyle.none
    } else {
    NotificationCenter.default.post(name: .passFavNotification,
    object: self.currentFav)
    DispatchQueue.main.asyncAfter(deadline: .now() + 300) {
        self.reviewRating.requestReview(isWrittenReview: false)
      }
    }
  }
   //trying to get this function to retrieve my objects in the persistent store
   func fetchSave() {
    let fetchRequest: NSFetchRequest<CurrentPlayers>
    fetchRequest = CurrentPlayers.fetchRequest()
    do {
      let objects = try context.fetch(fetchRequest)
      //I get over 40000 objects
      print("These are how many saved favourites I have: \(objects.count)")
      tableView.reloadData()
    } catch {
      print("Fetch failed")
    }
   }
   
  @IBAction func save(_ sender: UIBarButtonItem) {
    let saveFav = CurrentPlayers(context: context)
    // Assign values to the entity's properties
    for o in prefArr {
    saveFav.yahooName = o.yahooName
    saveFav.team = o.team
    saveFav.position = o.position
    saveFav.photoUrl = o.photoUrl
    print("These are my saved objects: \(saveFav)")
    // To save the new entity to the persistent store, call
    // save on the context
    }
    do {
      try context.save()
    } catch {
      print(error)
    }
  }
Answered by ZoneX in 687065022

Alright I will close this thread because I got more updates.

Could you explain in detail what you do and get and provide enough code so that we can understand:

I can save it with a button

Is it ?  

@IBAction func save(_ sender: UIBarButtonItem) {

If so, where do you save ? Where is context defined ?

but I can not seem to retrieve the objects when I restart the app.

Where do you try to reload ?

I get it to print over 40000 (I have no idea why)

What do you mean ?

and I see no favourites in my favourites section when I added them before.

Where are those favourites in the code above ?

Is it ?  

Yes that is my save button.

If so, where do you save ? Where is context defined ?

The button is where I save it. Context is defined at the beginning of my favourites section.

let context = PersistenceService.persistentContainer.viewContext

Where do you try to reload ?

I want to reload it when I go back to my favourites section after I restart the app but it reloads every time I go to my favourites section whether or not I add a favourite.

What do you mean ?

Lets say I add a favourite. I go to my favourites section there is one favourite, the one I added but when I restart the app , I can't see the favourite I added before in my favourites section.

Where are those favourites in the code above ?

my favourites are currentFav but when I click the save button I define them as saveFav.

Really very hard, as each time, because you don't give a comprehensive view on your code.

I get it to print over 40000

Could you explain ?

Some unclear points in your code:

  • How is CurrentPlayers defined ?
  • Have you checked that data are effectively saved ? You should do a fetch just after save and test how many objects you get.

Do you know which branch is executed in viewDidLoad ? Add some print:

   override func viewDidLoad() {

    super.viewDidLoad()
    if currentFav == nil {
          print("currentFav", currentFav)    //display nil
          fetchSave()         // <<<---- What do you get on console here ?
          self.tableView.separatorStyle = UITableViewCell.SeparatorStyle.none
    } else {
          print("currentFav", currentFav)
          NotificationCenter.default.post(name: .passFavNotification, object: self.currentFav)   // <<<--- Who handles this notification ? How
          DispatchQueue.main.asyncAfter(deadline: .now() + 300) {
             self.reviewRating.requestReview(isWrittenReview: false)
           }
    }
  }
  • You post a notification: please show how you handle it. Does fetch occur there ?

This is my currentPlayers class

import Foundation
import CoreData

enum DecoderConfigurationError: Error {
  case missingManagedObjectContext
}


extension CodingUserInfoKey {
  static let managedObjectContext = CodingUserInfoKey(rawValue: "managedObjectContext")!
}

@objc(CurrentPlayers)
public class CurrentPlayers: NSManagedObject, Decodable {

   
  enum CodingKeys: String, CodingKey {
      case photoUrl = "PhotoUrl"
      case firstName = "FirstName"
      case lastName = "LastName"
      case position = "Position"
      case team = "Team"
      case yahooName = "YahooName"
      case status = "Status"
      case jerseyNumber = "Jersey"
    }
    
    public static var managedObjectContext: NSManagedObjectContext?
    
    required public convenience init(from decoder: Decoder) throws {
      guard let context = decoder.userInfo[.managedObjectContext] as? NSManagedObjectContext else {
        throw DecoderConfigurationError.missingManagedObjectContext
      }
      
     self.init(context: context)
      //...
     let values = try decoder.container(keyedBy: CodingKeys.self)
     photoUrl = try values.decode(String.self, forKey: CodingKeys.photoUrl)
     firstName = try values.decode(String.self, forKey: CodingKeys.firstName)
     lastName = try values.decode(String.self, forKey: CodingKeys.lastName)
     position = try values.decode(String.self, forKey: CodingKeys.position)
     team = try values.decode(String.self, forKey: CodingKeys.team)
     yahooName = try values.decodeIfPresent(String.self, forKey: CodingKeys.yahooName)
     status = try values.decode(String.self, forKey: CodingKeys.status)
     jerseyNumber = try values.decodeIfPresent(Int64.self, forKey: CodingKeys.jerseyNumber) ?? 0
    }
}

I changed the viewDidLoad code because my currentFav was nil in both conditions and I did not need the post notification there.

   override func viewDidLoad() {
    super.viewDidLoad()
    fetchSave()
    if prefArr.count == 0 {
    print("There are 0 favourites")         //when I add a favourite(s) and the app terminates it goes to this block
    self.tableView.separatorStyle = UITableViewCell.SeparatorStyle.none
    } else {
    print("There are favourites")            //when I add a favourite(s) and do not terminate it goes to this block
    DispatchQueue.main.asyncAfter(deadline: .now() + 300) {
        self.reviewRating.requestReview(isWrittenReview: false)
      }
    }
  }

Whenever I click save it prints 1 more object then I already have regardless of how many favourites there are.

   @IBAction func save(_ sender: UIBarButtonItem) {
    var saveFav = CurrentPlayers(context: context)
    // Assign values to the entity's properties
    for o in prefArr {
    saveFav.yahooName = o.yahooName
    saveFav.team = o.team
    saveFav.position = o.position
    saveFav.photoUrl = o.photoUrl
    print("These are my saved objects: \(saveFav)")
    // To save the new entity to the persistent store, call
    // save on the context
    }
    do {
      try context.save()
      fetchSave()     //if I have 53704 saved objects then after I click save I have 53705 it seems to always be over 40000         
    } catch {
      print(error)
    }
  }

I can easily access my favourite now with HockeyDetailVC.item I made the variable item static. I think it can prove useful. Really appreciate the help.

Whenever I click save it prints 1 more object then I already have regardless of how many favourites there are.

Isn't it what you expect to have one more object ? I don't really understand your comment here.

if I have 53704 saved objects then after I click save I have 53705 it seems to always be over 40000.

What is the real number of objects you should have ?

Isn't it what you expect to have one more object ? I don't really understand your comment here.

What I mean is whether I add a favourite or not and click save it just adds 1

What is the real number of objects you should have ?

func fetchSave() {
    let fetchRequest: NSFetchRequest<CurrentPlayers>
    fetchRequest = CurrentPlayers.fetchRequest()
    do {
      let objects = try context.fetch(fetchRequest)
      //I get over 40000 objects
      print("These are how many saved favourites I have: \(objects.count)")
      tableView.reloadData()
    } catch {
      print("Fetch failed")
    }
   }

 @IBAction func save(_ sender: UIBarButtonItem) {
    var saveFav = CurrentPlayers(context: context)
    // Assign values to the entity's properties
    for o in prefArr {
    saveFav.yahooName = o.yahooName
    saveFav.team = o.team
    saveFav.position = o.position
    saveFav.photoUrl = o.photoUrl
    // To save the new entity to the persistent store, call
    // save on the context
    print("These are my saved objects: \(saveFav)")
    }
    do {
      try context.save()
      fetchSave()
    } catch {
      print(error)
    }
  }

Lets say I have 1 favourite and save it, my saveFav shows the favourite I saved in it's CoreData form, the entity, id, data and my objects.count should be 1 but it's over 56848. If I save it again it should still be 1 but it's 56849.

It is still very difficult to understand your code, partly because naming of var is not explicit enough.

What is prefArr ? How is it populated (if an array).

When you save:

  • you create a new instance saveFav
  • then, I don't understand what you are doing in the loop. Is it just for displaying saveFav ?
  • I do not see in save where you reference a newly created fav

So the first questions:

  • where and how do you create a new favorite
  • how do you use it in save

When you save you create a new instance saveFav then, I don't understand what you are doing in the loop. Is it just for displaying saveFav ?

I do not see in save where you reference a newly created fav

The reason I have a loop is because I want to define all the objects in my array called prefArr as saveFav when the save button is pressed and I try to save them to the context.

where and how do you create a new favorite

I create a new favourite by adding it with a button in another VC

class HockeyDetailVC: UITableViewController {
   
  let imageController = FetchImage()
   
  static var item: CurrentPlayers?

     @IBAction func addToFav(_ sender: Any) {
        let alert = UIAlertController(title: "Favourite Added 💙", message: "\(name.text ?? "") is added to favourites", preferredStyle: .alert)
        alert.addAction(UIAlertAction(
          title: "OK",
          style: UIAlertAction.Style.default)
        { _ in
        FavouriteManager.shared.add(HockeyDetailVC.item!)
          })
        self.present(alert, animated: true, completion: nil)
      }
class FavouriteManager {
   
   
  static let shared = FavouriteManager()
   
  var favArr : [CurrentPlayers] = []
   

  //this function is called when button is clicked
  func add(_ player: CurrentPlayers) {
    NotificationCenter.default.post(
      name: .passFavNotification,
      object: player
    )
    favArr.append(player)
    for player in favArr {
      if !favArr.contains(player) {
        favArr.append(player)
      }
    }
    
  }
}

how do you use it in save

The save button is supposed to take the objects in my array and make them saveFav. Hope this makes sense.

The reason I have a loop is because I want to define all the objects in my array called prefArr as saveFav when the save button is pressed and I try to save them to the context.

OK, but you create a single instance.

Why don't you create in the loop ?

Code could become like this:

 @IBAction func save(_ sender: UIBarButtonItem) {
    // -->>  NOT HERE  var saveFav = CurrentPlayers(context: context)
    // Assign values to the entity's properties
    for o in prefArr {
       var saveFav = CurrentPlayers(context: context)     // <<-- Create a new instance each time here
       saveFav.yahooName = o.yahooName
       saveFav.team = o.team
       saveFav.position = o.position
       saveFav.photoUrl = o.photoUrl
       // To save the new entity to the persistent store, call save on the context
       do {   // <<-- Save each
          try context.save()
          fetchSave()  // Not sure it is needed
         print("This is my saved object: \(saveFav)")    
       } catch {
          print(error)
      }
    }

  }

Okay I see your point this will save each of my instances that I have. The issue is my fetchSave() func then. I should be able to see my favourites when I restart the app.

 override func viewDidLoad() {
    super.viewDidLoad()
    fetchSave()
    //don't seem to have any saveFavs in my array when it restarts
    if prefArr.count == 0 {
    print("There are 0 favourites")
    self.tableView.separatorStyle = UITableViewCell.SeparatorStyle.none
    } else {
    print("There are favourites")
    DispatchQueue.main.asyncAfter(deadline: .now() + 300) {
        self.reviewRating.requestReview(isWrittenReview: false)
      }
    }
  }
 func fetchSave() {
   //If I have any number of favourites and restart the app this code block executes
    if prefArr.count == 0 {
      print("No saved favourites")
    } else {
    let fetchRequest: NSFetchRequest<CurrentPlayers>
    fetchRequest = CurrentPlayers.fetchRequest()
    do {
      let objects = try context.fetch(fetchRequest)
      print("These are how many saved favourites I have: \(objects.count)")
      tableView.reloadData()
    } catch {
      print("Fetch failed")
    }
   }
}

Please tell exactly where you stand now: are the favorites saved corectly 

When I click save it prints out all the favourites I saved as a CoreData entity so i think it's saved.

What are all print output you get when you restart the app ?

When I restart the app it prints no saved favourites and there are 0 favourites.

provide more precise output with         print("There are favourites", prefArr.count)

This prints how many favourites I have, depending on the number I added.

Okay so I got rid of fetchSave I don't think I need it since when it saves it should not need to be fetched because I add it to the array. I still have trouble getting the save to work.

   @IBAction func save(_ sender: UIBarButtonItem) {
    // Assign values to the entity's properties
    for o in prefArr {
    let saveFav = CurrentPlayers(context: context)
    saveFav.yahooName = o.yahooName
    saveFav.team = o.team
    saveFav.position = o.position
    saveFav.photoUrl = o.photoUrl
    // To save the new entity to the persistent store, call
    // save on the context
    print("These are my saved objects: \(saveFav)")
    do {
      try context.save()
      prefArr.append(saveFav) //favourite is added
    } catch {
      print("error is: \(error)")
    }
  }
  }
}

 I still have trouble getting the save to work.

What problem exactly ? No save at all ? partial save ?

What do you get from:

    print("These are my saved objects: \(saveFav)")

Could you show more of convenience init ? It seems you get only one value.

What problem exactly ? No save at all ? partial save ?

No save it all.

Could you show more of convenience init ? It seems you get only one value.

I added a prefArr.count to it. If I add a player to my favourites I get this. It is a Core Data entity as you can see and I also get how many favourites I have from the count.

These are my saved objects: <CurrentPlayers: 0x6000033fd630> (entity: CurrentPlayers; id: 0x600001031f80 <x-coredata:///CurrentPlayers/tB4FE931D-0582-445E-BFAB-099B58D904391054>; data: {
  firstName = nil;
  jerseyNumber = 0;
  lastName = nil;
  photoUrl = "https://s3-us-west-2.amazonaws.com/static.fantasydata.com/headshots/nhl/low-res/30000007.png";
  position = G;
  status = nil;
  team = MON;
  yahooName = "Carey Price";
})
1

Another update I modified my save function and I think it is consistent with my print results. Like it prints 3 favourites when there are 3 favourites and shows me my saved favourites. I just need need to figure out how to retrieve the data when the app restarts because nothing is saved. Any input would really help.

   @IBAction func save(_ sender: UIBarButtonItem) {
    let entity = NSEntityDescription.entity(forEntityName: "CurrentPlayers", in: context)!
    let saveFav = CurrentPlayers(entity: entity, insertInto: context)
    for o in prefArr {
      saveFav.yahooName = o.yahooName
      saveFav.team = o.team
      saveFav.position = o.position
      saveFav.photoUrl = o.photoUrl
    do {
      try context.save()
      print("These are my saved objects: \(saveFav)")
      print("how many saved objects: \(prefArr.count)")
    } catch {
      print("error is: \(error)")
    }
    }
  }
}
Fetch Core Data Issue
 
 
Q