Tech Insights - App Store iOS

Force Update & show new App Version is Available

Navigating the Dynamics of Force Updates and Notifying Users about the Latest App Versions

Kanagasabapathy Rajkumar

--

Force Update App

To understand the distribution of a new version of our App that Apple has provided.

After you publish the app to the App Store, we might be required to upgrade the app and release a new version.

Importance of App Updates

Beyond the UI features, the updates play a crucial role in fortifying security, fixing bugs/issues, improvements and enhancing the overall user experience.

As developers, it is our responsibility to convey the importance of the updates in our code base.

Strategies for Non-Intrusive Notifications

In-app notifications and Push Notifications

In-App Notifications

func showInAppNotification(message: String) {
let alertController = UIAlertController(
title: "Update Available",
message: message,
preferredStyle: .alert
)

let updateAction = UIAlertAction(title: "Update", style: .default) { _ in
// Handle the action to open the App Store for the update
if let appStoreURL = URL(string: "https://apps.apple.com/in/app/paytm-money-stocks-mf-ipo/id1344431352") {
UIApplication.shared.open(appStoreURL, options: [:], completionHandler: nil)
}
}

alertController.addAction(updateAction)

// Present the alert
if let viewController = UIApplication.shared.keyWindow?.rootViewController {
viewController.present(alertController, animated: true, completion: nil)
}
}

// Example usage
let updateMessage = "A new version of the app is available. Update now for the latest features and improvements."
showInAppNotification(message: updateMessage)

Push Notifications

Firebase
Reference — https://stackoverflow.com/questions/17262511/how-do-ios-push-notifications-work

Usage

// Inside AppDelegate.swift
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any]) {
// Handle the received remote notification, e.g., show an in-app notification
if let updateMessage = userInfo["updateMessage"] as? String {
showInAppNotification(message: updateMessage)
}
}

Force-Update

Force updating the apps is typically reserved for critical situations where the security, functionality or integrity of the application is at severe risk.

Scenarios:

  1. Security Vulnerabilities — Discovered vulnerability that puts user data or privacy at risk. In rare cases during SSL pinning, the leaf node certificate has expired.
  2. Critical Bug Fix — The bug has affected the user’s performance due to some dependency or recent upgrade.
  3. Server-side changes — There are some changes in the backend system or domain changes.
  4. Deprecations of Old Versions — Developers should stay up to date on the deprecation details of certain APIs.

There are some considerations to keep in mind before asking the user for a force update.

User’s choice or consent, Potential disruption while performing some task, Limited Internet Access and Transparent communication.

Implementation

Please find the GitHub Repo:

I have integrated Swift Package — NetworkKit in my project. Please take a look

Before heading to implementation, we can use the iTunes lookup API endpoint, which gives us the latest app metadata for a given bundle identifier.

I am going to use the Apple Pages app in this example. https://itunes.apple.com/br/lookup?bundleId=361309726https://itunes.apple.com/br/lookup?bundleId=com.apple.Pages

