Swiftpack.co -  DnV1eX/EnterpriseAppUpdater as Swift Package
Swiftpack.co is a collection of thousands of indexed Swift packages. Search packages.
DnV1eX/EnterpriseAppUpdater
Manage iOS In-House App Updates
.package(url: "https://github.com/DnV1eX/EnterpriseAppUpdater.git", from: "1.2.2")

EnterpriseAppUpdater

Enterprise App Updater loads app's manifest file, checks for a new version, provides an alert with patch notes and starts the update.

Setup

Swift Package Manager

Open your application project in Xcode 11 or later, go to menu File -> Swift Packages -> Add Package Dependency... and paste the package repository URL https://github.com/DnV1eX/EnterpriseAppUpdater.git.

CocoaPods (deprecated)

Add the pod to your Podfile:

pod 'EnterpriseAppUpdater', '~> 1.2'

Prepare Application Manifest

  1. Generate manifest.plist during enterprise app distribution in Xcode Organizer;
  2. Make sure software-package asset contains direct https link to the app's .ipa;
  3. Optionally add release notes to subtitle metadata to display in the update alert (use \n for line break);
  4. Upload the manifest and get a direct https link you will use to initialize AppUpdater.

Manifest URL must remain the same when the update is released, you only edit .plist content such as app url, bundle-version and optional subtitle.

Usage Example

import EnterpriseAppUpdater

SwiftUI

class Model: ObservableObject {
    
    let updater = AppUpdater(manifest: url) // Initialize AppUpdater with manifest.plist URL.
    @Published var manifestItem: AppUpdater.Manifest.Item?
    
    func checkForUpdate() {
        updater.publisher
            .handleEvents(receiveOutput: { item in
                print(AppUpdater.Message.available, item.metadata.version ?? "?")
            }, receiveCompletion: { completion in
                if case .failure(let error) = completion {
                    switch error {
                    case AppUpdater.ManifestLoadError.connectionError:
                        print(AppUpdater.Message.noConnection, error)
                    case AppUpdater.ManifestCheckError.noAppUpdateNeeded:
                        print(AppUpdater.Message.upToDate, error)
                    default:
                        print(AppUpdater.Message.error, error)
                    }
                }
            })
            .map(Optional.init)
            .replaceError(with: nil)
            .assign(to: &$manifestItem)
    }
}

struct MyApp: App {
    
    @StateObject fileprivate var model = Model()
    @Environment(\.openURL) private var openURL
    
    var body: some Scene {
        WindowGroup {
            ContentView()
                .alert(item: $model.manifestItem) { item in
                    //model.updater.alert(for: item, onStart: openURL.callAsFunction)
                    model.updater.alert(for: item) { url in
                        print(AppUpdater.Message.started, item.metadata.version ?? "?")
                        openURL(url) { success in
                            if !success {
                                print(AppUpdater.Message.error, AppUpdater.URLError.unableToOpen(url))
                            }
                        }
                    } onPostpone: {
                        print(AppUpdater.Message.postponed, item.metadata.version ?? "?")
                    }
                }
                .onAppear {
                    model.checkForUpdate()
                }
        }
    }
}

UIKit

class AppDelegate: UIResponder, UIApplicationDelegate {

    let updater = AppUpdater(manifest: url) // Initialize AppUpdater with manifest.plist URL.
    
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        checkForUpdate(application)
        return true
    }
    
    func checkForUpdate(_ application: UIApplication) {
        updater.loadManifest { result in
            switch result {
            case .success(let manifest):
                switch updater.check(manifest: manifest) {
                case .success(let item):
                    print(AppUpdater.Message.available, item.metadata.version ?? "?")
                    //let alert = updater.alert(for: item)
                    let alert = updater.alert(for: item) { _ in
                        print(AppUpdater.Message.started, item.metadata.version ?? "?")
                        updater.start { error in
                            print(AppUpdater.Message.error, error)
                        }
                    } onPostpone: { _ in
                        print(AppUpdater.Message.postponed, item.metadata.version ?? "?")
                    }
                    application.windows.first?.rootViewController?.present(alert, animated: true)
                case .failure(let error):
                    switch error {
                    case .noAppUpdateNeeded:
                        print(AppUpdater.Message.upToDate, error)
                    default:
                        print(AppUpdater.Message.error, error)
                    }
                }
            case .failure(let error):
                switch error {
                case .connectionError:
                    print(AppUpdater.Message.noConnection, error)
                default:
                    print(AppUpdater.Message.error, error)
                }
            }
        }
    }
}

It's up to you whether to load manifest and check for update on the app launch, at time intervals, or by user request. A good practise is to check for update when the app becomes active, if the specified time has passed since the last check.

License

Copyright © 2019-2021 DnV1eX. All rights reserved. Licensed under the Apache License, Version 2.0.

GitHub

link
Stars: 2
Last commit: 3 weeks ago

Ad: Job Offers

iOS Software Engineer @ Perry Street Software
Perry Street Software is Jack’d and SCRUFF. We are two of the world’s largest gay, bi, trans and queer social dating apps on iOS and Android. Our brands reach more than 20 million members worldwide so members can connect, meet and express themselves on a platform that prioritizes privacy and security. We invest heavily into SwiftUI and using Swift Packages to modularize the codebase.

Release Notes

3 weeks ago

Swiftpack is being maintained by Petr Pavlik | @ptrpavlik | @swiftpackco | API