vishal dharankar
vishal dharankar

Reputation: 7746

Where to add observers in SwiftUI widget

I am trying to build a better widget, I want the widget to be refreshed every time the battery state or level changes. I know for both need notification is to be added. So my logic is when the notification about battery state changes I want to refresh the timeline.

However I am not able to understand where to put the observer under TimelineProvider or Widget view?

struct timeWidgetEntryView : View {
    var entry: Provider.Entry
    let NC = NotificationCenter.default
    
    init(entry:Provider.Entry) {
        self.entry = entry

    }

    func batteryLevelDidChange(_ notification: Notification) {
        UIDevice.current.isBatteryMonitoringEnabled = true
        var level = UIDevice.current.batteryLevel
        level = level * 100
        WidgetCenter.shared.reloadAllTimelines()
        NSLog("Level changed / state changed")
    }
    
    var body: some View {
        VStack {
            Text(entry.date, style: .time).foregroundColor(.black).frame(minWidth: /*@START_MENU_TOKEN@*/0/*@END_MENU_TOKEN@*/, idealWidth: 100, maxWidth: 100, minHeight: /*@START_MENU_TOKEN@*/0/*@END_MENU_TOKEN@*/, idealHeight: /*@START_MENU_TOKEN@*/100/*@END_MENU_TOKEN@*/, maxHeight: 50, alignment: /*@START_MENU_TOKEN@*/.center/*@END_MENU_TOKEN@*/)
            Text("Battery : \(entry.batteryState)").foregroundColor(.black).frame(minWidth: /*@START_MENU_TOKEN@*/0/*@END_MENU_TOKEN@*/, idealWidth: /*@START_MENU_TOKEN@*/100/*@END_MENU_TOKEN@*/, maxWidth: /*@START_MENU_TOKEN@*/.infinity/*@END_MENU_TOKEN@*/, minHeight: /*@START_MENU_TOKEN@*/0/*@END_MENU_TOKEN@*/, idealHeight: /*@START_MENU_TOKEN@*/100/*@END_MENU_TOKEN@*/, maxHeight: 50, alignment: /*@START_MENU_TOKEN@*/.center/*@END_MENU_TOKEN@*/)
        }
        .cornerRadius(10)
        .frame(width:.infinity, height:.infinity, alignment: .center)
        .onReceive(NotificationCenter.default.publisher(for: UIDevice.batteryStateDidChangeNotification))
               { obj in
                  // Change key as per your "userInfo"
                  if let userInfo = obj.userInfo, let info = userInfo["info"] {
                     print(info)
                  }
            
            WidgetCenter.shared.reloadAllTimelines()
        }
    }
}

But it's not working as expected, so my question is should I move the observer to timeline provider? If yes, where?

Upvotes: 3

Views: 1381

Answers (1)

pawello2222
pawello2222

Reputation: 54576

You need to listen to the notifications in the main App instead - as explained in Apple tutorials:

Then, when you receive the UIDevice.batteryStateDidChangeNotification, just call:

WidgetCenter.shared.reloadAllTimelines()

To keep getting notifications when your app is not visible, you may need to enable background notifications. See this thread:

Upvotes: 5

Related Questions