skip to Main Content

In my main view MainView() I load weather data from WeatherKit asynchronously via .task() which runs whenever the user’s location changes:

.task(id: locationManager.currentLocation){

      if let location = locationManager.currentLocation{
           weather = try await weatherService.weather(for: location)
      }
}

However, the user’s location may not change for a long period of time or even never and I would like for the weather data to be at least updated once every hour.

How would I update WeatherKit data every hour AND update every single time their location changes.

Obviously I can’t rely on the WeatherKit weather object within the ‘.task(id:)’ to update as that is the object that I’d need to update.

2

Answers


  1. for fetching location after every few mins what approach I have used is

    class BackgroundLocationManager: NSObject, ObservableObject, CLLocationManagerDelegate {
        
        static let shared = BackgroundLocationManager()
        private var locationManager: CLLocationManager!
        private var timer: DispatchSourceTimer?
        private var counter: Int = 0
        @Published var location: LocationModel? = nil
        
        private override init() {
            super.init()
            locationManager = CLLocationManager()
            locationManager.delegate = self
            locationManager.desiredAccuracy = kCLLocationAccuracyBest
            locationManager.distanceFilter = kCLDistanceFilterNone
            locationManager.allowsBackgroundLocationUpdates = true
            locationManager.requestAlwaysAuthorization()
        }
        
        
        private func startTimer(delay: Int = 15) {
            let queue = DispatchQueue(label: "com.example.timer", qos: .background)
            timer = DispatchSource.makeTimerSource(queue: queue)
            timer?.schedule(deadline: .now(), repeating: .seconds(delay))
            timer?.setEventHandler { [weak self] in
                    self?.locationManager.startUpdatingLocation()
                self?.counter = 0
            }
            timer?.resume()
        }
        
        func fetchLocation(interval: Int? = nil) {
            if let interval = interval {
                startTimer(delay: interval)
            } else {
                self.locationManager.startUpdatingLocation()
            }
            
        }
        
        
        func stopFetchingLocation() {
            timer?.cancel()
            timer = nil
        }
        
        
        
        
    }
    
    extension BackgroundLocationManager {
        
        func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
            switch status {
            case .notDetermined:
                break
            case .restricted, .denied:
              // do something when permission is denied
            case .authorizedAlways, .authorizedWhenInUse, .authorized:
             // do something when permission is given
                break
            @unknown default:
                return
            }
        }
        
        func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
            if let error = error as? CLError, error.code == .denied {
                // do something when unable to fetch location
                return
            }
        }
        
        func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
            self.counter += 1
            locationManager.stopUpdatingLocation()
            let currentLocation = locations.last?.coordinate
            let long = String(currentLocation?.longitude ?? 0)
            let lat = String(currentLocation?.latitude ?? 0)
            let verticalAcc = String(locations.last?.verticalAccuracy ?? 0)
            let horizontalAcc = String(locations.last?.horizontalAccuracy ?? 0)
            let altitude = String(locations.last?.altitude ?? 0)
            let location = LocationModel(longitude: long, latitude: lat,
                                         verticalAccuracy: verticalAcc,
                                         horizontalAccuracy: horizontalAcc, alitude: altitude)
            if counter <= 1 {
                self.location = location
            }
        }
        
    }
    
    

    To use it according to my need

    struct MainView: View {
        @StateObject var vm = MainViewModel()
    
        init() {
            BackgroundLocationManager.shared.fetchLocation() // when needed once
            BackgroundLocationManager.shared.fetchLocation(interval: 15) // needed after how many seconds
        }
    
        var body: some View {
            ZStack {
              MyCustomeView()
            }
            .onReceive(BackgroundLocationManager.shared.$location) { location in
                vm.doSomething(location: location)
            }
        }
    }
    
    Login or Signup to reply.
  2. Use a timer inside your view.

    struct WeatherView: View {
        
        let weatherService: WeatherService
        let location: Location
        @State var weather: Weather?
        let timer = Timer.publish(every: 3600, on: .main, in: .common).autoconnect()
        
        var body: some View {
            Text(weather.description)
                .onReceive(timer) { _ in
                    fetch()
                }
        }
        
        func fetch() {
            Task {
                weather = try await weatherService.weather(for: location)
            }
        }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search