Reputation: 13
I am trying to have my player favourites persist when the app gets terminated and relaunches using Core Data. I made a save button to do just that but regardless of whether I press it or not the data I retrieve is all the players multiple times but it should only be the players I favourite. I know my save function prints the right amount of favourites and what are my favourites regardless the number. I just need to modify my viewwillappear so that it only gives me the favourites that I save with the save button.
//this is my class for my favourites
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
}
}
//this is the class that has access to the saveContext
class PersistenceService {
private init() {}
static var context: NSManagedObjectContext {
return persistentContainer.viewContext
}
// MARK: CoreData
static var persistentContainer: NSPersistentContainer = {
/*
The persistent container for the application. This implementation
creates and returns a container, having loaded the store for the
application to it. This property is optional since there are legitimate
error conditions that could cause the creation of the store to fail.
*/
let container = NSPersistentContainer(name: "playerModel")
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
// 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.
/*
Typical reasons for an error here include:
* The parent directory does not exist, cannot be created, or disallows writing.
* The persistent store is not accessible, due to permissions or data protection when the device is locked.
* The device is out of space.
* The store could not be migrated to the current model version.
Check the error message to determine what the actual problem was.
*/
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
return container
}()
static func saveContext () {
let context = persistentContainer.viewContext
if context.hasChanges {
do {
try context.save()
} 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)")
}
}
}
}
//this is in my FavouritesVC below
class FavouritesVC: UITableViewController {
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
let fetchRequest = NSFetchRequest<CurrentPlayers>(entityName: "CurrentPlayers")
do {
prefArr = try context.fetch(fetchRequest)
for p in prefArr {
if p.yahooName == "Jordan Gross" {
print("Jordan added")
}
}
print("There are this many saved favourites \(prefArr.count)")
} catch let error {
print("Could not fetch. \(error)")
}
}
@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)")
}
}
}
}
}
Upvotes: 0
Views: 106
Reputation: 285039
There is a serious mistake in the saveFav
method.
You create one new record and overwrite it with all the data in the loop, so eventually you have one new record with the data of the last item in the loop.
You have to create one record for each item in favSet
, move the creation line into the loop
@IBAction func saveFav(_ sender: UIBarButtonItem) {
print("saved button pressed")
for o in favSet {
let saveFav = CurrentPlayers(context: context)
saveFav.yahooName = o.yahooName
saveFav.team = o.team
saveFav.position = o.position
saveFav.photoUrl = o.photoUrl
}
PersistenceService.saveContext()
}
And it's recommended to name entities in singular form (CurrentPlayer
)
Upvotes: 1