get location in app intent for interactive widgets

I have an app intent for interactive widgets. when I touch the toggle, the app intent perform to request location.

func perform() async throws -> some IntentResult {
        
        var mark: LocationService.Placemark?
        do {
            mark = try await AsyncLocationServive.shared.requestLocation()
            print("[Widget] ConnectHandleIntent: \(mark)")
        } catch {
            WidgetHandler.shared.error = .locationUnauthorized
            print("[Widget] ConnectHandleIntent: \(WidgetHandler.shared.error!)")
            
        }
        return .result()
    }

@available(iOSApplicationExtension 13.0, *)
@available(iOS 13.0, *)
final class AsyncLocationServive: NSObject, CLLocationManagerDelegate {
    
    static let shared = AsyncLocationServive()
    
    private let manager: CLLocationManager
    private let locationSubject: PassthroughSubject<Result<LocationService.Placemark, LocationService.Error>, Never> = .init()
    
    lazy var geocoder: CLGeocoder = {
        let geocoder = CLGeocoder()
        return geocoder
    }()

    @available(iOSApplicationExtension 14.0, *)
    @available(iOS 14.0, *)
    var isAuthorizedForWidgetUpdates: Bool {
        manager.isAuthorizedForWidgetUpdates
    }

    override init() {
        manager = CLLocationManager()
        super.init()
        manager.delegate = self
    }
    
    func requestLocation() async throws -> LocationService.Placemark {
        let result: Result<LocationService.Placemark, LocationService.Error> = try await withUnsafeThrowingContinuation { continuation in
            var cancellable: AnyCancellable?
            var didReceiveValue = false
            cancellable = locationSubject.sink(
                receiveCompletion: { _ in
                    if !didReceiveValue {
                        // subject completed without a value…
                        continuation.resume(throwing: LocationService.Error.emptyLocations)
                    }
                },
                receiveValue: { value in
                    // Make sure we only send a value once!
                    guard !didReceiveValue else {
                        return
                    }
                    didReceiveValue = true
                    // Cancel current sink
                    cancellable?.cancel()
                    // We either got a location or an error
                    continuation.resume(returning: value)
                }
            )
            // Now that we monitor locationSubject, ask for the location
            DispatchQueue.global().async {
                self.manager.requestLocation()
            }
        }
        switch result {
        case .success(let location):
            // We got the location!
            return location
        case .failure(let failure):
            // We got an error :(
            throw failure
        }
    }
    
    
    // MARK: CLLocationManagerDelegate
    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        defer {
            manager.stopUpdatingLocation()
        }
        guard let location = locations.first else {
            locationSubject.send(.failure(.emptyLocations))
            return
        }
        debugPrint("[Location] location: \(location.coordinate)")
        manager.stopUpdatingLocation()
        if geocoder.isGeocoding {
            geocoder.cancelGeocode()
        }
        geocoder.reverseGeocodeLocation(location) { [weak self] marks, error in
            guard let mark = marks?.last else {
                self?.locationSubject.send(.failure(.emptyMarks))
                return
            }
            
            debugPrint("[Location] mark: \(mark.description)")
            self?.locationSubject.send(.success(mark))
        }
        
    }

    func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
        locationSubject.send(.failure(.errorMsg(error.localizedDescription)))
    }
}

I found it had not any response when the app intent performed. And there is a location logo tips in the top left corner of phone screen.

I print manager.isAuthorizedForWidgetUpdates , it always true. But I call requestLocation or startUpdatingLocation, it will never get any location

If you are calling the Intent from your Widget it should have access to the location permission. You can learn about giving location access your Widgets here.

get location in app intent for interactive widgets
 
 
Q