{
"resultCount": 1,
"results": [
{
"isGameCenterEnabled": false,
"features": [
"iosUniversal"
],
"advisories": [],
"supportedDevices": [
"iPhone15-iPhone15",
"iPhone15Plus-iPhone15Plus",
"iPhone15Pro-iPhone15Pro",
"iPhone15ProMax-iPhone15ProMax"
],
"ipadScreenshotUrls": [],
"appletvScreenshotUrls": [],
"artworkUrl60": "https://is1-ssl.mzstatic.com/image/thumb/Purple116/v4/92/00/af/9200af99-97d8-707e-b569-ebd2b881bcfc/AppIcon-0-0-1x_U007emarketing-0-0-0-7-0-0-sRGB-0-0-0-85-220-0.png/60x60bb.jpg",
"artworkUrl512": "https://is1-ssl.mzstatic.com/image/thumb/Purple116/v4/92/00/af/9200af99-97d8-707e-b569-ebd2b881bcfc/AppIcon-0-0-1x_U007emarketing-0-0-0-7-0-0-sRGB-0-0-0-85-220-0.png/512x512bb.jpg",
"artworkUrl100": "https://is1-ssl.mzstatic.com/image/thumb/Purple116/v4/92/00/af/9200af99-97d8-707e-b569-ebd2b881bcfc/AppIcon-0-0-1x_U007emarketing-0-0-0-7-0-0-sRGB-0-0-0-85-220-0.png/100x100bb.jpg",
"artistViewUrl": "https://apps.apple.com/br/developer/apple/id284417353?mt=12&uo=4",
"screenshotUrls": [
"https://is1-ssl.mzstatic.com/image/thumb/PurpleSource112/v4/1a/5f/1f/1a5f1fd7-d42d-aa47-d157-2ff5f49bde2c/e5cecc21-d73a-441e-87c7-d8d0b3cd0bf1_9.png/392x696bb.png"
],
"kind": "software",
"currency": "BRL",
"releaseNotes": "• Traga uma nova dimensão aos seus documentos com objetos 3D em formato USDZ\n• Veja previsões de texto em linha enquanto você digita*\n• Comece a colaborar facilmente com outras pessoas em um documento durante uma ligação do FaceTime*\n• Encontre e abra documentos sugeridos ao buscar o Pages com o Spotlight*\n• Arraste documentos para o ícone do Pages na Tela de Início para abri-los ou importá-los*\n• Use o novo modelo Relatório Minimalista, com tipografia, cores e layout elegantes\n• Dê estilo a parágrafos com novas opções de borda e cores de fundo\n• Remova bordas externas de gráficos importados de arquivos do Microsoft Office\n\n* Requer iOS 17 ou iPadOS 17 e posteriores",
"artistId": 284417353,
"artistName": "Apple",
"genres": [
"Produtividade",
"Negócios"
],
"price": 0,
"description": "O Pages é o processador de texto mais bonito já visto em dispositivos móveis. Comece com um modelo feito pela Apple para criar relatórios, livros digitais, currículos, pôsteres e outros documentos incríveis. Ou use um documento em branco e crie o seu próprio design. Adicione imagens, filmes, áudio, tabelas, gráficos e formas facilmente. Você pode até usar o Apple Pencil (em dispositivos compatíveis) ou o dedo para desenhar e anotar. O Pages foi feito especialmente para iPad e iPhone.\n\nUse o Apple Pencil ou o dedo para desenhar e anotar\n• Adicione desenhos com caneta, lápis, giz de cera e ferramentas de preenchimento facilmente, e anime-os para vê-los ganhar vida\n• Use a Anotação Inteligente para adicionar comentários e marcações que se mantêm ancorados ao texto associado\n• Converta palavras escritas à mão em texto com o recurso Escrever à mão e o Apple Pencil\n\nColabore com outras pessoas ao mesmo tempo\n• Com a colaboração em tempo real, toda a equipe pode trabalhar simultaneamente em um documento no Mac, iPad, iPhone e até mesmo em um PC\n• Compartilhe um documento publicamente ou com pessoas específicas, veja quem está trabalhando no documento com você e visualize os cursores de outras pessoas para seguir suas edições\n• Veja uma lista das alterações recentes em documentos colaborativos, incluindo quando uma pessoa entra, comenta e faz alterações\n• Disponível para documentos armazenados no iCloud ou no Box\n\nCrie belos documentos\n• Use a Visualização de Tela no iPhone para mostrar automaticamente textos, imagens e outros elementos otimizados para ajustar à tela\n• Escolha entre mais de 90 modelos feitos pela Apple\n• Aprimore documentos com uma biblioteca com mais de 700 formas editáveis\n• Adicione facilmente imagens, vídeo e áudio\n• Adicione uma galeria de imagens para visualizar uma coleção de fotos na mesma página\n• Crie livros EPUB interativos que podem ser compartilhados com outras pessoas ou publicados no Apple Books para download ou compra\n• Importe e edite arquivos do Microsoft Word e de texto\n\nFerramentas avançadas\n• Use a visualização de índice para navegar facilmente pelo documento ou livro\n• Adicione comentários e participe de conversas encadeadas\n• Ative o controle de alterações para marcar um documento ao editá-lo\n• Adicione marcadores para ir facilmente de uma parte a outra do documento\n• Ative páginas lado a lado para formatar o documento como páginas duplas\n• Crie modelos de página para manter a consistência do design por todo o documento de layout de página\n• Crie notas de rodapé e finais, e visualize contagens de caracteres, palavras e parágrafos\n• Adicione equações matemáticas elegantes com notação LaTeX ou MathML\n• Use o modo de apresentação para ler facilmente e rolar pelo texto automaticamente ao discursar\n\niCloud\n• Ative o iCloud para disponibilizar seus documentos automaticamente no Mac, iPad, iPhone e a partir de um navegador em um Mac ou PC em iCloud.com\n• O Pages salva os documentos automaticamente conforme você faz alterações\n\nCompartilhe uma cópia do seu trabalho\n• Use o AirDrop para enviar documentos a alguém por perto\n• Compartilhe um link do seu trabalho com rapidez e facilidade via Mail ou Mensagens\n• Exporte documentos para os formatos EPUB, Microsoft Word, RTF, TXT e PDF\n• Imprima sem fio via AirPrint, incluindo seleção do intervalo de páginas, número de cópias e impressão em frente e verso\n\nAlguns recursos podem exigir acesso à internet; tarifas e termos adicionais podem ser aplicados.",
"genreIds": [
"6007",
"6000"
],
"bundleId": "com.apple.Pages",
"sellerName": "Apple Inc.",
"releaseDate": "2010-05-26T02:18:04Z",
"primaryGenreName": "Productivity",
"primaryGenreId": 6007,
"isVppDeviceBasedLicensingEnabled": true,
"minimumOsVersion": "16.0",
"trackId": 361309726,
"trackName": "Pages",
"currentVersionReleaseDate": "2023-09-21T15:37:21Z",
"trackCensoredName": "Pages",
"languageCodesISO2A": [
"UK",
"VI"
],
"fileSizeBytes": "458606592",
"sellerUrl": "http://www.apple.com/br/pages/",
"formattedPrice": "Grátis",
"contentAdvisoryRating": "+4",
"averageUserRatingForCurrentVersion": 4.77975,
"userRatingCountForCurrentVersion": 17117,
"averageUserRating": 4.77975,
"trackViewUrl": "https://apps.apple.com/br/app/pages/id361309726?uo=4",
"trackContentRating": "+4",
"version": "13.2",
"wrapperType": "software",
"userRatingCount": 17117
}
]
}

