in Projects

NiceFeed: Updating all subscriptions at once (And, picking up Python)

Per my earlier post, I had a nice break last week from working on NiceFeed while my internet was unusable. However, the plan to stay away from my computer turned out to be a miserable failure, and I decided instead to learn Python. I used Python Projects for Beginners: A Ten-Week Bootcamp Approach to Python Programming by Connor Milliken except instead instead of 10 weeks, I gave myself one. Of course, the real test is what to do with it now—I’d been planning to learn Django and pick up some backend skills, but the chapter on introductory data analytics really got my wheels spinning… We’ll see.

Anyway. A NiceFeed user emailed me about possibly adding an option to update all subscriptions at once. Up to this point, the closest thing I had was a background worker (via WorkManager) that cycles through all existing subscriptions but only updates one subscription every 15 minutes. If it finds any new content, it posts a notification displaying the most recent entry found. Needless to say, if a user has many, many subscriptions, it would take a long time for a full cycle to complete.

We want the app to be able to update all subscriptions in one move. This means quickly looping through every one of them, each time requesting the subscription URL, identifying any new content, and saving it to the database. To do this, I created a new CoroutineWorker class that does the following:


private val repo = NiceFeedRepository.get()
private val feedParser = FeedParser(repo.networkMonitor)

override suspend fun doWork(): Result {
    val feedUrls = repo.getFeedUrlsSynchronously()
    if (feedUrls.isEmpty()) return Result.success()

    for (url in feedUrls) {
        val currentEntryIds: List<String> = repo.getEntryIdsByFeedSynchronously(url)
        val feedWithEntries: FeedWithEntries? = feedParser.getFeedSynchronously(url)

        feedWithEntries?.entries?.let { entries ->
            val newEntries = entries.filterNot { currentEntryIds.contains(it.url) }
            val entryIds = entries.map { it.url }
            val oldEntryIds = currentEntryIds.filterNot { entryIds.contains(it) }
            repo.handleBackgroundUpdate(url, newEntries, oldEntryIds)
        }
    }

    return Result.success()
}

From there, it’s simply a matter of feeding it to WorkManager, which I’ve set to run the worker once a day. I’ve also added the option (via Snackbar message) to run the worker whenever new subscriptions are imported by OPML, since OPML only imports minimal feed data, without any entries. Now we are that much closer to a standard feature of most respectable RSS readers. I find that I enjoy “background” code like this much more than designing and creating the user interface… Hence, I’m still undecided as to whether I should also add a button to allow the option of running the worker whenever the user wants, rather than just on schedule. Thoughts for another day.

Write a Comment

Comment