swift 如何至少每小时运行一次.task()

nfg76nw0  于 2023-01-12  发布在  Swift
关注(0)|答案(2)|浏览(131)

在主视图MainView()中,我通过.task()异步加载来自WeatherKit的天气数据,.task()在用户位置改变时运行:

.task(id: locationManager.currentLocation){

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

然而,用户的位置可能在很长一段时间内不会改变,甚至永远不会改变,我希望天气数据至少每小时更新一次。
我如何每小时更新WeatherKit数据 * 和 * 每次他们的位置改变时更新。
显然,我不能依赖于'.task(id:)'中的WeatherKit天气对象来更新,因为这是我需要更新的对象。

qaxu7uf2

qaxu7uf21#

为了每隔几分钟获取一个位置,我使用的方法是

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

根据我的需要使用

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

pcww981p2#

在视图中使用计时器。

struct WeatherView: View {
    
    let timer = Timer.publish(every: 3600, on: .main, in: .common).autoconnect()
    
    var body: some View {
        Text("Weather")
            .onReceive(timer) { _ in
                fetch()
            }
    }
    
    func fetch() {
        Task {
            weather = try await weatherService.weather(for: location)
        }
    }
}

相关问题