Home Articles

Force Update & show new App Version is Available

Force Update App Example

Force Update App Example

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

  1. In-app notifications

  1. Push Notifications

In-App Notifications

Alert Controller Setup:

We are creating a method showInAppNotification(message: String) for in-app notification. Firstly, we will setup the AlertController using UIAlertController

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

Alert Controller Setup

Update Action:

Update the alert action using the AppStore URL of particular app. For instance, I have taken Paytm-money App.

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

Update Alert Action

Adding Action and Presenting the Alert:

alertController.addAction(updateAction)

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

Presenting an alert

Usage Example:

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

Usage Example

Complete Code:

Please find the complete code below:

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

    let updateAction = UIAlertAction(
        title: "Update",
        style: .default
    ) { _ in
        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)

Complete code example

Push Notifications

Push Notification Example - Firebase

Push Notification Example - Firebase

Flow

Push Notifications Flow - Reference

Push Notifications Flow - Reference

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)
    }
}

Adds a method to didReceiveRemoteNotification in AppDelegate.swift

Explanation

  • This function is triggered when the app receives a remote notification. It’s defined in AppDelegate.swift.

  • If the userInfo dictionary contains an updateMessage key, retrieve its value as a string.

  • Use showInAppNotification to display the message from the remote notification to the user.

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

  • Security Vulnerabilities — Discovered vulnerability that puts user data or privacy at risk. In rare cases during SSL pinning, the leaf node certificate has expired.

  • Critical Bug Fix — The bug has affected the user’s performance due to some dependency or recent upgrade.

  • Server-side changes — There are some changes in the backend system or domain changes.

  • 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

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=361309726 → https://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
    }
  ]
}

Pages itunes JSON

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]

Model class

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 Version number

To fetch the Bundle identifier of the app:

Bundle.main.bundleIdentifier

Fetch Bundle Identifier of App

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"
}

ForceUpdateViewModel

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
    }
}

fetchAppStatus method

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()
        }
    }
}

Integrating Network Layer to ViewModel

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)
}

Combine Example

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)
    }
}

SwiftUI Example

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)
}

UIKit Example

Note

Please find the GitHub Repo

This is a free third party commenting service we are using for you, which needs you to sign in to post a comment, but the good bit is you can stay anonymous while commenting.