Hey I created a widget where I get the user location and with that coordinate I call an API request to get a near location on my getTimeline.
First Issue: Sometimes when the timeline refresh automatically (after the 1h 30m) the request is not done and the widget refresh with an empty state, even that the data have been save on UserDefaults(suiteName: ) in the widget request and in the app.
@available(iOS 17.0, *)
struct NearLocationWidget: Widget {
let kind: String = "{name}"
var body: some WidgetConfiguration {
StaticConfiguration(kind: kind, provider: Provider()) { entry in
NearLocationWidgetEntryView(entry: entry)
.invalidatableContent()
}
.supportedFamilies([.systemSmall])
}
}
@available(iOS 17.0, *)
struct Provider: Provider {
private let locationManager: WidgetLocationManager = .init()
func getTimeline(in context: Context, completion: @escaping (Timeline<WidgetEntry>) -> ()) {
self.locationManager.fetchLocation { coordinate in
Task {
// Prepare entry
let currentDate = Date()
// Get saved values
let savedCoordinate: CLLocationCoordinate2D? = UserDefaultsStorage.shared.getObjectFromGroup(for: .widgetCoordinates)
let savedNearLocation: LocationObject? = UserDefaultsStorage.shared.getObjectFromGroup(for: .widgetNearLocationData)
// Start first entry
var entry: WidgetEntry = .init(date: currentDate, isLogIn: true, isLocationEnabled: self.locationManager.isAuthorizedForWidgetUpdates, nearestLocation: savedNearLocation)
// If coordinates are not empty, request the nearest location
if let coordinate = coordinate ?? savedCoordinate {
let nearLocation = await NetworkService.shared.getNearestLocation(coordinate: coordinate)
entry.nearestLocation = nearLocation
}
// Next fetch happens in 1h and 30min later
let nextUpdate = Calendar.current.date(byAdding: DateComponents(hour: 1, minute: 30), to: currentDate)!
let timeline = Timeline(entries: [entry], policy: .after(nextUpdate))
completion(timeline)
}
}
}
}
Second Issue: My widget have a button to refresh the widget (See below), which Intent just return the .result() to produce the timeline refresh, but it has the same issue that above, sometimes it works properly, but when is been a while since the app is closed or in background I just have an empty state on my widget.
@available(iOS 17, *)
struct RefreshWidgetIntent: AppIntent {
static var title: LocalizedStringResource = "Widget"
static var description = IntentDescription("Refresh Widget")
func perform() async throws -> some IntentResult {
return .result()
}
}
@available(iOS 17.0, *)
struct RefreshIconView: View {
var body: some View {
Button(intent: RefreshWidgetIntent()) {
Image(systemName: "arrow.clockwise")
.resizable()
.aspectRatio(contentMode: .fit)
.foregroundStyle(Color.Inverted.surface)
.frame(height: 20)
.padding(12)
.background(Color.Surface.cutout)
.clipShape(Circle())
}
.buttonStyle(PlainButtonStyle())
.frame(width: 44, height: 44)
}
}
I have all the location permissions need it on the info plist. Tested on Simulator and Physical Device (Most on physical device: iPhone 13, iOS 17.3.1).
- Is that the best way to reload a widget?
- May that be the issue of why is not reloading properly?