Model

// MARK: - AppMetaDataResult
struct AppMetaDataResult: Codable {
let resultCount: Int
let results: [AppMetaData]
}

// MARK: - Result
struct AppMetaData: Codable {
let releaseDate: String
let releaseNotes: String
let averageUserRatingForCurrentVersion: Double
let trackViewUrl: URL
let version: String
}

typealias AppMetaDataInfo = [AppMetaData]

ViewModel

In the ViewModel, enums of AppStatus that conform to Equatable to use the comparison of app status.

we can fetch the version number using CFBundleShortVersionString:

Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String

To fetch the Bundle identifier of the app:

Bundle.main.bundleIdentifier

In my example, I am using hardcoded values.

final class ForceUpdateViewModel: ObservableObject {
enum AppStatus: Equatable {
case upToDate
case updateAvailable(version: String, storeURL: URL, releaseNotes: String)
}
private var currentVersion: () -> String? = {
"12.1"
}
private let bundleIdentifier: String? = "com.apple.Pages"
}

Swift Concurrency Async/Await

private func fetchAppStatus() async -> AppStatus {
do {
let appMetaDataResult = try await networkService.sendRequest(
endpoint: AppUpdateEndPoint.appUpdateMetaData(
queryParams: Constants.bundleID)) as AppMetaDataResult
print(appMetaDataResult.results)
guard let appMetaData = appMetaDataResult.results.first else {
throw NetworkError.noResponse
}
return try updateStatus(for: appMetaData)
} catch {
print(error)
return AppStatus.upToDate
}
}

Adding Network Layer, Dependency Injection and @MainActor to the ViewModel:

import NetworkKit

@MainActor
final class ForceUpdateViewModel: ObservableObject {
/// ..... Previous declaration
private var networkService: Networkable
init(networkService: Networkable = NetworkService()) {
self.networkService = networkService
Task {
appStatus = await fetchAppStatus()
}
}
}

MainActor — A singleton actor whose executor is equivalent to the main dispatch queue.

Combine

Let’s integrate the Reactive Programming

private func fetchAppStatusCombine() {
networkService.sendRequest(
endpoint: AppUpdateEndPoint.appUpdateMetaData(
queryParams: Constants.bundleID),
type: AppMetaDataResult.self)
.receive(on: RunLoop.main)
.sink { completion in
switch completion {
case .finished:
print("Finished")
case .failure(let error):
print(error.customMessage)
}
} receiveValue: { result in
print(result)
}
.store(in: &cancellable)
}

I have added Force Update and also Apple provided an Update popup to SwiftUI View and UIKit

SwiftUI

Upon clicking the Update option in Popup, it will navigate to the AppStore.

struct AlertView: View {
@State private var showingAlert = false
@State private var forceUpdate = false
var version: String
var storeURL: URL
var releaseNotes: String
var body: some View {
VStack {
// Button to trigger the alert with "Update" and "Cancel" buttons
Button("Show Update Alert") {
forceUpdate = false
showingAlert.toggle()
}

// Button to trigger the force update alert with only "Update" button
Button("Show Force Update Alert") {
forceUpdate = true
showingAlert.toggle()
}
}
.alert(isPresented: $showingAlert) {
// Use a ternary operator to conditionally create the Alert
return forceUpdate
? Alert(title: Text("Force Update"),
message: Text("A new version is available. Update now?"),
dismissButton: .default(Text("Update"), action: {
handleUpdate()
}))
: Alert(title: Text("Update \(version)"),
message: Text(releaseNotes),
primaryButton: .default(Text("Update"), action: {
handleUpdate()
}),
secondaryButton: .cancel())
}
}

func handleUpdate() {
UIApplication.shared.open(storeURL)
}
}

UIKit

@objc func showUpdateAlert() {
forceUpdate = false
showAlert()
}

@objc func showForceUpdateAlert() {
forceUpdate = true
showAlert()
}

func showAlert() {
switch viewModel.appStatus {
case .upToDate:
print("App Upto Date")
case .updateAvailable(let version, let storeURL, let releaseNotes):
let alertController = UIAlertController(
title: forceUpdate ? "Force Update" : "Update \(version)",
message: forceUpdate ? "A new version is available. Update now?": releaseNotes,
preferredStyle: .alert
)

alertController.addAction(UIAlertAction(
title: "Update",
style: .default,
handler: { _ in
self.handleUpdate(storeURL: storeURL)
}
))

if !forceUpdate {
alertController.addAction(UIAlertAction(
title: "Cancel",
style: .cancel,
handler: nil
))
}

present(alertController, animated: true, completion: nil)
}
}

func handleUpdate(storeURL: URL) {
UIApplication.shared.open(storeURL)
}

Grateful for your read, now let’s code on

Interested in connecting? 
Feel free to connect with me on: LinkedIn or follow on GitHub

Please take a look at my first-ever Article 👇

--

--

Kanagasabapathy Rajkumar

Swift Enthusiast | Building Seamless iOS Experiences 🚀 | Swift & Objective-C |