Search code examples
iosswiftcore-data

Why is this creating duplicates instead of the unique CoreData entries?


I'm trying to preload some data into CoreData when the view loads for the first time and there are no entries yet and its created the correct number of entires, but then it's making them all the same values (duplicates of the same item). The idea is to create a CoreData entity with preloaded Achievements which the user can then earn throughout the app, which will just update the 'completed' attribute of that entry. Problem is, it's creating the correct number of entries, but it's creating them all the same. Here is what I currently have:

import SwiftUI
import CoreData

struct AchievementView: View {
    
    @Environment(\.managedObjectContext) private var viewContext
    @FetchRequest(entity: Achievements.entity(), sortDescriptors: [
        NSSortDescriptor(key: "id", ascending: true)])
      private var achievements: FetchedResults<Achievements>


    var body: some View {
        NavigationView {
            ZStack {
                Color("Background")
                    .ignoresSafeArea(.all)
                VStack {
                    HStack {
                        let completedCount = achievements.filter(\.completed).count
                        let totalCount = achievements.count
                        let pctComp: Double = (Double(completedCount) / Double(totalCount)) * 100
                        
                        Image(systemName: "star.circle").padding(.leading, 5)
                            .foregroundColor(.secondary)
                        Text("Achievements".uppercased())
                            .fontWeight(.heavy)
                            .foregroundColor(.secondary)
                        Spacer()
                        Text("\(completedCount) OF \(totalCount) (\(pctComp, specifier: "%.f")%)")
                            .font(.system(size:16))
                            .fontWeight(.heavy)
                            .foregroundColor(.secondary)
                            .padding(.trailing, 10)
                    }
                    ScrollView(showsIndicators: false) {
                        VStack {
                            ForEach(achievements) { achievement in
                                HStack {
                                    VStack {
                                        if achievement.completed == false {
                                            Text(achievement.image ?? "")
                                                .padding()
                                                .grayscale(0.99)
                                                .overlay (
                                                    Circle()
                                                        .stroke(Color(.systemGray6), lineWidth: 2)
                                                ).padding()
                                        } else {
                                            Text(achievement.image ?? "")
                                                .padding()
                                                .overlay (
                                                    Circle()
                                                        .stroke(Color.green, lineWidth: 2)
                                                ).padding()
                                        }
                                    }
                                    VStack{
                                        if achievement.completed == false {
                                            HStack {
                                                Text(achievement.name ?? "")
                                                    .fontWeight(.heavy)
                                                    .foregroundColor(Color(.systemGray2))
                                                Spacer()
                                            }
                                            HStack {
                                                Text(achievement.desc ?? "")
                                                    .fontWeight(.light)
                                                    .font(.system(size:10))
                                                    .foregroundColor(Color(.systemGray2))
                                                Spacer()
                                            }
                                        } else {
                                            HStack {
                                                Text(achievement.name ?? "")
                                                    .fontWeight(.heavy)
                                                Spacer()
                                            }
                                            HStack {
                                                Text(achievement.desc ?? "")
                                                    .fontWeight(.light)
                                                    .font(.system(size:10))
                                                Spacer()
                                            }
                                        }
                                    }
                                    Spacer()
                                }   .background(Color("WeekShowBack"))
                                    .cornerRadius(15)
                            }
                        }
                    }
                }.padding()
            }
        }.navigationViewStyle(StackNavigationViewStyle())
            .onAppear(perform: loadAchievements)
    }
    
    private func loadAchievements() {
        
        print("Number of Times Called")
        let achievementNames = ["First Entry", "Movie Starter"]
        let achievemnetDesc = ["Enter your first movie or show", "Track 10 Movies"]
        let achievementComp = [false, false]
        let achievementImage = ["🥇","🔟"]
        
        if achievements.count == 0 {
            
            for index in 0..<achievementNames.count {
                let newAchievement = Achievements(context: self.viewContext)
                newAchievement.name = achievementNames[index]
                newAchievement.desc = achievemnetDesc[index]
                newAchievement.completed = achievementComp[index]
                newAchievement.image = achievementImage[index]
                
                do {
                    try viewContext.save()
                    print("Achievement Loaded!!!!!!")
                } catch let error {
                    print(error)
                }
            }
        }
    }
        
}


   

Screenshot


Solution

  • I just figured it out...thank goodness! This was driving me insane but I'm not sure why this matters as I thought CoreData automatically assigns one. Anyways, I added this line to the do statement in the function:

    newAchievement.id = UUID()
    

    Now it's creating the correct achievements in CoreData and now I can get on to actually coding each achievement. Thank you everyone for the assistance!