0

I try to implement a simple pattern on Widgets:

  • get a textField in the app;
  • have the Widget updated when textField is changed.

App is UIKit (not SwiftUI). I've read here that I could pass it through UserDefaults,

How to pass uiviewcontroller data to swiftui widget class

and also through a shared singleton. I tried but couldn't make it work.

What I tried:

  • Create a singleton to hold the data to pass:
    class Util {
        
        class var shared : Util {
            struct Singleton {
                static let instance = Util()
            }
            return Singleton.instance;
        }
        
        var globalToPass = "Hello"
    }
  • shared the file between the 2 targets App and WidgetExtension
  • In VC, update the singleton when textField is changed and ask for widget to reload timeline
    @IBAction func updateMessage(_ sender: UITextField) {
       
      Util.shared.globalToPass = valueToPassLabel.text ?? "--"
      WidgetCenter.shared.reloadTimelines(ofKind: "WidgetForTest")
      WidgetCenter.shared.reloadAllTimelines()
    }

Problem : Widget never updated its message field

Here is the full widget code at this time:

import WidgetKit
import SwiftUI

struct LoadStatusProvider: TimelineProvider {
    
    func placeholder(in context: Context) -> SimpleEntry {
        
        SimpleEntry(date: Date(), loadEntry: 0, message: Util.shared.globalToPass)
    }

    func getSnapshot(in context: Context, completion: @escaping (SimpleEntry) -> ()) {
        
        let entry = SimpleEntry(date: Date(), loadEntry: 0, message: Util.shared.globalToPass)
        completion(entry)
    }

    func getTimeline(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 minuteOffset in 0 ..< 2 {
            let entryDate = Calendar.current.date(byAdding: .minute, value: 5*minuteOffset, to: currentDate)!
            let entry = SimpleEntry(date: entryDate, loadEntry: minuteOffset, message: Util.shared.globalToPass)
            entries.append(entry)
        }
        let timeline = Timeline(entries: entries, policy: .atEnd)
        completion(timeline)
    }
}

struct SimpleEntry: TimelineEntry {
    let date: Date
    let loadEntry: Int
    let message: String
}

struct WidgetForTestNoIntentEntryView : View {
    var entry: LoadStatusProvider.Entry
    var body: some View {
        let formatter = DateFormatter()
        formatter.timeStyle = .medium
        let dateString = formatter.string(from: entry.date)
        return
            VStack {
                Text(String(entry.message))
                HStack {
                    Text("Started")
                    Text(entry.date, style: .time)
                }
                HStack {
                    Text("Now")
                    Text(dateString)
                }
                HStack {
                    Text("Loaded")
                    Text(String(entry.loadEntry))
                }
            }
    }
}
@main
struct WidgetForTestNoIntent: Widget {
    let kind: String = "WidgetForTestNoIntent"
    var body: some WidgetConfiguration {
        StaticConfiguration(kind: kind, provider: LoadStatusProvider()) { entry in
            WidgetForTestNoIntentEntryView(entry: entry)
        }
        .configurationDisplayName("My Widget")
        .description("This is an example widget.")
    }
}

struct WidgetForTestNoIntent_Previews: PreviewProvider {
    static var previews: some View {
        
        WidgetForTestNoIntentEntryView(entry: SimpleEntry(date: Date(), loadEntry: 0, message: "-"))
            .previewContext(WidgetPreviewContext(family: .systemSmall))
    }
}
pawello2222
  • 46,897
  • 22
  • 145
  • 209
claude31
  • 874
  • 6
  • 8
  • Does this answer your question? [Share data between main App and Widget in SwiftUI for iOS 14](https://stackoverflow.com/questions/63922032/share-data-between-main-app-and-widget-in-swiftui-for-ios-14) – pawello2222 Dec 31 '20 at 18:13
  • Thanks for the link. It provide some interesting information. But that is mainly the solution with UserDefaults. I am looking for a solution either with singleton or with EnvironmentObject (but need to use it in UIViewController class as well). I have tried a modified version using EnvironmentObject, but now the widget just displays a black screen… Worse and worse. – claude31 Dec 31 '20 at 22:27
  • I have found a new link to explore: https://stackoverflow.com/questions/63976424/how-to-refresh-widget-data?rq=1 – claude31 Dec 31 '20 at 22:32
  • Widgets are *separated* from the main app. You can't communicate with extensions with singletons or other *in-app* solutions. You need another *storage*. Either UserDefaults, files, Core Data... Think of it like extensions are another processes. – pawello2222 Jan 01 '21 at 00:17

0 Answers0