Home > Net >  .onAppear() function not working with NavigationLink
.onAppear() function not working with NavigationLink

Time:07-25

I am working on a project that gathers real-time location data and stores it in a database (using google firebase). I am relatively new to Swift but have been coding for about 2 years. I have created a basic HomePage UI and a MapView UI, using a navigation link to move from page to page. The MapViewModel class is a class used to gather location data from the user. I am trying to call a method of the MapViewModel class with .onAppear. Although when I run the program the function is not called and "inside" isn't printed on the terminal. Am I missing something on how .onAppear and navigation links work? Please let me know what the best solution would be to call the function when the view is switched to MapView.

import SwiftUI
import MapKit

struct HomePage: View {
    @State private var viewModel = MapViewModel()
    var body: some View {
        NavigationView {
            ZStack {
                LinearGradient(
                    gradient: Gradient(colors: [.blue, Color(red:0.18, green: 0.79, blue: 0.91, opacity: 1.0)]),
                    startPoint: .topLeading,
                    endPoint: .bottomTrailing)
                .edgesIgnoringSafeArea(.all)
                NavigationLink {
                    MapView()
                }
                label: {
                    ZStack {
                        Circle()
                            .trim(from: 0.5)
                            .frame(width: 450, height: 450)
                            .foregroundColor(Color(red: 1.0, green: 0.89, blue: 0.36, opacity: 1.0))
                        
                        Text("Navigate")
                            .foregroundColor(.white)
                            .font(.system(size:32, weight: .bold, design: .default))
                            .padding(.bottom, 280)
                    }
                    .position(x: 195, y: 900)
                    .ignoresSafeArea(.all)
                }
            }
        }
    }
}

struct MapView: View {
    @State private var viewModel = MapViewModel()
    var body: some View {
        Map(coordinateRegion: $viewModel.region, showsUserLocation: true)
            .ignoresSafeArea()
            .accentColor(Color(.systemPink))
            .onAppear {
                print("inside")
                viewModel.checkIfLocationServiceIsEnabled()
            }
    }
}

final class MapViewModel : NSObject, ObservableObject, CLLocationManagerDelegate {
    
    @Published var region = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: 33, longitude: -120), span: MKCoordinateSpan(latitudeDelta: 0.5, longitudeDelta: 0.5))
     
    var locationManager: CLLocationManager?
    
    func checkIfLocationServiceIsEnabled() {
        if CLLocationManager.locationServicesEnabled() {
            locationManager = CLLocationManager()
            locationManager!.startUpdatingLocation()
            locationManager!.delegate = self
        }
        else {
            print("Print error message")
        }
    }
    
    func checkLocationAuthorization() {
        guard let locationManager = locationManager else { return }
        switch locationManager.authorizationStatus {
        case .notDetermined:
            locationManager.requestWhenInUseAuthorization()
        case .restricted:
            print("Location restricted")
        case .denied:
            print("You have denied location permission, go to settings")
        case .authorizedAlways, .authorizedWhenInUse:
            print("Inside")
            if locationManager.location?.coordinate != nil {
                region = MKCoordinateRegion(center: locationManager.location!.coordinate, span: MKCoordinateSpan(latitudeDelta: 0.5, longitudeDelta: 0.5))
            }
            else {
                print("Location not found")
            }
            
        @unknown default:
            break
        }
    }
    
    func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
        checkLocationAuthorization()
    }
    
    func locationManager(_ manager: CLLocationManager,  didUpdateLocations locations: [CLLocation]) {
        let lastLocation = locations.last!
        print(lastLocation)
                   
    }
}


struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        HomePage()
    }
}

CodePudding user response:

Use @StateObject var viewModel = MapViewModel() in your HomePage view. Add .environmentObject(viewModel) to your NavigationView and the corresponding @EnvironmentObject var viewModel: MapViewModel in MapView. Works for me.

This is the code I used for my tests, that works for me.

import Foundation
import SwiftUI
import MapKit

struct ContentView: View {
    var body: some View {
        HomePage()
    }
}

struct HomePage: View {
    @StateObject var viewModel = MapViewModel() // <-- here
    
    var body: some View {
        NavigationView {
            ZStack {
                LinearGradient(
                    gradient: Gradient(colors: [.blue, Color(red:0.18, green: 0.79, blue: 0.91, opacity: 1.0)]),
                    startPoint: .topLeading,
                    endPoint: .bottomTrailing)
                .edgesIgnoringSafeArea(.all)
                NavigationLink {
                    MapView()
                }
                label: {
                    ZStack {
                        Circle()
                            .trim(from: 0.5)
                            .frame(width: 450, height: 450)
                            .foregroundColor(Color(red: 1.0, green: 0.89, blue: 0.36, opacity: 1.0))
                        
                        Text("Navigate")
                            .foregroundColor(.white)
                            .font(.system(size:32, weight: .bold, design: .default))
                            .padding(.bottom, 280)
                    }
                    .position(x: 195, y: 900)
                    .ignoresSafeArea(.all)
                }
            }
        }.environmentObject(viewModel) // <-- here
    }
}

struct MapView: View {
    @EnvironmentObject var viewModel: MapViewModel // <-- here
    
    var body: some View {
            Map(coordinateRegion: $viewModel.region, showsUserLocation: true)
                .ignoresSafeArea()
                .accentColor(Color(.systemPink))
        .onAppear {
            print("\n----> in onAppear \n")
            viewModel.checkIfLocationServiceIsEnabled()
        }
    }
}

final class MapViewModel : NSObject, ObservableObject, CLLocationManagerDelegate {
    
    @Published var region = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: 33, longitude: -120), span: MKCoordinateSpan(latitudeDelta: 0.5, longitudeDelta: 0.5))
    
    func checkIfLocationServiceIsEnabled() {
        print("\n----> in checkIfLocationServiceIsEnabled")
    }
}

If that does not work with your system, try this:

struct MapView: View {
    @EnvironmentObject var viewModel: MapViewModel
    
    var body: some View {
        VStack {  // <-- here
            Map(coordinateRegion: $viewModel.region, showsUserLocation: true)
                .ignoresSafeArea()
                .accentColor(Color(.systemPink))
        }
        .onAppear {
            print("\n----> in onAppear \n")
            viewModel.checkIfLocationServiceIsEnabled()
        }
    }
}
  • Related