iOS 14 Widget Never Refreshes

I have an existing Obj-C based app. One of its features is to show a UITableView of the last 10 entries from an RSS feed associated to the app.

I have created a Widget for this app to show whatever the most recent entry is on that RSS feed. I do this by parsing the RSS, adding that as the Timeline entry, and displaying it.

When you first install the app and add the widget, everything is good. It shows the last entry. However, if a new item is added to the RSS feed, it won't show up on the widget, no matter how long you wait. I can verify the item is there by installing fresh on a different device, and I'll see the new item, but the other device still shows the previous one. Here is how I am handling the timeline:

Code Block struct Provider: TimelineProvider {
    let textView = UILabel()
    @State private var rssItems:[RSSItem]?
    let feedParser = FeedParser()
    func placeholder(in context: Context) -> SimpleEntry {
        SimpleEntry(date: Date(), title:"News", description: "News article here", link: "Http://link", pubDate: "Today")
    }
    func getSnapshot(in context: Context, completion: @escaping (SimpleEntry) -> ()) {
        let entry = SimpleEntry(date: Date(), title:"News", description: "News Article Here", link: "Http://link", pubDate: "Today")
        completion(entry)
    }
    func getTimeline(in context: Context, completion: @escaping (Timeline<SimpleEntry>) -> ()) {
       // WidgetCenter.shared.reloadAllTimelines()
        var entries: [SimpleEntry] = []
        
        
        feedParser.parseFeed(url: "https://fritchcoc.wordpress.com/feed") {(rssItems) in
            self.rssItems = rssItems
            let currentDate = Date()
            let string1 = "Latest News: "
            let string2 = rssItems[0].title
           
            let dateString = rssItems[0].pubDate
            let dateFormatter = DateFormatter()
            dateFormatter.dateFormat = "EEE, d MMM yyyy HH:mm:ss zzz"
            dateFormatter.locale = Locale.init(identifier: "en_US")
            let dateObj = dateFormatter.date(from: dateString)
            dateFormatter.dateFormat = "MM-dd-yyyy"
            let newDateSTring =  (dateFormatter.string(from: dateObj!))
            let theURLTHING = URL(string: "game:///octo")!
            
                var appendString1 = string1+string2
                let entry = SimpleEntry(date: currentDate, title:appendString1, description: rssItems[0].description, link: rssItems[0].link, pubDate: newDateSTring)
                entries.append(entry)
            let refreshDate = Calendar.current.date(byAdding: .minute, value: 2, to: Date())!
            
            let timeline = Timeline(entries: [entry], policy: .after(refreshDate))
            completion(timeline)
        }
       
    }
}

Here is the Swift file I am using to parse. If you notice, I am even including a call to reloadAllWidgets along with calling the completion handler.

Code Block import Foundation
import WidgetKit
struct RSSItem {
    var title: String
    var link: String = ""
    var description: String
    var pubDate: String
}
class FeedParser: NSObject, XMLParserDelegate
{
    var rssItems: [RSSItem] = []
    private var currentElement = ""
    private var currentTitle: String = "" {
        didSet {
            currentTitle = currentTitle.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
        }
    }
    private var currentDescription: String = "" {
        didSet {
            currentDescription = currentDescription.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
        }
    }
    private var currentPubDate: String = "" {
        didSet {
            currentPubDate = currentPubDate.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
           
        }
    }
    private var currentLink: String = "" {
        didSet {
            currentLink = currentLink.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
        }
    }
    private var parserCompletionHandler: (([RSSItem]) -> Void)?
    func parseFeed(url: String, completionHandler: (([RSSItem]) -> Void)?)
    {
       // WidgetCenter.shared.reloadAllTimelines()
        self.parserCompletionHandler = completionHandler
        let request = URLRequest(url: URL(string: url)!)
        let urlSession = URLSession.shared
        let task = urlSession.dataTask(with: request) { (data, response, error) in
            guard let data = data else {
                if let error = error {
                    print(error.localizedDescription)
                }
                return
            }
            /// parse our xml data
            let parser = XMLParser(data: data)
            parser.delegate = self
            parser.parse()
        }
        task.resume()
    }
    // MARK: - XML Parser Delegate
    func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String] = [:])
    {        //WidgetCenter.shared.reloadAllTimelines()
        currentElement = elementName
        if currentElement == "item" {
            currentTitle = ""
            currentDescription = ""
            currentPubDate = ""
            currentLink = ""
        }
    }
    func parser(_ parser: XMLParser, foundCharacters string: String)
    {      //  WidgetCenter.shared.reloadAllTimelines()
        switch currentElement {
        case "title": currentTitle += string
        case "content:encoded" : currentDescription += string
        case "pubDate" : currentPubDate += string
        case "link" : currentLink += string
        default: break
        }
    }
    func parser(_ parser: XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?)
    {
       // WidgetCenter.shared.reloadAllTimelines()
      
        if elementName == "item" {
           
            let rssItem = RSSItem(title: currentTitle, link: currentLink, description: currentDescription, pubDate: currentPubDate)
            self.rssItems.append(rssItem)
        }
    }
    func parserDidEndDocument(_ parser: XMLParser) {
        WidgetCenter.shared.reloadAllTimelines()
        parserCompletionHandler?(rssItems)
        
    }
    func parser(_ parser: XMLParser, parseErrorOccurred parseError: Error)
    {
        print(parseError.localizedDescription)
    }
}


Replies

If you can file a Technical Support Incident we can take a deeper look into the issue. One thing you can try first is using a background URLSession for getting your data and in your Widget Configuration adding .onBackgroundURLSessionEvents to receive updates. You are not guaranteed to get a refresh every time you call .reloadAllWidgets, particularly if you call it often. RSS feeds can receive a lot of new data in a small amount of time so it is possible you aren't seeing reloads because the system is being asked to reload too often.
I created an incident.
AND WHY IN THE WORLD are we not guaranteed an update when reload is called? This is not supposed to be a mini-app, and is supposed to be available at a glance, updated with dynamic content, so why create a system that cannot be guaranteed to refresh data as needed????