Внедрение WeatherKit в виджет — SwiftUI
Итак, у меня есть виджет для приложения погоды (приложение в настоящее время не использует WeatherKit, но в конечном итоге я хотел бы его переместить), но я хочу заранее реализовать виджет с помощью WeatherKit. Я сделал тестовое приложение, используя WeatherKit, и все компоненты, которые я хотел бы использовать в виджете, работали правильно.
Вот все основное приложение, которое отлично работает:
import SwiftUI
import CoreLocation
import WeatherKit
var userCity: String = ""
class LocationManager: NSObject, ObservableObject {
@Published var currentLocation: CLLocation?
@Published var userCity: String = ""
private let locationManager = CLLocationManager()
override init() {
super.init()
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.distanceFilter = kCLDistanceFilterNone
locationManager.requestAlwaysAuthorization()
locationManager.startUpdatingLocation()
locationManager.delegate = self
}
}
extension LocationManager: CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let location = locations.last, currentLocation == nil else { return }
DispatchQueue.main.async {
self.currentLocation = location
}
CLGeocoder().reverseGeocodeLocation(location, completionHandler: {(placemarks, error) -> Void in
print(location)
guard error == nil else {
print("Reverse geocoder failed with error" + error!.localizedDescription)
return
}
guard placemarks!.count > 0 else {
print("Problem with the data received from geocoder")
return
}
let pm = placemarks![0].locality
print(pm!)
let userLocation = (pm!)
print(userLocation)
DispatchQueue.main.async {
self.userCity = userLocation
}
})
}
}
struct ContentView: View {
let weatherService = WeatherService.shared
@StateObject private var locationManager = LocationManager()
@State private var weather: Weather?
var body: some View {
VStack {
if let weather{
let celsiusWeather = weather.currentWeather.temperature.converted(to: .celsius)
let celsiusFormatted = String(format: "%.0f", celsiusWeather.value)
let celsiusFinal = "\(celsiusFormatted)°C"
let fahrenheitWeather = weather.currentWeather.temperature.converted(to: .fahrenheit)
let fahrenheitFormatted = String(format: "%.0f", fahrenheitWeather.value)
let fahrenheitFinal = "\(fahrenheitFormatted)°F"
VStack{
Image(systemName: weather.currentWeather.symbolName)
.resizable()
.symbolRenderingMode(.palette)
.scaledToFit()
.frame(width: 80, height: 80)
Text(locationManager.userCity)
.font(.largeTitle)
Text(fahrenheitFinal)
Text(celsiusFinal)
}
}
}
.padding()
.task(id: locationManager.currentLocation) {
do{
if let location = locationManager.currentLocation{
self.weather = try await weatherService.weather(for: location)
print(weather!)
}
}catch{
print(error)
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
По сути, я просто пытался переместить этот код в виджет, не зная, будет ли он работать правильно (очевидно, что что-то не так).
И вот что я пытался сделать с виджетом, но он пуст, поэтому что-то не работает и не может получить данные о погоде. Но Xcode не дает мне никаких ошибок.
import WidgetKit
import SwiftUI
import WeatherKit
import CoreLocation
var userCity: String?
class LocationManager: NSObject, ObservableObject {
@Published var currentLocation: CLLocation?
@Published var userCity: String = ""
private let locationManager = CLLocationManager()
override init() {
super.init()
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.distanceFilter = kCLDistanceFilterNone
locationManager.requestAlwaysAuthorization()
locationManager.startUpdatingLocation()
locationManager.delegate = self
}
}
extension LocationManager: CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let location = locations.last, currentLocation == nil else { return }
DispatchQueue.main.async {
self.currentLocation = location
}
CLGeocoder().reverseGeocodeLocation(location, completionHandler: {(placemarks, error) -> Void in
print(location)
guard error == nil else {
print("Reverse geocoder failed with error" + error!.localizedDescription)
return
}
guard placemarks!.count > 0 else {
print("Problem with the data received from geocoder")
return
}
let pm = placemarks![0].locality
print(pm!)
let userLocation = (pm!)
print(userLocation)
DispatchQueue.main.async {
self.userCity = userLocation
}
})
}
}
struct Provider: TimelineProvider {
func placeholder(in context: Context) -> SimpleEntry {
SimpleEntry(date: Date(), city: "", temp: "0") // placeholder humidity
}
func getSnapshot(in context: Context, completion: @escaping (SimpleEntry) -> ()) {
let entry = SimpleEntry(date: Date(), city: "", temp: "0") // placeholder humidity
completion(entry)
}
func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
let locationManager = LocationManager()
@State var currentWeather: Weather?
let nextUpdate = Date().addingTimeInterval(1800)
Task {
do{
if let location = locationManager.currentLocation{
let currentWeather = try await WeatherService.shared.weather(for: location)
print(currentWeather)
}
}catch{
print(error)
}
}
let entry = SimpleEntry(date: .now, city: userCity!, temp: (currentWeather?.currentWeather.temperature.description)!)
let timeline = Timeline(entries: [entry], policy: .after(nextUpdate))
completion(timeline)
}
}
struct SimpleEntry: TimelineEntry {
let date: Date
let city: String
let temp: String
}
struct WidgetWeatherTestWidgetEntryView : View {
var entry: Provider.Entry
var body: some View {
VStack{
Text(entry.city)
Text(entry.temp)
}
}
}
struct ShitsdagWidget: Widget {
let kind: String = "WidgetWeatherTest"
var body: some WidgetConfiguration {
StaticConfiguration(kind: kind, provider: Provider()) { entry in
WidgetWeatherTestWidgetEntryView(entry: entry)
}
.configurationDisplayName("Widget Weather Test")
.description("Testing weatherkit in a Widget")
.supportedFamilies([.systemSmall])
}
}
Поскольку виджеты настроены немного иначе, чем просто SwiftUI с временной шкалой и всем остальным, у меня могут быть вещи в неправильном месте, которые будут запускаться в правильном порядке. Любая помощь будет здорово.