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.
Post
Replies
Boosts
Views
Activity
I had write a widget after iOS 17+, It had a Toggle to perform a appintent .
When switch the toggle, the appintent will perfrom , just like
`
func perfrom() async throws -> some IntentResult {
// first
let first = try await getFristValue()
let second = try await getSecondValue(by: first)
let third = try awiat getThirdValue(by: second)
return .result()
}
`
and I found, it will work when I am debugging connect with Xcode.
But, when I don't connect xcode, it will not work.
How can I fixed it ?