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)
}
}
}
}
}
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!