Skip to content

Bypass updater local and CDN HTTP cache on check for updates#891

Closed
MineraleYT wants to merge 8 commits into
TypeWhisper:mainfrom
MineraleYT:fix/sparkle-updater-cache-bypass
Closed

Bypass updater local and CDN HTTP cache on check for updates#891
MineraleYT wants to merge 8 commits into
TypeWhisper:mainfrom
MineraleYT:fix/sparkle-updater-cache-bypass

Conversation

@MineraleYT

@MineraleYT MineraleYT commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR bypasses local client-side and CDN edge caching when checking for application updates via the Sparkle framework.

Problem

When newly compiled releases are pushed to the server, checking for updates manually inside the app can sometimes fail to detect them for up to a few hours. This delay occurs because macOS URLSession cache or intermediate CDN servers (like GitHub Releases or Cloudflare) serve cached feed XML documents.

Solution

  • Implemented SPUUpdaterDelegate's updater(_:feedURLRequest:) delegate method inside AppDelegate.
  • Customized the Appcast XML request configuration to use .reloadIgnoringLocalCacheData.
  • Added standard HTTP caching control headers (Cache-Control: no-cache and Pragma: no-cache). This guarantees that manual checks always request fresh version metadata directly from the source server.

Test Plan

  • Ran scripts/pr-preflight.sh
  • Built and ran locally
  • Tested the changed functionality manually (Verified delegate API conformity and successful local compilation of the application target)
  • No regressions in existing features

Summary by CodeRabbit

  • Bug Fixes
    • Improved the app’s update feed requests to avoid cached results by adding a dynamic “nocache” query parameter tied to the current time, ensuring update information stays current across runs.

@MineraleYT MineraleYT requested a review from SeoFood as a code owner July 14, 2026 16:50
@coderabbitai

coderabbitai Bot commented Jul 14, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 66119eaa-cd36-4c1d-b2be-a73453ab83b8

📥 Commits

Reviewing files that changed from the base of the PR and between d69c6a2 and 31a760e.

⛔ Files ignored due to path filters (1)
  • TypeWhisper.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved is excluded by !**/Package.resolved
📒 Files selected for processing (1)
  • TypeWhisper/App/TypeWhisperApp.swift

📝 Walkthrough

Walkthrough

The Sparkle updater delegate now supplies a timestamp-based nocache query parameter for feed requests, while leaving the parameter’s display fields empty.

Changes

Sparkle feed refresh

Layer / File(s) Summary
Force-refresh feed requests
TypeWhisper/App/TypeWhisperApp.swift
Adds feedParameters(for:sendingSystemProfile:), returning the current UNIX timestamp as the nocache parameter value.

Estimated code review effort: 1 (Trivial) | ~2 minutes

Suggested reviewers: seofood

Poem

A rabbit whispers, “Fetch anew,”
With time-stamped hops in every queue.
No stale feed can hide today,
Fresh bytes bounce along the way.
Squeak hooray!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly matches the main change: bypassing updater HTTP cache during update checks.
Description check ✅ Passed The description follows the required template and includes a complete summary and test plan with checked items.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@SeoFood SeoFood left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requesting changes because the added delegate method is not invoked by the pinned Sparkle version.

Comment thread TypeWhisper/App/TypeWhisperApp.swift Outdated
AppConstants.effectiveUpdateChannel.sparkleChannels
}

nonisolated func updater(_ updater: SPUUpdater, feedURLRequest originalRequest: URLRequest) -> URLRequest {

@SeoFood SeoFood Jul 14, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method is never called. Sparkle 2.8.1 does not declare updater(_:feedURLRequest:) in SPUUpdaterDelegate, and the framework contains no corresponding call site. Swift allows this extra method, so the build and CI still pass even though the update requests remain unchanged.

Sparkle already applies .reloadIgnoringLocalCacheData internally to appcast requests. If custom headers are required, please use a supported API such as SPUUpdater.httpHeaders or address the caching behavior at the feed host, then verify the actual outgoing request. As written, this PR does not change manual or automatic update checks.

@SeoFood SeoFood mentioned this pull request Jul 14, 2026

@SeoFood SeoFood left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for replacing the unsupported delegate hook. The callback is valid now, but the end-to-end cache behavior still prevents this from solving the reported issue. The Sparkle dependency update and its Swift 6 actor migration are being handled separately in #893 / #894.

AppConstants.effectiveUpdateChannel.sparkleChannels
}

nonisolated func feedParameters(for updater: SPUUpdater, sendingSystemProfile: Bool) -> [[String : String]] {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is now a real Sparkle delegate callback, but it still does not bypass the CDN in front of the actual feed. I sent three requests to https://typewhisper.github.io/typewhisper-mac/appcast.xml with different UUID values for nocache; GitHub Pages returned the same cached object for all three (x-cache: HIT with an identical x-github-request-id). The query string is therefore not part of the effective cache key for this feed, so this changes every updater URL without making the appcast fresh.

Please remove this cache-buster and solve freshness at the hosting/release layer, or move the feed to an endpoint whose cache behavior can be controlled. The fix needs an end-to-end check that can distinguish stale and newly published appcast content.

@SeoFood

SeoFood commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

The new release-build has now failed on the four Sparkle 2.9 MainActor warnings in UpdateChecker.swift, matching the local verification. #894 contains the dependency bump together with the required actor migration and closes #893.

Please drop the Package.resolved bump from this PR and rebase after #894. That keeps this PR focused on the appcast freshness problem, whose current query-parameter approach still does not bypass the GitHub Pages cache.

@SeoFood

SeoFood commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Closing this PR because the current timestamp query parameter does not bypass the effective GitHub Pages CDN cache, so it does not solve the appcast freshness problem end to end.

The Sparkle 2.9.4 update and required MainActor migration have landed separately in #894. The remaining hosting and release-layer cache work is now tracked in #895.

Thank you for iterating on the original implementation.

@SeoFood SeoFood closed this Jul 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants