0

I have a function that saves to viewContext while in a detailView. The function grabs the users location, so it isn’t instant. The function works properly if I stay on the detailView for the item I selected, or go the to detialView for a different item. If the user leaves the detailView and goes back to the previous screen the function doesn’t work. I think this is due to there being no viewContext. I’ve tried declaring the viewContext as a separate variable but I can’t get that to work.

This should be all of the code relevant to the question. I can post the locationManager and CoreData if needed.


import SwiftUI

@main

struct MyApp: App {
    @StateObject private var locationManager = LocationManagerModel()
    let persistanceController = PersistanceController.shared
    var body: some Scene {
        WindowGroup {
            ContentView()
                .environment(\.managedObjectContext, persistanceController.container.viewContext)
                .environmentObject(locationManager)
                
                .transition(AnyTransition.opacity.animation(.easeInOut(duration: 0.5)))
                .accentColor(.green)
        }
    }
}
import SwiftUI
import CoreLocation
import CoreData
import Combine

struct ContentView: View {
    @State private var shotClub = Club()
    @State private var ballClub = Club()
    @State private var showNewClub: Bool = false
    @State var roundStarted = false
    @EnvironmentObject var locationManager: LocationManagerModel
    @AppStorage("NUMBER_KEY") var counter = 0
    @AppStorage("NUMBER_KEY") var putts = 0
    @AppStorage("BOOLEAN") var premium = false

    var body: some View {
        TabView{
            ClubListView()
                .tabItem {
                    Image(systemName: "list.bullet.rectangle.portrait")
                }
                .accentColor(Color.green)
        }
    }
}
import SwiftUI

struct ClubListView: View {
    @Environment(\.managedObjectContext) private var viewContext
    @FetchRequest(
        entity: Club.entity(),
        sortDescriptors:[],
        animation: .default)
    private var clubs:FetchedResults<Club>
    @State private var shotClub = Club()
    @State private var ballClub = Club()
    @State private var showNewClub: Bool = false
    @State private var showSettings: Bool = false
    @EnvironmentObject var locationManager: LocationManagerModel
    var body: some View {
        let addNewShot = NewShotModel(locationManager: locationManager)
        NavigationView{
            ZStack {
                List {
                    ForEach(clubs.sorted{$0.averageYardage > $1.averageYardage}) {club in 
                        NavigationLink(destination: ClubDetailView(club: club,  waiting: $locationManager.waiting, shotCoord: $locationManager.shotCoord, ballCoord: $locationManager.ballCoord,  shotClub: $shotClub, ballClub: $ballClub, distanceYards: $locationManager.distanceYards, showError: $locationManager.showError)
                            .environmentObject(locationManager)
                            .onChange(of: locationManager.distanceChange, perform: {
                                value in
                                if locationManager.distanceYards != nil{
                                    addNewShot.addNewShot(shotClub: shotClub, newShot: locationManager.distanceYards!, waiting: locationManager.waiting, ballClub: ballClub, viewContext: viewContext)
                                    
                                    shotClub = ballClub
                                    print(locationManager.distanceYards as Any) 
                                }
                            }), label:{
                                ClubListRow(club: club, shotClub: $shotClub).environmentObject(locationManager)
                            } )
                    }
                    .onDelete(perform: deleteClub)
                } //: List
            } //: ZStack
            .navigationTitle("Clubs")
            .toolbar {
                ToolbarItem(placement: .navigationBarTrailing) {
                    Button(action: {
                        self.showNewClub = true
                    }) {
                        Image(systemName: "plus.circle.fill")
                            .foregroundColor(.accentColor)
                    } //: Button
                } //: ToolbarItem
            }
        }
        .environmentObject(locationManager)
        .sheet(isPresented: $showNewClub) {
            NewClubView(isShow: $showNewClub).accentColor(Color.green)
        }
    }
    private func deleteClub(index: IndexSet) -> Void {
        withAnimation {
            index.map { clubs[$0] }.forEach(viewContext.delete)
            do {
                try viewContext.save()
            } catch {
                let nsError = error as NSError
                fatalError("Unresolved error \(nsError.localizedDescription)")
            }
        }
    }
}


import SwiftUI
import CoreLocation
import CoreData


struct ClubDetailView: View {
    
    @ObservedObject var club: Club
    @Environment(\.managedObjectContext) private var viewContext
    @EnvironmentObject var locationManager: LocationManagerModel
    @State var newShot: Int = 0
    @Binding var waiting: Bool
    @Binding var shotCoord: CLLocation?
    @Binding var ballCoord: CLLocation?
    @Binding var shotClub: Club
    @Binding var ballClub: Club
    @Binding var distanceYards: Int?
    @Binding var showError: Bool
    var body: some View{
        ZStack {
            VStack{
                List {
                    if self.club.putter == false {
                        Section {
                            Text("Average distance: \(club.averageYardage) yards")
                            Text("\(club.strokesList.count) Strokes Counted")
                            Text("Distance Standard Deviation: \(lround(club.strokesStandardDeviation))y")
                        }
                        
                        Section {
                            ForEach(club.strokesList, id: \.self) { stroke in
                                Text("\(stroke)y") // Display your stroke data here
                            }.onDelete(perform: deleteStroke)
                        }
                    } else {
                        Section {
                            Text("Average Putts per Hole: club.strokesList.count / holes played")
                            Text("\(club.strokesList.count) Strokes Counted")
                        }
                    }
                }
                Spacer()
                
                if waiting == false && club.putter == false {
                    Button(action: {locationManager.currentLocation(mode: .shot)
                        shotClub = club
                    },label: {
                        Text("Swing Location")
                            .foregroundColor(.white)
                            .font(.system(.title, design: .rounded, weight: .bold))
                            .frame(maxWidth: .infinity)
                    })
                    .buttonStyle(.borderedProminent)
                    .alert("Shot Location has not been recorded. Please try again.", isPresented: $showError) {
                        Button("OK", role: .cancel) {}
                    }
                }
                
                if waiting && club == shotClub{
                    Button(action: {
                        waiting = false
                    }, label: {
                        Text("Now Tracking")
                            .foregroundColor(.white)
                            .font(.system(.title, design: .rounded, weight: .bold))
                            .frame(maxWidth: .infinity)
                    })
                    .buttonStyle(.borderedProminent)
                    .tint(.red)
                }
                
                if waiting && club != shotClub && club.putter == false{
                    Button(action: {locationManager.currentLocation(mode: .ball)
                        ballClub = club
                    }, label: {
                        Text("Ball Location")
                            .foregroundColor(.white)
                            .font(.system(.title, design: .rounded, weight: .bold))
                            .frame(maxWidth: .infinity)
                    })
                    .buttonStyle(.borderedProminent)
                }
            }
        }
    }
    private func deleteStroke(at offsets: IndexSet) {
        club.strokesList.remove(atOffsets: offsets)
        do {
            try viewContext.save()
        } catch {
            print("Error saving context: \(error)")
        }
    }
}
extension UINavigationController{
    open override func viewWillLayoutSubviews() {
        navigationBar.topItem?.backBarButtonItem = UIBarButtonItem(title: "", style: .plain, target: nil, action: nil)
    }   
}

import SwiftUI
import CoreData
import CoreLocation


class NewShotModel: ObservableObject {
    @Environment(\.managedObjectContext) private var viewContext
    private let locationManager: LocationManagerModel
    init(locationManager: LocationManagerModel) {
        self.locationManager = locationManager
    }
    func addNewShot(shotClub: Club, newShot: Int, waiting: Bool, ballClub: Club, viewContext: NSManagedObjectContext) -> Void {
        let shotClub = shotClub
        let newShot = newShot
        shotClub.strokesList.append(newShot)
        do {
            try viewContext.save()
            print("shot saved")
            locationManager.shotCoord = locationManager.ballCoord
        } catch {
            let nsError = error as NSError
            print("Unresolved error \(nsError.localizedDescription)")
        }
    }
}
jhbetts
  • 17
  • 5
  • An unclear question and a lot of code but here are two observations, 1) you can’t use @Environment outside a view so remove it from NetShotModel and instead inject the view context or get it from the PersistentControler class and 2) don’t declare a variable inside the view’s `body`, make it a property instead – Joakim Danielson Aug 30 '23 at 07:20

0 Answers0