Как использовать структуру WeatherKit Swift для получения информации о погоде внутри расширения виджета?

Я не могу получить данные о погоде с помощью фреймворка WeatherKit внутри расширения виджета.

Вот проект, который я создал только для этого примера. Этот виджет показывает влажность места образца.

Вот код:

      import WidgetKit
import SwiftUI
import WeatherKit
import CoreLocation

struct Provider: TimelineProvider {
    func placeholder(in context: Context) -> SimpleEntry {
        SimpleEntry(date: Date(), humidity: 9.99) // placeholder humidity
    }

    func getSnapshot(in context: Context, completion: @escaping (SimpleEntry) -> ()) {
        let entry = SimpleEntry(date: Date(), humidity: 9.99) // placeholder humidity
        completion(entry)
    }

    func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
        Task {
            let nextUpdate = Date().addingTimeInterval(3600) // 1 hour in seconds
            
            let sampleLocation = CLLocation(latitude: 51.5072, longitude: 0.1276) // sample location (London)
            let weather = try await WeatherService.shared.weather(for: sampleLocation)
            let entry = SimpleEntry(date: .now, humidity: weather.currentWeather.humidity)
            
            let timeline = Timeline(entries: [entry], policy: .after(nextUpdate))
            completion(timeline)
        }
    }
}

struct SimpleEntry: TimelineEntry {
    let date: Date
    let humidity: Double
}

struct WidgetWeatherTestWidgetEntryView : View {
    var entry: Provider.Entry

    var body: some View {
        Text("\(entry.humidity)")
    }
}

struct WidgetWeatherTestWidget: 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([.systemMedium])
    }
}

В симуляторе работает нормально:

&amp;lt;img alt="введите описание изображения здесь" src="https://i.stack.imgur.com/6vAfB.jpg"/&amp;gt;

Но это не работает в реальном устройстве. Как только я позвонюWeatherServiceхweather(for: CLLocation) async throws -> Weatherвиджет "заблокирован".

&amp;lt;img alt="заблокированный виджет" src="https://i.stack.imgur.com/bxJOy.jpg"/&amp;gt;

Я попытался переместитьtry await WeatherService.shared.weather(for: sampleLocation)в разные места, но ничего не получалось. Как только я закомментирую эту строку (и создам запись с заполнителем влажности), все заработает.

Я добавил возможность WeatherKit в цель основного приложения и в цель WidgetExtension. Я также добавил WeatherKit «Возможности» и «Служба приложений» в «Сертификаты, идентификаторы и профили» веб-сайта Apple Developer.

Фреймворк WeatherKit отлично работает в других частях основного приложения.

Я еще не пробовал использовать REST API WeatherKit.

Любые идеи, что может происходить?

1 ответ

Вы должны установить свою запись для использованияResult, таким образом вы можете визуализировать любые возникающие ошибки.

      struct SimpleEntry: TimelineEntry {
    let date: Date
    let configuration: ConfigurationIntent
    let result: Result<any CurrentWeatherProtocol, Error>
}

Затем в представлении вы можете либо представить ошибку, либо представление погоды.

      struct PWCWidgetEntryView : View {
    var entry: Provider.Entry

    var body: some View {
        Text(entry.date, style: .time)
        switch entry.result{
        case .success(let weather):
            Image(systemName: weather.symbolName)
        case .failure(let error):
         
            Text(error.localizedDescription)

            let nserror = error as NSError
            Text(nserror.userInfo.description)

            Text("\(error)")
        }
    }
}

Вся моя кодовая база является частью гораздо более сложного проекта, но это то, чтоgetTimelineвыглядит как.

      func getTimeline(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
    Task{
        let now = Date.now
        let inHr = Calendar.current.date(byAdding: .hour, value: 1, to: now)!
        do{
            
            let location = try await locSvc.requestSingleLocation()
            let wSvc = try await CDWeatherService().currentWeather(for: WeatherEntry(location: .init(location: location)))
            //Return a successful result
            let entry = SimpleEntry(date: now, configuration: configuration, result: .success(wSvc))
            let timeline = Timeline(entries: [entry], policy: .after(inHr))
            completion(timeline)
        }catch{
            //Return a failure result
            let entry = SimpleEntry(date: now, configuration: configuration, result: .failure(error))
            let timeline = Timeline(entries: [entry], policy: .after(inHr))
            completion(timeline)
        }
    }
}

2 вещи, которые, как я обнаружил, являются моими проблемами, — это расширения виджетов для macOS и iOS, вы должны убедиться, что выбрали правильный.

Кроме того, убедитесь, что вы добавили «WeatherKit» в «Подписание и возможности» расширения виджета.

WeatherDaemon.WDSJWTAuthenticatorServiceListener.Errors ошибка 2.

Было связано с тем, что Weather Kit не был включен в «Службы приложений» в AppStore Connect, он должен быть включен в App Store Connect — Идентификатор виджета — Возможности

App Store Connect — идентификатор виджета — AppServices

Подписание и возможности в Xcode для приложения и/или виджета

Другие вопросы по тегам