Skip to content

Add App Store automatic update check via iTunes Lookup API - #42

Open
stanchevskid wants to merge 3 commits into
mainfrom
feature/app-store-update-check
Open

Add App Store automatic update check via iTunes Lookup API#42
stanchevskid wants to merge 3 commits into
mainfrom
feature/app-store-update-check

Conversation

@stanchevskid

Copy link
Copy Markdown

Implement checkForUpdatesFromAppStore extension on PrinceOfVersions that queries the iTunes Lookup API for the latest app version. Supports phased release tracking, notification frequency (once/always), configurable network timeout, and optional bundle ID override.

Summary

Add a new checkForUpdatesFromAppStore extension function on PrinceOfVersions that enables zero-config update checks against the Apple App Store via the iTunes Lookup API. This complements the existing URL-based configuration check with a native App Store lookup path for iOS.

Related issue: None

Changes

Type

  • Feature: This pull request introduces a new feature.
  • Bug fix: This pull request fixes a bug.
  • Refactor: This pull request refactors existing code.
  • Documentation: This pull request updates documentation.
  • Other: This pull request makes other changes.

Additional information

  • This pull request introduces a breaking change.

Description

Library (princeofversions/src/iosMain)

  • IosPrinceOfVersions.kt — Added checkForUpdatesFromAppStore() extension function with support for:
    • Phased release tracking (skips versions still in 7-day rollout)
    • Notification frequency (ONCE suppresses repeat notifications for the same version, ALWAYS always reports)
    • Configurable network timeout
    • Optional bundle ID override (defaults to NSBundle.mainBundle.bundleIdentifier)
  • AppStoreResponseParser.kt — Parses the iTunes Lookup API JSON response, extracting version and currentVersionReleaseDate. Returns null when resultCount is 0.
  • AppStorePhasedReleaseChecker.kt — Determines whether a version is still within its 7-day phased rollout window based on the release date.
  • IosAppStoreStorage.ktStorage implementation backed by NSUserDefaults for persisting the last notified App Store version.

Checklist

  • I have performed a self-review of my own code.
  • I have tested my changes, including edge cases.
  • I have added necessary tests for the changes introduced (if applicable).
  • I have updated the documentation to reflect my changes (if applicable).

Additional notes

App Store updates are never mandatory, so the result status will only ever be UpdateStatus.OPTIONAL or UpdateStatus.NO_UPDATE.

Implement checkForUpdatesFromAppStore extension on PrinceOfVersions that
queries the iTunes Lookup API for the latest app version. Supports phased
release tracking, notification frequency (once/always), configurable
network timeout, and optional bundle ID override.
@stanchevskid stanchevskid self-assigned this Apr 3, 2026
Extract JSON deserialization and result extraction into private helper
methods to reduce the number of throw statements in parse().

Copilot AI 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.

Pull request overview

This pull request adds a new checkForUpdatesFromAppStore extension function to enable zero-config update checks against the Apple App Store via the iTunes Lookup API for iOS. The implementation provides support for phased release tracking, configurable notification frequency (ONCE/ALWAYS), custom network timeouts, and optional bundle ID override.

Changes:

  • Adds checkForUpdatesFromAppStore() extension function with phased release tracking and notification frequency controls
  • Implements iTunes Lookup API response parsing via AppStoreResponseParser
  • Adds phased release window detection via AppStorePhasedReleaseChecker
  • Adds NSUserDefaults-backed storage for App Store version tracking via IosAppStoreStorage

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
IosPrinceOfVersions.kt Adds main extension function with App Store check logic and notification frequency handling
AppStoreResponseParser.kt Parses iTunes Lookup API JSON responses to extract version and release date
AppStorePhasedReleaseChecker.kt Determines if a version is still within its 7-day phased rollout period
IosAppStoreStorage.kt Implements Storage interface using NSUserDefaults for persisting last notified version

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Cover AppStoreResponseParser (10 tests), AppStorePhasedReleaseChecker
(6 tests), and IosAppStoreStorage (3 tests) with scenarios for valid
inputs, edge cases, and error conditions.
@ItsBruno

ItsBruno commented Apr 8, 2026

Copy link
Copy Markdown
Contributor

I assume that this functionality is present in the original iOS prince of versions and is missing here?

