Hello. I’ve wrestled with this for four days without success so I could use some help. The code below simply adds a new record to an Entity (‘Activity’). This code works fine and does what it is supposed to on add and delete. However, the entity now has a relationship from itself (Activity) to another Entity (Player). Each Activity has 1 player. The view is loaded on init with the currently active player and that record is stored in a state variable. Now when the viewcontext is saved, the record stored in the state variable (a Player record is invalidated). This physically is seen as the players name disappears from the screen after the context save. This ONLY happens when the relationship field is maintained - if I don’t do that, the activity record saves fine and the state is maintained. I cant figure this out and could use help.
import SwiftUI
import CoreData
struct ActivityLogView: View {
@Environment(\.managedObjectContext) private var viewContext
@State var activityDate: Date = Date()
var activityType: ActivityType
@State var activityDescription = ""
var thePredicate: NSPredicate
var playerPredicate: NSPredicate
var passedID: String
@State var thePlayer: Player
var activityRequest: FetchRequest<Activity>
private var activities: FetchedResults<Activity>{activityRequest.wrappedValue}
@FetchRequest private var players: FetchedResults<Player>
static let taskDateFormat: DateFormatter = {
let formatter = DateFormatter()
formatter.dateStyle = .medium
return formatter
}()
init(moc: NSManagedObjectContext, id: String, inputActivityType: ActivityType){
// Get the current active player
self.passedID = id
if self.passedID == "" {
self.playerPredicate = NSPredicate(value: true)
} else {
self.playerPredicate = NSPredicate(format: "%K == %@", "id", id
as CVarArg)
}
let fetchRequest: NSFetchRequest<Player> = Player.fetchRequest()
fetchRequest.sortDescriptors = []
fetchRequest.predicate = self.playerPredicate
self._players = FetchRequest(fetchRequest: fetchRequest)
do {
let firstPlayer = try moc.fetch(fetchRequest)
if firstPlayer.isEmpty {
self._thePlayer = State(initialValue: Player())
} else {
self._thePlayer = State(initialValue: firstPlayer[0])
}
} catch {
fatalError("Problem fetching players in seasons...")
}
/*
self.playerRequest = FetchRequest(
entity: Player.entity(),
sortDescriptors: [],
predicate: self.playerPredicate,
animation: .easeIn
)
*/
self.activityType = inputActivityType
self.thePredicate = NSPredicate(format: "activityType == %i", inputActivityType.rawValue)
// TODO: Limit Fetch Rows (requires extension and new fetch request on Activity object
self.activityRequest = FetchRequest(
entity: Activity.entity(),
sortDescriptors: [NSSortDescriptor(keyPath: \Activity.activityDate, ascending: false)],
predicate: self.thePredicate,
animation: .easeIn
)
}
var body: some View {
VStack{
VStack{
HStack{
if !players.isEmpty {
Text("\(thePlayer.givenName ?? "default") \(thePlayer.familyName ?? "player")")
.bold()
.padding(.horizontal)
}
Spacer()
}
HStack{
Text("Date: ")
.padding(.leading)
DatePicker("", selection: $activityDate, displayedComponents: .date)
Spacer()
Text("\(activityType.fullDisplay) ")
Button(action: saveActivity){
Image(systemName: "plus.circle.fill")
}
.padding(.horizontal)
.disabled(activityDescription.isEmpty)
}
TextField("description", text: $activityDescription)
.padding(.horizontal)
}
.padding(.top)
VStack{
if !activities.isEmpty {
Divider()
HStack{
Text("ACTIVITIES")
.font(.body)
.fontWeight(/*@START_MENU_TOKEN@*/.bold/*@END_MENU_TOKEN@*/)
Spacer()
}
}
List{
ForEach(activities, id: \.self) { activity in
// NavigationLink(destination: TeamDetailView(
// team: team,
// teamName: team.teamName ?? "",
// coach: team.coach ?? "",
// assistantCoach: team.assistantCoach ?? ""
// )
// .environment(\.managedObjectContext, self.viewContext)){
// Text("\(team.teamName ?? "")")
// }
VStack(alignment: .leading){
Text("\(activity.activityDate!, formatter: ActivityLogView.taskDateFormat)")
Text("\(activity.activityDescription ?? "")")
.font(.callout)
.italic()
}
}
.onDelete(perform: deleteActivity)
}.listStyle(PlainListStyle())
}
.navigationTitle("Activities")
.navigationBarTitleDisplayMode(.inline)
}
}
private func deleteActivity(offsets: IndexSet) {
withAnimation {
offsets.map { activities[$0] }.forEach(viewContext.delete)
do {
try viewContext.save()
} catch {
// Replace this implementation with code to handle the error appropriately.
let nsError = error as NSError
fatalError("Unresolved error deleting activity\(nsError), \(nsError.userInfo)")
}
}
}
private func saveActivity() {
withAnimation {
let newActivity = Activity(context: viewContext)
newActivity.id = UUID()
newActivity.activityDate = activityDate
activityDate = Date()
newActivity.activityType = activityType.rawValue
newActivity.activityDescription = activityDescription
activityDescription = ""
newActivity.whichPlayer = thePlayer
//players.first?.mutableSetValue(forKey: "activityList").add(newActivity)
do {
try newActivity.managedObjectContext?.save()
} catch {
// TODO: Replace this implementation with code to handle the error appropriately.
let nsError = error as NSError
fatalError("Unresolved error saving activity\(nsError), \(nsError.userInfo)")
}
}
}
}
After the first save: