How To Notify Users About App Updates

Whenever you release an app update, it's important to make sure existing users don't fall behind and that your user base doesn't become fragmented across multiple versions. So, some apps will choose to notify users when a newer version is available or will force them to upgrade if they fall too far behind.

Most implementations of "forced updates" require a custom endpoint that specifies the minimum supported version. That is out of scope for this article, so we'll instead focus on a purely native approach for detecting and notifying users about available app updates.

We'll use the iTunes Connect API since we can easily retrieve all of the metadata for the current version of our app by calling this endpoint with our app's bundleID:

URL(string: "http://itunes.apple.com/lookup?bundleId=\(bundleID)")

Let's create some models to help us manage the response and a quick extension on Bundle to help us grab the app's current version number:

// MARK: - AppStoreResponse
struct AppStoreResponse: Codable {
    let resultCount: Int
    let results: [Result]
}

// MARK: - Result
struct Result: Codable {
    let releaseNotes: String
    let releaseDate: String
    let version: String
}

private extension Bundle {
    var releaseVersionNumber: String? {
        infoDictionary?["CFBundleShortVersionString"] as? String
    }
}
I've reduced the Codable models to just the relevant fields needed for my use case. Feel free to add the other properties (if needed).

Implementation

struct AppStoreUpdateChecker {
    static func isNewVersionAvailable() async -> Bool {
        guard let bundleID = Bundle.main.bundleIdentifier, 
                let currentVersionNumber = Bundle.main.releaseVersionNumber, 
                let url = URL(string: "http://itunes.apple.com/lookup?bundleId=\(bundleID)") else {                
            // Invalid inputs
            return false
        }

        do {
            let (data, _) = try await URLSession.shared.data(from: url)
            let appStoreResponse = try JSONDecoder().decode(AppStoreResponse.self, from: data)

            guard let latestVersionNumber = appStoreResponse.results.first?.version else {
                // No app with matching bundleID found
                return false
            }

            return currentVersionNumber != latestVersionNumber
        }
        catch {
            // TODO: Handle error
            return false
        }
    }
}

// Usage:
Task {
    if await AppStoreUpdateChecker.isNewVersionAvailable() {
        print("New version of app is availabe. Showing blocking alert!")
    }
}

Oftentimes, other implementations of this functionality will introduce complicated logic for comparing version numbers against one another. However, since App Store Connect requires that all version numbers are strictly increasing, it should be sufficient to just check for a mismatch in version numbers.


If you're interested in articles about iOS Development & Swift, check out my YouTube channel or follow me on Twitter.

If you're an indie iOS developer, make sure to check out my weekly newsletter featuring the best indie iOS apps:

Indie Watch
Indie Watch is an exclusive weekly hand-curated newsletter showcasing the best iOS, macOS, watchOS, and tvOS apps from developers worldwide.

Feel free to submit your own apps for consideration!


Do you have an iOS Interview coming up?

Check out my book Ace The iOS Interview!

Subscribe to Digital Bunker

Don’t miss out on the latest issues. Sign up now to get access to the library of members-only issues.
[email protected]
Subscribe