Bagaimana cara menyegarkan data Widget?

Sep 20 2020

Kode Saya mengambil dua JSONvariabel dan harus menampilkannya di Widget saya. Widget tetap kosong. Tanpa widget itu menunjukkan kepada saya semuanya benar dalam aplikasi saya.

Apa yang saya lakukan salah? API dalam kode hanya untuk pengujian sehingga Anda juga dapat memeriksanya. Apakah ada sesuatu yang perlu saya ubah untuk menampilkannya di widget?

Struktur Saya:

import Foundation

struct Results: Decodable {
    let data: [Post]
}

struct Post: Decodable, Identifiable {
    let id: String
    var objectID: String {
        return id
    }
    let home_name: String
    let away_name: String
}

Mengambil JSON:

import Foundation

class NetworkManager: ObservableObject {
    
    @Published var posts = [Post]()
    
    @Published var test = ""
    @Published var test2 = ""
    
    func fetchData() {
        if let url = URL(string: "https://livescore-api.com/api-client/teams/matches.json?number=10&team_id=19&key=I2zBIRH3S01Kf0At&secret=6kLvfRivnqeNKUzsW84F0LISMJC1KdvQ&number=7&team_id=46") {
            let session = URLSession(configuration: .default)
            let task = session.dataTask(with: url) { (gettingInfo, response, error) in
                if error == nil {
                    let decoder = JSONDecoder()
                    if let safeData = gettingInfo {
                        do {
                            let results = try decoder.decode(Results.self, from: safeData)
                            DispatchQueue.main.async {
                                self.posts = results.data
                                self.test = results.data[0].away_name
                                self.test2 = results.data[0].home_name
                            }
                        } catch {
                            print(error)
                        }
                    }
                }
            }
            task.resume()
        }
    }
}

Menampilkan Widget:

import WidgetKit
import SwiftUI
import Intents

struct Provider: IntentTimelineProvider {
    func placeholder(in context: Context) -> SimpleEntry {
        SimpleEntry(date: Date(), configuration: ConfigurationIntent())
    }

    func getSnapshot(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (SimpleEntry) -> ()) {
        let entry = SimpleEntry(date: Date(), configuration: configuration)
        completion(entry)
    }

    func getTimeline(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
        var entries: [SimpleEntry] = []

        // Generate a timeline consisting of five entries an hour apart, starting from the current date.
        let currentDate = Date()
        for hourOffset in 0 ..< 5 {
            let entryDate = Calendar.current.date(byAdding: .hour, value: hourOffset, to: currentDate)!
            let entry = SimpleEntry(date: entryDate, configuration: configuration)
            entries.append(entry)
        }

        let timeline = Timeline(entries: entries, policy: .atEnd)
        completion(timeline)
    }
}

struct SimpleEntry: TimelineEntry {
    let date: Date
    let configuration: ConfigurationIntent
}

struct WidgetNeuEntryView : View {
    
    @ObservedObject var networkManager = NetworkManager()
    var entry: Provider.Entry
    var body: some View {
        Text(networkManager.test)
    }
}

@main
struct WidgetNeu: Widget {
    let kind: String = "WidgetNeu"

    var body: some WidgetConfiguration {
        IntentConfiguration(kind: kind, intent: ConfigurationIntent.self, provider: Provider()) { entry in
            WidgetNeuEntryView(entry: entry)
        }
        .configurationDisplayName("My Widget")
        .description("This is an example widget.")
    }
}

struct WidgetNeu_Previews: PreviewProvider {
    static var previews: some View {
        WidgetNeuEntryView(entry: SimpleEntry(date: Date(), configuration: ConfigurationIntent()))
            .previewContext(WidgetPreviewContext(family: .systemSmall))
    }
}

networkManager.test harus ditampilkan sebagai teks tetapi seperti yang saya katakan itu kosong.

Jawaban

10 pawello2222 Sep 20 2020 at 10:17

Anda tidak dapat menggunakan ObservedObjectseperti yang biasa Anda gunakan di Aplikasi Anda.

Di Widget Anda menggunakan TimelineProvideryang membuat Entrytampilan untuk Anda.


  1. Tambahkan properti lain ke Anda TimelineEntry, sebut saja clubName:
struct SimpleEntry: TimelineEntry {
    let date: Date
    let clubName: String
}
  1. Perbarui NetworkManagerdan kembalikan hasil di completion:
class NetworkManager {
    func fetchData(completion: @escaping ([Post]) -> Void) {
        ...
        URLSession(configuration: .default).dataTask(with: url) { data, _, error in
            ...
            let result = try JSONDecoder().decode(Results.self, from: data)
            completion(result.data)
            ...
        }
        .resume()
    }
}
  1. Gunakan NetworkManagerdi TimelineProviderdan buat entri garis waktu ketika fetchDataselesai:
struct Provider: TimelineProvider {
    var networkManager = NetworkManager()

    func placeholder(in context: Context) -> SimpleEntry {
        SimpleEntry(date: Date(), clubName: "Club name")
    }

    func getSnapshot(in context: Context, completion: @escaping (SimpleEntry) -> Void) {
        let entry = SimpleEntry(date: Date(), clubName: "Club name")
        completion(entry)
    }

    func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> Void) {
        networkManager.fetchData { posts in
            let entries = [
                SimpleEntry(date: Date(), clubName: posts[0].home_name)
            ]
            let timeline = Timeline(entries: entries, policy: .never)
            completion(timeline)
        }
    }
}
  1. Gunakan entry.clubNamedi badan tampilan:
struct WidgetNeuEntryView: View {
    var entry: Provider.Entry

    var body: some View {
        VStack {
            Text(entry.date, style: .time)
            Text("Club: \(entry.clubName)")
        }
    }
}

Perhatikan bahwa dalam contoh di atas, kebijakan muat ulang disetel neveruntuk hanya memuat data sekali.

Anda dapat dengan mudah mengubahnya menjadi atEndatau after(date:)jika Anda ingin memuat ulang timeline secara otomatis.

Jika Anda perlu memuat ulang timeline secara manual kapan saja, Anda dapat menghubungi:

WidgetCenter.shared.reloadAllTimelines()

Ini akan berfungsi di Aplikasi dan Widget.


Berikut adalah repositori GitHub dengan berbagai contoh Widget termasuk Widget Jaringan.