My question is whether adding this is urgent?

My worry is that from the perspective of a KMP app, having this functionality only in the iOS platform module diverges the API of the platform modules and makes using the lib more convoluted. Now a KMP user for the iOS module can query the App Store, but for the Android/Jvm module needs to host a config json.

If the platform module APIs diverge enough, from the lib users perspective, the benefit of using a single lib which has a similar API on all modules compared to different libs per module with different api's disappears.

I synced with @antunflas and we came to the conclusion that we should likely plan adding support for this behaviour in a separate, stand alone module, which covers all iOS/Android stores and perhaps jvm if we find something that makes sense there. The implementation specifics of this are not clear yet and some time would need to be set aside to investigate and plan this. But if adding this behaviour to the iOS module is urgent, we can merge this now and plan the module implementation variant and a major, breaking release later.

Additionally, it would be good to document this new behaviour in the readme as well.

Also, @KCeh, I suggest adding codeowners and automatic assignment to PR reviews so that changes to the lib get reviewed by all parties.

CancellationException::class,
)
public suspend fun PrinceOfVersions.checkForUpdatesFromAppStore(
trackPhaseRelease: Boolean = false,

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.

The default for trackPhaseRelease here is false, but the native lib defaults it to true.

)
public suspend fun PrinceOfVersions.checkForUpdatesFromAppStore(
trackPhaseRelease: Boolean = false,
notificationFrequency: NotificationType = NotificationType.ONCE,

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.

The default for notificationFrequency is ONCE here vs .always in the native lib.

ConfigurationException::class,
CancellationException::class,
)
public suspend fun PrinceOfVersions.checkForUpdatesFromAppStore(

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.

Can you check the country: String? parameter that the native lib exposes is missing here.

val appStoreInfo = AppStoreResponseParser.parse(jsonResponse)

if (appStoreInfo == null) {
BaseUpdateResult(

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.

The native lib returns AppStoreUpdateResult, which exposes updateVersion, updateState, phaseReleaseInProgress, releaseDate, installedVersion, and lastVersionAvailable. Here we collapse everything down to BaseUpdateResult(version, status). The biggest practical loss is phaseReleaseInProgress, when trackPhaseRelease=true and the version is mid-rollout, the caller currently can't tell "in phased rollout, will be available soon" apart from "truly no update available." Native consumers use that to drive UI ("update coming soon" vs nothing). Suggest introducing a dedicated AppStoreUpdateResult type for the iOS surface that mirrors the native fields.

@Filip2Stojanovski

Copy link
Copy Markdown
Contributor

I assume that this functionality is present in the original iOS prince of versions and is missing here?

My question is whether adding this is urgent?

My worry is that from the perspective of a KMP app, having this functionality only in the iOS platform module diverges the API of the platform modules and makes using the lib more convoluted. Now a KMP user for the iOS module can query the App Store, but for the Android/Jvm module needs to host a config json.

If the platform module APIs diverge enough, from the lib users perspective, the benefit of using a single lib which has a similar API on all modules compared to different libs per module with different api's disappears.

I synced with @antunflas and we came to the conclusion that we should likely plan adding support for this behaviour in a separate, stand alone module, which covers all iOS/Android stores and perhaps jvm if we find something that makes sense there. The implementation specifics of this are not clear yet and some time would need to be set aside to investigate and plan this. But if adding this behaviour to the iOS module is urgent, we can merge this now and plan the module implementation variant and a major, breaking release later.

Additionally, it would be good to document this new behaviour in the readme as well.

Also, @KCeh, I suggest adding codeowners and automatic assignment to PR reviews so that changes to the lib get reviewed by all parties.

The intent here is to keep the KMP iOS surface 1:1 with ios-prince-of-versions so that consumers migrating from the native lib find the same API shape. That's why this lives only in iosMain — it mirrors the iOS-only checkForUpdateFromAppStore and isn't meant to be a cross-platform store-check abstraction. Agreed that a unified iOS+Android store module is the right long-term design and worth a separate, major release. This isn't urgent, but I'd rather not block the iOS parity piece on the Android side catching up — can we treat this PR as the iOS parity piece and track the unified module as a follow-up? @stanchevskid will add README docs and a note in the PR body calling out the planned cross-platform variant.

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.

4 participants