Thursday, July 28, 2022
HomeiOS developmentswift - The best way to reload an iOS widget from a...

swift – The best way to reload an iOS widget from a perform within the mum or dad app?


I created a widget for my app that’s meant to be refreshed day-after-day after midnight, in addition to when a go to (AKA name) is began, ended, or canceled. The midnight replace is working effective, however the reload triggered by the perform calls within the mum or dad app just isn’t resulting in any change within the UI, regardless of WidgetCenter.shared.reloadTimelines(ofKind: "Name") being known as once I set a breakpoint within the debugger.

Options I’ve tried that have not labored:

  • Calling WidgetCenter.shared.reloadAllTimelines() as an alternative of specifying widget variety to ensure all bases are coated when reloading
  • Ready with the app within the foreground for a couple of minutes after triggering a change to make sure that the widget has time to refresh
  • Calling reloadTimelines(ofKind:) from strategies within the AppDelegate (this truly works just about immediately, however is not a viable answer as a result of it might shortly run via the day by day reload finances)
  • Transferring the decision to reloadTimelines(ofKind:) elsewhere within the code

Right here is my code for getTimeline:

    func getTimeline(in context: Context,
                     completion: @escaping (Timeline<Entry>) -> Void) {
        let currentDate = Date()
        let midnight = Calendar.present.startOfDay(for: currentDate)
        let nextMidnight = Calendar.present.date(byAdding: .day, worth: 1, to: midnight)!
        let entry: Entry

        entry = Entry(context: WidgetEnvironment.shared
            .coreDataManager.viewContext, date: .now)

        let timeline = Timeline(entries: [entry], coverage: .after(nextMidnight))
        completion(timeline)
    }

Right here is the struct for the widget:

@principal
struct CallWidget: Widget {
    let variety: String = "Name"
    let coreDataManager = WidgetEnvironment.shared
        .coreDataManager

    var physique: some WidgetConfiguration {
        StaticConfiguration(variety: variety, supplier: Supplier()) { entry in
            CallWidgetEntryView(entry: entry)
        }
        .configurationDisplayName("Name")
        .description("Present or subsequent name")
        .supportedFamilies([.systemMedium])
    }
}

Right here is the initializer for my ViewModel, which I take advantage of as an entry by passing within the context and date and creating the variables I would like for the widget UI:

extension CallWidgetEntryView {
    class CallWidgetViewModel: TimelineEntry {
        personal var context: NSManagedObjectContext
        var date: Date
        var callStatus: CallStatus
        var callStatusLabel = Labels.getLabelFor(label: Labels.noCallScheduled)
        var storeName = ""
        var handle = ""
        var addressLine2 = ""
        var callStartTime = Date.now
        var backgroundColor = Coloration("grey")
        var modelTime = ""
        var lastCallDate = Date.now

        init(context: NSManagedObjectContext, date: Date) {
            self.context = context
            self.date = date

            if let name = Name.inProgress(context: WidgetEnvironment.shared
                .coreDataManager.viewContext) {
                callStatus = .currentCall
                callStatusLabel = Labels.getLabelFor(label: Labels.callInProgress)
                storeName = name.account?.title ?? ""
                handle = name.account?.handle ?? ""
                addressLine2 = name.account?.addressLine2 ?? ""
                callStartTime = name.actualStartDate ?? .now
                modelTime = ""
                lastCallDate = .now
                backgroundColor = Coloration("inexperienced")
            } else if let account = Account.fetchWithNextCall(on: .now, in: WidgetEnvironment.shared
                .coreDataManager.viewContext) {
                callStatus = .nextCall
                callStatusLabel = Labels.getLabelFor(label: Labels.nextScheduledCall)
                storeName = account.title ?? ""
                handle = account.handle ?? ""
                addressLine2 = account.addressLine2
                callStartTime = .now
                modelTime = DateComponentsFormatter.modelCallTimeFormatter
                    .string(from: DateComponents(minute: Int(account.modelTime))) ?? ""
                lastCallDate = account.dateOfLastCompletedStoreCall ?? .now
                backgroundColor = Coloration("blue")
            } else /* No extra calls at present */ {
                callStatus = .noCall
                callStatusLabel = Labels.getLabelFor(label: Labels.noCallScheduled)
                backgroundColor = Coloration("grey")
            }
        }
        // ...
    }
}

And listed below are the perform calls (every from a separate file):
Begin Go to

    func startCall(scheduled: Bool,
                   location: CLLocation? = nil,
                   locationExceptionReason: String? = nil) {
        let context = AppEnvironment.shared.mesh.viewContext
        // ...
        context.performAndWait {
            // ...
            attempt? context.save()
        }
        // ...
        WidgetCenter.shared.reloadTimelines(ofKind: "Name")
        // ...
    }

Finish Go to

    func endCall(_ name: Name,
                      location: CLLocation? = nil,
                      locationExceptionReason: String? = nil) {
        guard let context = name.managedObjectContext else { return }
        context.performAndWait {
            // ...
            attempt? context.save()
            _ = attempt? ResetHasChangesProcessor.resetHasChanges(context: context)
        }

        // ...
        
        WidgetCenter.shared.reloadTimelines(ofKind: "Name")

        let mesh = AppEnvironment.shared.mesh
        DDLogInfo("Performing sync")
        mesh.carry out {
            FullSyncTask(changesOnly: true,
                         mesh: mesh)
        }
    }

Cancel Go to

    func cancelCall() {
        defer { navigationController?.popViewController(animated: true) }
        guard let context = name?.managedObjectContext else { return }
        context.performAndWait { [weak self] in
            // ...
            self?.name?.cancel()
            attempt? context.save()
            _ = attempt? ResetHasChangesProcessor.resetHasChanges(context: context)
        }
        WidgetCenter.shared.reloadTimelines(ofKind: "Name")
    }

I’m questioning if possibly the widget would not have time to react to the up to date context earlier than I reload it, however I do not know methods to go about testing or resolving this.

Any and all assist could be enormously appreciated.

RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

- Advertisment -
Google search engine

Most Popular

Recent Comments