[removeExtensions]: Bug-Fix: 1. existing App is ALTApplication not InstalledApp - corrected this 2. process as background mode if prompt can't be made or else signal error if operation context changed in-flight

This commit is contained in:
Magesh K
2025-01-13 07:20:44 +05:30
parent dc4a543b3b
commit e8798499d3
2 changed files with 97 additions and 45 deletions

View File

@@ -41,8 +41,9 @@ final class RemoveAppExtensionsOperation: ResultOperation<Void>
))
}
self.removeAppExtensions(from: targetAppBundle,
appInDatabase: appInDatabase as? InstalledApp,
appInDatabase: appInDatabase as? ALTApplication,
extensions: targetAppBundle.appExtensions,
context.authenticatedContext.presentingViewController)
@@ -59,9 +60,9 @@ final class RemoveAppExtensionsOperation: ResultOperation<Void>
private func removeAppExtensions(from targetAppBundle: ALTApplication,
appInDatabase: InstalledApp?,
extensions: Set<ALTApplication>,
_ presentingViewController: UIViewController?)
appInDatabase: ALTApplication?,
extensions: Set<ALTApplication>,
_ presentingViewController: UIViewController?)
{
// target App Bundle doesn't contain extensions so don't bother
@@ -69,47 +70,39 @@ final class RemoveAppExtensionsOperation: ResultOperation<Void>
return self.finish(.success(()))
}
//App-Extensions: Ensure existing app's extensions in DB and currently installing app bundle's extensions must match
let existingAppEx: Set<InstalledExtension> = appInDatabase?.appExtensions ?? Set()
let targetAppEx: Set<ALTApplication> = targetAppBundle.appExtensions
let existingAppExNames = existingAppEx.map{ appEx in appEx.bundleIdentifier}
let targetAppExNames = targetAppEx.map{ appEx in appEx.bundleIdentifier}
let excessExtensionsInTargetApp = targetAppEx.filter{
!(existingAppExNames.contains($0.bundleIdentifier))
}
let necessaryExtensionsInExistingApp = existingAppEx.filter{
targetAppExNames.contains($0.bundleIdentifier)
}
// always cleanup existing app (app-in-db) based on incoming app that is targeted for install
appInDatabase?.appExtensions = necessaryExtensionsInExistingApp
let isMatching = (targetAppEx.count == existingAppEx.count) && excessExtensionsInTargetApp.isEmpty
let diagnosticsMsg = "RemoveAppExtensionsOperation: App Extensions in existingApp and targetAppBundle are matching: \(isMatching)\n"
+ "RemoveAppExtensionsOperation: existingAppEx: \(existingAppExNames); targetAppBundleEx: \(String(describing: targetAppExNames))\n"
print(diagnosticsMsg)
// if background mode, then remove only the excess extensions
guard let presentingViewController: UIViewController = presentingViewController else {
// perform silent extensions cleanup for those that aren't already present in existing app
print("\n Performing background mode Extensions removal \n")
print("RemoveAppExtensionsOperation: Excess Extensions In TargetAppBundle: \(excessExtensionsInTargetApp)")
print("RemoveAppExtensionsOperation: Necessary Extensions In ExistingAppInDatabase: \(necessaryExtensionsInExistingApp)")
do {
try Self.removeExtensions(from: excessExtensionsInTargetApp)
return self.finish(.success(()))
} catch {
return self.finish(.failure(error))
// process extensionsInfo
let excessExtensions = processExtensionsInfo(from: targetAppBundle, appInDatabase: appInDatabase)
DispatchQueue.main.async {
guard let presentingViewController: UIViewController = presentingViewController,
presentingViewController.viewIfLoaded?.window != nil else {
// background mode: remove only the excess extensions automatically for re-installs
// keep all extensions for fresh install (appInDatabase = nil)
return self.backgroundModeExtensionsCleanup(excessExtensions: excessExtensions)
}
// present prompt to the user if we have a view context
let alertController = self.createAlertDialog(from: targetAppBundle, extensions: extensions, presentingViewController)
presentingViewController.present(alertController, animated: true){
// if for any reason the view wasn't presented, then just signal that as error
if presentingViewController.presentedViewController == nil {
let errMsg = "RemoveAppExtensionsOperation: unable to present dialog, view context not available." +
"\nDid you move to different screen or background after starting the operation?"
self.finish(.failure(
OperationError.invalidOperationContext(errMsg)
))
}
}
}
}
private func createAlertDialog(from targetAppBundle: ALTApplication,
extensions: Set<ALTApplication>,
_ presentingViewController: UIViewController) -> UIAlertController
{
/// Foreground prompt:
let firstSentence: String
if UserDefaults.standard.activeAppLimitIncludesExtensions
@@ -175,8 +168,58 @@ final class RemoveAppExtensionsOperation: ResultOperation<Void>
}
})
DispatchQueue.main.async {
presentingViewController.present(alertController, animated: true)
return alertController
}
struct ExtensionsInfo{
let excessInTarget: Set<ALTApplication>
let necessaryInExisting: Set<ALTApplication>
}
private func processExtensionsInfo(from targetAppBundle: ALTApplication,
appInDatabase: ALTApplication?) -> Set<ALTApplication>
{
//App-Extensions: Ensure existing app's extensions in DB and currently installing app bundle's extensions must match
let targetAppEx: Set<ALTApplication> = targetAppBundle.appExtensions
let targetAppExNames = targetAppEx.map{ appEx in appEx.bundleIdentifier}
guard let extensionsInExistingApp = appInDatabase?.appExtensions else {
let diagnosticsMsg = "RemoveAppExtensionsOperation: ExistingApp is nil, Hence keeping all app extensions from targetAppBundle"
+ "RemoveAppExtensionsOperation: ExistingAppEx: nil; targetAppBundleEx: \(targetAppExNames)"
print(diagnosticsMsg)
return Set() // nothing is excess since we are keeping all, so returning empty
}
let existingAppEx: Set<ALTApplication> = extensionsInExistingApp
let existingAppExNames = existingAppEx.map{ appEx in appEx.bundleIdentifier}
let excessExtensionsInTargetApp = targetAppEx.filter{
!(existingAppExNames.contains($0.bundleIdentifier))
}
let excessExtensionsInExistingApp = existingAppEx.filter{
!(targetAppExNames.contains($0.bundleIdentifier))
}
let isMatching = (targetAppEx.count == existingAppEx.count) && excessExtensionsInTargetApp.isEmpty
let diagnosticsMsg = "RemoveAppExtensionsOperation: App Extensions in existingApp and targetAppBundle are matching: \(isMatching)\n"
+ "RemoveAppExtensionsOperation: existingAppEx: \(existingAppExNames); targetAppBundleEx: \(String(describing: targetAppExNames))\n"
print(diagnosticsMsg)
return excessExtensionsInTargetApp
}
private func backgroundModeExtensionsCleanup(excessExtensions: Set<ALTApplication>) {
// perform silent extensions cleanup for those that aren't already present in existing app
print("\n Performing background mode Extensions removal \n")
print("RemoveAppExtensionsOperation: Excess Extensions In TargetAppBundle: \(excessExtensions.map{$0.bundleIdentifier})")
do {
try Self.removeExtensions(from: excessExtensions)
return self.finish(.success(()))
} catch {
return self.finish(.failure(error))
}
}
}