-1

In short, I have a function that is called by a CLLocationManager and takes a while to update things on the server. In the UI a function fetch() can be triggered. This should run after the update() has finished. Thus, if there is no update() running, right away. Further, this fetch() should only run if it was actually triggered by the Button in the view.

(After having asked this question it turned out I was not specific enough, to the point where the question and its answer did actually not solve my issue.)

Here is a MRE:

import SwiftUI
import CoreLocation

class ExampleManager: ObservableObject {
    func fetch() {
        print("these would be some results")
    }
}

struct ContentView2: View {
    
    @StateObject var locationManager = LocationManager()
    @StateObject var example = ExampleManager()
    
    var body: some View {
        Button {
            example.fetch()
        } label: {
            Text("fetch")
        }
    }
}

class LocationManager: NSObject, ObservableObject, CLLocationManagerDelegate {
    
    // .. necessary stuff of CLLocationManager
    
    private func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) async {
        guard let newLocation: CLLocation = locations.first else { return }
        await update(newLocation: newLocation)
    }
    
    func update(newLocation: CLLocation) async {
        print("1")
        try! await Task.sleep(nanoseconds: 10_000_000_000)
        print("2")
    }
}
D. Kee
  • 169
  • 14

1 Answers1

0

You probably don't want to show the button unless there is something to fetch to begin with. I have a couple of scenarios here:

import UIKit
import SwiftUI
import CoreLocation

var greeting = "Hello, playground"

class ExampleManager: ObservableObject {
    func fetch() {
        
    }
}

struct ContentView: View {
    @StateObject var locationManager = LocationManager()
    @StateObject var example = ExampleManager()
    
    var body: some View {
//        ProgressView()
//            .onReceive(locationManager.$updated) { updated in
//                example.fetch()
//            }
        if locationManager.updated {
            Button {
                example.fetch()
            } label: {
                Text("fetch")
            }
        } else {
            EmptyView()
        }
    }
}

class LocationManager: NSObject, ObservableObject, CLLocationManagerDelegate {
    @Published var updated = false
    
    private func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) async {
        guard let newLocation: CLLocation = locations.first else { return }
        await update(newLocation: newLocation)
    }
    
    func update(newLocation: CLLocation) async {
        print("1")
        try! await Task.sleep(nanoseconds: 10_000_000_000)
        print("2")
        updated = true
    }
}
cora
  • 1,916
  • 1
  • 10
  • 18
  • This would only solve the issue for the first time of running the `update()` function. However, it should **always** wait for any current `update()` to finish. – D. Kee Dec 25 '22 at 09:05
  • 1
    @D.Kee You can reset updated to false after each fetch. – cora Dec 25 '22 at 12:52