WidgetKit iOS 17 : Interactive widget no longer responsive after a long idle time

I have developed an interactive widget which looks as the following

It is using CoreData.

The view is implemented as the following

struct widget_extensionEntryView : View {
    var body: some View {
        if let nsTodoList = nsTodoList {
            VStack(alignment: .leading, spacing: 1) {
                ...
                ForEach(0..<prefixGoodNSTodoArray.count, id: \.self) { index in
                    ...
                    let todoIntent = TodoIntent(objectIDAsString: objectIDAsString, parentOrder: parentOrder)
                    
                    Button(intent: todoIntent) {
                    }
                }
            }
        }
    }
}

The AppIntent is implemented as the following

import Foundation
import AppIntents
import WidgetKit
import CoreData

struct TodoIntent: AppIntent {
    static var title: LocalizedStringResource = "Complete Task"
    static var description: IntentDescription = IntentDescription("Complete selected task")
    
    @Parameter(title: "objectIDAsString")
    var objectIDAsString: String
    
    @Parameter(title: "parentOrder")
    var parentOrder: Int
    
    init() { }
    
    init(objectIDAsString: String, parentOrder: Int) {
        self.objectIDAsString = objectIDAsString
        self.parentOrder = parentOrder
    }
    
    func perform() async throws -> some IntentResult {
        guard let objectID = NSManagedObjectID.from(objectIDAsString) else { return .result() }
        
        guard let nsTodoList = NSTodoListRepository.INSTANCE.getNSTodoList(objectID) else { return .result() }
        
        nsTodoList.toggleChecked(context: CoreDataStack.INSTANCE.viewContext, parentOrder: Int64(parentOrder))
        
        RepositoryUtils.saveContextIfPossible(CoreDataStack.INSTANCE.viewContext)
        
        TodoWidgetOptions.isWrittenByWidgetExtension = true
        
        // Refresh all home widgets.
        // TODO: https://developer.apple.com/forums/thread/721937
        WidgetCenter.shared.reloadAllTimelines()
        
        return .result()
    }
}

The interactive widget works pretty well.

However, tapping on the widget has no response in the following situations:

  1. After an overnight, we turn on the iPhone's screen and tap on the widget.
  2. After a few hours of idle time, we turn on the iPhone's screen and tap on the widget.

One of the steps below will make the widget workable again:

  1. Launch and close the main app again. The main app will call WidgetCenter.shared.reloadAllTimelines() during sceneDidEnterBackground.
  2. Press and hold on the widget, choose 'Edit widget', and select the desired Todo list.

One thing to take note of is that I am using IntentTimelineProvider instead of AppIntentTimelineProvider. The reason I am using 'older tech' is due to the limitation mentioned in https://developer.apple.com/forums/thread/741053

However, I am not sure whether this is the root cause of the problem.

Does anyone have any idea why such a problem occurs?

Thanks.

Post not yet marked as solved Up vote post of yccheok Down vote post of yccheok
655 views

Replies

This is the video recording to show interactive widget is not working after an 8 hours overnight.

This is the video recording to show interactive widget is working again, after launching and closing the main app. The main app however will call WidgetCenter.shared.reloadAllTimelines(). Hence, I am not sure whether main app relaunching is causing widget to work again, or WidgetCenter.shared.reloadAllTimelines() is causing widget to work again.

I am using iOS version 17.1.1, iPhone SE to perform testing.

We manage to have a temporary workaround, by changing "never refresh" timeline

func getTimeline(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
    var entries: [SimpleEntry] = []
    
    let currentDate = Date()
    
    let nsTodoList = NSTodoListRepository.INSTANCE.getNSTodoList(configuration)
    
    let entry = SimpleEntry(
        date: currentDate,
        nsTodoList: nsTodoList,
        configurationIntent: configuration
    )
    
    entries.append(entry)

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

to "refresh every 1 hour"

func getTimeline(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
    var entries: [SimpleEntry] = []
    
    let currentDate = Date()
    
    let nextUpdateDate = Calendar.current.date(byAdding: .hour, value: 1, to: currentDate)!
    
    let nsTodoList = NSTodoListRepository.INSTANCE.getNSTodoList(configuration)
    
    let entry = SimpleEntry(
        date: currentDate,
        nsTodoList: nsTodoList,
        configurationIntent: configuration
    )
    
    entries.append(entry)

    // Provide data entry snapshot for "now". This getTimeline function will be triggered again after 1 hour.
    let timeline = Timeline(entries: entries, policy: .after(nextUpdateDate))
    completion(timeline)
}

However, this seems like a bug to me because an interactive widget is supposed to function properly even if it isn't refreshed for an extended period.

Does anyone know where I can report this issue to Apple? Thank you.