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?
With iOS 14 and later, you can configure your widgets. For example, you can edit the Weather widget to see the forecast for your current location or a different location. Here's how: Touch and hold a widget to open the quick actions menu.
The My Location widget allows the network to detect your physical location and zoom the map to it. The location can be highlighted if necessary. The widget takes advantage of HTML geolocation. When the app runs on desktops, it uses the browser on the network to detect the location.
Tap On The Widget Sometimes all you need to do to update the information display on a widget is to tap on it and open the app. When the app opens, its content will refresh, which should refresh the widget too.
Open Settings on your iPhone or iPod touch with iOS 14+ or your iPad with iPadOS 14+. Choose “Privacy” in the root list. Tap “Location Services” at the very top of the list. Tap a listed app that has a widget, such as Weather. Now choose between the available options to manage widget location access.
One to coolest new features in iOS 14 is widgets, which allow you to view important information from critical apps at a glance wherever you want from your iPhone Home Screen. The widgets come in different sizes and, at least for now, are available exclusively for native apps. Here’s how to use them on the iPhone.
Before iOS and iPadOS 14 came along, a widget would simply inherit location permissions from its container app. For instance, setting location access for the Weather app to “Always” in iOS 13 would automatically give the same location permission to the accompanying widget.
Now, when you run the code above on your device, you’ll get the following revamped prompt in iOS 14: Screenshot from the author’s phone. By toggling the “Precise” button, you can choose to allow approximate or accurate location access. Now, there could be a use case where you are required to only access a user’s accurate location.
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)
.......
})
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With