I have CLLocationManager working inside a widget. I had to add these two values to the widgets info.plist file:
<key>NSLocationUsageDescription</key>
<string>1</string>
<key>NSWidgetWantsLocation</key>
<true/>
Post
Replies
Boosts
Views
Activity
I'm not sure of the best way to use CLLocationManager inside WidgetKit, it would be nice to get a sample from Apple around it showing exactly how we should do it.
To fix the main thread issue, what I did was create a wrapper class that I instantiate in the widget struct that will hold a reference to the CLLocationManager, then on the call to timeline() I create the CLLocationManager instance at that time and store a reference to it in the wrapper, this means it is created on the right thread. If you don't store a reference to this class that lives beyond the call to timeline() the callbacks are never called.
Then, instead of streaming updates from the CLLocationManager instance, I use CLLocationManager requestLocation() to get a one time callback.
Something like this:
LocationWrapper.swift
final class LocationWrapper {
var location: CLLocationManager?
	func fetchLocation(completion: @escaping (CLLocation) -> Void) {
		 // call requestLocation(), hook up to callbacks etc.
		 // call completion once you a
	}
}
widget_main.swift
struct Provider: TimelineProvider {
var locationWrapper = LocationWrapper()
public func timeline(with context: Context, completion: @escaping (Timeline<SimpleEntry>) -> ()) {
if locationManager.location == nil {
locationManager.location = CLLocationManager()
}
		locationManager.fetchLocation { location in
				// call completion, update ui etc.
		}
}
On the latest beta5 version, the behavior I see is that even if you give permission for the app to use location services, when you go to the widget gallery to select your widget it pops up another permission box asking if you want to allow/deny location services in the widget.
So looks like giving permission in the app does not automatically give permission to the widget.
However, this might be a bug, if I turn off permission for the widget in the settings -> privacy section, when calling locationManager.authorizationStatus property it will return I have permission but calling the location APIs just returns an error.
I'm not sure if this would work for your case but you could just add a field like "applyRedaction" to your entry object, set that to true in the case where your widget doesn't have permission, fill in the rest of the field with static data like you do in getSnapshot() and return the entry from getTimeline using the completion method as in the success case.
Then in your widget main entry struct where the view is returned you can just manually apply the redaction based on the value of the applyRedaction value e.g.
if entry.applyRedaction {
	return AnyView(Text("Hello").redacted(reason: .placeholder))
} else {
	return AnyView(Text("Hello"))
}
I use this pattern in my widget to just show a redacted view of my widget in the case that there are any transient network errors.
I have an app with WidgetKit running on iOS13 using xcode beta6. I was getting a crash as well, to fix it I added the WidgetKit.framework binary to my app target and marked it "optional" and now the app loads.
To make sure iOS13 code doesn't try to call WidgetCenter I just wrap the calls in:
if #available(iOS 14, *) {
WidgetCenter.shared.reloadAllTimelines()
}
You need to 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>
Then when you open the widget gallery, looks like from beta5 onwards iOS will show a prompt asking you to allow/deny location permissions to the widget.
The next important thing is that inside getTimeline() if you are creating an instance of CLLocationManager you must keep a reference to it past the end of the getTimeline call otherwise no updates will happen on the delegate. In my case I just created a wrapper class that the widget stores as a property then I setup the CLLocationManager instance on the first run of getTimeline and assign it to a field in the wrapper class.
At least this worked for me :)
If you set the supportedFamilies modifier on the WidgetConfiguration to [] then the widget no longer shows in the widget gallery. That might be an option.
e.g.
public var body: some WidgetConfiguration {
StaticConfiguration(
kind: kind,
provider: Provider()
) { entry in
widget_mainEntryView(entry: entry)
}
.configurationDisplayName("Foo")
.description("Foo")
.supportedFamilies([])
}
There is no guarantee that getSnapshot will be called each time the galley is opened, the OS might cache previous results as you've seen.
In the case the user has never logged in to the app and adds the widget, so their is no previous cached data in your getSnapshot call you could check the auth status of your app and if the user is logged out:
Show a "Logged out" UI in the widget
Show some canned data to fill out your widget with example data (to make it look good in the gallery).
Then once the user logs in via the app you can get the widget to refresh its UI by calling: https://developer.apple.com/documentation/widgetkit/widgetcenter/reloadalltimelines()
For the other case where you don't want cached data in the gallery once the user logs out, you could try calling reloadAllTimelines() when the user logs out in the app and see if that causes the snapshot to be updated.
Unfortunately, entering my real apple ID/password does not work. It show success but the upgrade/downgrade never occurs the UI still shows the original subscription level.
Signing out of the production AppleID and only having the sandbox tester on the device doesn't work either. In that scenario the password box is displayed but with an empty username in the dialog, entering the sandbox password just fails.
I'm confused how people are successfully testing these scenarios :/
Did you enable WeatherKit in the apps Signing & Capabilities section for the app?
The WeatherKit option is un the Capabilities tab not App Services. You also need to register a WeatherKit key in the top level Keys section in the developer site. Once you have completed both of those step, this question has information on how to call the REST API using a JWT: https://developer.apple.com/forums/thread/707418
Was this ever clarified by an Apple employee?
It doesn't seem like we should have to tick the diagnostics box based on the statement: "You are not responsible for disclosing data collected by Apple." for crash logs and diagnostics provided in the xcode interface, otherwise every app in the app store would have to have this option checked if it was required.
Great, thank you!
We were also having this issue. It was recommended to make sure that any images you have aren't too big, ideally somewhere around the size of the widget dimensions, that should help fix this problem. Reducing the size of the images we were using as backgrounds to our widgets seems to have helped us.
It doesn't seem like you can reduce the draw calls using multiple entities, but instead I used the new LowLevelMesh to render lots of geometry available in iOS18.