Skip to content
UPMAppPublic

About

UpdateManager iOS Integration

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 

Repository files navigation

UpdateManager iOS Integration

UpdateManager lets an iOS application receive remotely published update and maintenance decisions. It connects to the managed UpdateManager service, caches the last compatible configuration, and can either present its built-in SwiftUI interface automatically or return decisions for presentation on demand.

Requirements

  • iOS 15 or later
  • Swift 6 and Xcode 16 or later
  • An application and public key created in the UpdateManager dashboard
  • A published configuration for the environment used by the app

The application public key is an identifier, not a secret. It is safe to embed in an application binary.

Install with Swift Package Manager

In Xcode, select File > Add Package Dependencies, enter the UpdateManager package repository URL, and add the UpdateManager product to the application target.

Then import the module where it is used:

import UpdateManager

Option 1: Automatic presentation

Automatic presentation is the recommended setup for most SwiftUI apps. Configure UpdateManager once during application startup and attach .updateManager() to the root view.

import SwiftUI
import UpdateManager

@main
struct MyApp: App {
    init() {
        UpdateManager.shared.configure(
            UpdateManagerConfiguration(
                publicKey: "pub_your_application_key",
                analyticsEnabled: false
            )
        )
    }

    var body: some Scene {
        WindowGroup {
            RootView()
                .updateManager()
        }
    }
}

The modifier checks the published policy when its root view starts and whenever the app returns to the active state. It presents:

  • A dismissible screen for optional updates when dismissal is allowed.
  • A non-dismissable screen for required updates.
  • A non-dismissable maintenance screen with a retry action.
  • Nothing when the current app version requires no action.

Update status is controlled by the published server policy. If a network request fails, UpdateManager safely uses compatible cached configuration when the selected fetch policy permits it. A failure never invents a required update or maintenance state.

Option 2: On-demand or custom presentation

Use manual presentation when the app should decide when to check, has custom update UI, or needs tighter control over navigation and multi-scene behavior.

Configure the SDK with manual presentation and do not add .updateManager() to the root view:

@main
struct MyApp: App {
    init() {
        UpdateManager.shared.configure(
            UpdateManagerConfiguration(
                publicKey: "pub_your_application_key",
                presentationMode: .manual,
                analyticsEnabled: false
            )
        )
    }

    var body: some Scene {
        WindowGroup {
            RootView()
        }
    }
}

Call checkForUpdates() whenever the app wants to evaluate the current policy:

Button("Check for Updates") {
    Task {
        do {
            let decision = try await UpdateManager.shared.checkForUpdates()

            switch decision {
            case .none:
                print("The app is up to date")
            case .optional(let content):
                print("Optional update: \(content.latestVersion)")
            case .required(let content):
                print("Required update: \(content.latestVersion)")
            case .maintenance(let content):
                print("Maintenance: \(content.title)")
            }
        } catch {
            print("Update check failed: \(error.localizedDescription)")
        }
    }
}

Present the built-in views on demand

The following example evaluates the policy when the user taps a button and presents UpdateManager's built-in SwiftUI views:

import SwiftUI
import UpdateManager

struct SettingsView: View {
    @State private var decision: UpdatePresentationDecision = .none
    @State private var errorMessage: String?

    var body: some View {
        Button("Check for Updates") {
            Task { await checkForUpdates() }
        }
        .alert("Update Check Failed", isPresented: .constant(errorMessage != nil)) {
            Button("OK") { errorMessage = nil }
        } message: {
            Text(errorMessage ?? "")
        }
        .fullScreenCover(isPresented: isPresentingUpdate) {
            switch decision {
            case .optional(let content):
                OptionalUpdateView(content: content) {
                    decision = .none
                }

            case .required(let content):
                RequiredUpdateView(content: content)

            case .maintenance(let content):
                MaintenanceView(content: content) {
                    await checkForUpdates()
                }

            case .none:
                EmptyView()
            }
        }
    }

    private var isPresentingUpdate: Binding<Bool> {
        Binding(
            get: { decision != .none },
            set: { isPresented in
                if !isPresented { decision = .none }
            }
        )
    }

    @MainActor
    private func checkForUpdates() async {
        do {
            decision = try await UpdateManager.shared.checkForUpdates()
        } catch {
            errorMessage = error.localizedDescription
        }
    }
}

checkForUpdates() evaluates the real published policy. It does not force an update screen when the current version is not eligible for one.

Fetch configuration without presenting UI

Apps can fetch the latest configuration for feature flags or custom behavior without presenting an update screen:

do {
    let result = try await UpdateManager.shared.fetchConfiguration(
        policy: .useCacheIfFresh
    )

    print("Configuration revision: \(result.configuration.revision)")
    print("Source: \(result.source)")
} catch {
    print("Configuration fetch failed: \(error.localizedDescription)")
}

Environments

Production is the default environment:

UpdateManager.shared.configure(publicKey: "pub_your_application_key")

To use another dashboard environment explicitly:

UpdateManager.shared.configure(
    UpdateManagerConfiguration(
        publicKey: "pub_your_application_key",
        environment: .staging,
        analyticsEnabled: false
    )
)

The selected environment must have a published configuration. Otherwise the SDK reports UpdateManagerError.configurationNotPublished.

Production checklist

Before shipping an app:

  1. Confirm that the public key belongs to the correct iOS application.
  2. Publish a configuration for the production environment.
  3. Provide a valid App Store URL before publishing a required update.
  4. Test optional, required, maintenance, and no-update states.
  5. Keep analytics disabled until analytics collection is enabled for the UpdateManager service.
  6. Prefer manual presentation for apps with multiple independent scenes or a custom navigation architecture.

Troubleshooting

No configuration has been published

Verify that the public key is correct and publish the configuration for the same environment configured in the SDK.

The SDK reports an invalid response

Confirm that the dashboard application is configured for iOS. Required updates must include a valid App Store URL.

A network request fails

Confirm that the device can reach https://api.updatemanager.dev. For support, capture the SDK error and the request ID available through diagnostics; never log session tokens or other application credentials.

About

UpdateManager iOS Integration

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages