Reputation: 5635
Has anyone tried to update user's location in iOS 14 Widget?
After reading Apple Developer forums I've come up with the writing wrapper around CLLocationManager
and using it this way:
class WidgetLocationManager: NSObject, CLLocationManagerDelegate {
var locationManager: CLLocationManager? {
didSet {
self.locationManager!.delegate = self
}
}
private var handler: ((CLLocation) -> Void)?
func fetchLocation(handler: @escaping (CLLocation) -> Void) {
self.handler = handler
self.locationManager!.requestLocation()
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
self.handler!(locations.last!)
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print(error)
}
}
And using it this way:
var widgetLocationManager = WidgetLocationManager()
func getTimeline(for configuration: SelectPlaceIntent, in context: Context, completion: @escaping (Timeline<Entry>) -> Void) {
if widgetLocationManager.locationManager == nil {
widgetLocationManager.locationManager = CLLocationManager()
widgetLocationManager.locationManager!.requestWhenInUseAuthorization()
}
widgetLocationManager.fetchLocation(handler: { location in
print(location)
.......
})
}
I also have these 2 entries in Widget's info.plist
:
<key>NSLocationUsageDescription</key>
<string>1</string>
<key>NSWidgetWantsLocation</key>
<true/>
When locationManager.requestLocation() is being called, authorisation status is authorisedWhenInUse, but delegate's method is never being called. What am I missing?
Upvotes: 11
Views: 5017
Reputation: 11
Make sure you have both these plist values set in your widgets Info.plist
file:
<key>NSWidgetWantsLocation</key>
<true/>
<key>NSLocationUsageDescription</key>
<string>Put some text here</string>
Upvotes: 1
Reputation: 784
First of all, the obvious problem that I see:
<key>NSLocationUsageDescription</key>
<string>1</string>
NSLocationUsageDescription
is deprecated: Apple Documentation , so you should be using NSLocationWhenInUseUsageDescription
or NSLocationAlwaysAndWhenInUseUsageDescription
instead. Be sure to include the permission that you choose in main apps Info.plist
as well
Additionally, creating CLLocationManager
in
func getTimeline(for configuration: SelectPlaceIntent, in context: Context, completion: @escaping (Timeline<Entry>) -> Void) {
...
}
might be problematic, since it can get called from background thread, so I would refactor your WidgetLocationManager
like this:
class WidgetLocationManager: NSObject, CLLocationManagerDelegate {
var locationManager: CLLocationManager?
private var handler: ((CLLocation) -> Void)?
override init() {
super.init()
DispatchQueue.main.async {
self.locationManager = CLLocationManager()
self.locationManager!.delegate = self
if self.locationManager!.authorizationStatus == .notDetermined {
self.locationManager!.requestWhenInUseAuthorization()
}
}
}
func fetchLocation(handler: @escaping (CLLocation) -> Void) {
self.handler = handler
self.locationManager!.requestLocation()
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
self.handler!(locations.last!)
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print(error)
}
}
and later use it like this:
var widgetLocationManager = WidgetLocationManager()
func getTimeline(for configuration: SelectPlaceIntent, in context: Context, completion: @escaping (Timeline<Entry>) -> Void) {
widgetLocationManager.fetchLocation(handler: { location in
print(location)
.......
})
}
Upvotes: 10