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:
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!