mirror of
https://github.com/SideStore/SideStore.git
synced 2026-02-09 06:43:25 +01:00
[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:
@@ -58,6 +58,8 @@ extension OperationError
|
|||||||
case anisetteV3Error//(message: String)
|
case anisetteV3Error//(message: String)
|
||||||
case cacheClearError//(errors: [String])
|
case cacheClearError//(errors: [String])
|
||||||
case noWiFi
|
case noWiFi
|
||||||
|
|
||||||
|
case invalidOperationContext
|
||||||
}
|
}
|
||||||
|
|
||||||
static var cancelled: CancellationError { CancellationError() }
|
static var cancelled: CancellationError { CancellationError() }
|
||||||
@@ -130,6 +132,10 @@ extension OperationError
|
|||||||
OperationError(code: .invalidParameters, failureReason: message)
|
OperationError(code: .invalidParameters, failureReason: message)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static func invalidOperationContext(_ message: String? = nil) -> OperationError {
|
||||||
|
OperationError(code: .invalidOperationContext, failureReason: message)
|
||||||
|
}
|
||||||
|
|
||||||
static func forbidden(failureReason: String? = nil, file: String = #fileID, line: UInt = #line) -> OperationError {
|
static func forbidden(failureReason: String? = nil, file: String = #fileID, line: UInt = #line) -> OperationError {
|
||||||
OperationError(code: .forbidden, failureReason: failureReason, sourceFile: file, sourceLine: line)
|
OperationError(code: .forbidden, failureReason: failureReason, sourceFile: file, sourceLine: line)
|
||||||
}
|
}
|
||||||
@@ -232,7 +238,10 @@ struct OperationError: ALTLocalizedError {
|
|||||||
|
|
||||||
case .invalidParameters:
|
case .invalidParameters:
|
||||||
let message = self._failureReason.map { ": \n\($0)" } ?? "."
|
let message = self._failureReason.map { ": \n\($0)" } ?? "."
|
||||||
return String(format: NSLocalizedString("Invalid parameters%@", comment: ""), message)
|
return String(format: NSLocalizedString("Invalid parameters\n%@", comment: ""), message)
|
||||||
|
case .invalidOperationContext:
|
||||||
|
let message = self._failureReason.map { ": \n\($0)" } ?? "."
|
||||||
|
return String(format: NSLocalizedString("Invalid Operation Context\n%@", comment: ""), message)
|
||||||
case .serverNotFound: return NSLocalizedString("AltServer could not be found.", comment: "")
|
case .serverNotFound: return NSLocalizedString("AltServer could not be found.", comment: "")
|
||||||
case .connectionFailed: return NSLocalizedString("A connection to AltServer could not be established.", comment: "")
|
case .connectionFailed: return NSLocalizedString("A connection to AltServer could not be established.", comment: "")
|
||||||
case .connectionDropped: return NSLocalizedString("The connection to AltServer was dropped.", comment: "")
|
case .connectionDropped: return NSLocalizedString("The connection to AltServer was dropped.", comment: "")
|
||||||
|
|||||||
@@ -41,8 +41,9 @@ final class RemoveAppExtensionsOperation: ResultOperation<Void>
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
self.removeAppExtensions(from: targetAppBundle,
|
self.removeAppExtensions(from: targetAppBundle,
|
||||||
appInDatabase: appInDatabase as? InstalledApp,
|
appInDatabase: appInDatabase as? ALTApplication,
|
||||||
extensions: targetAppBundle.appExtensions,
|
extensions: targetAppBundle.appExtensions,
|
||||||
context.authenticatedContext.presentingViewController)
|
context.authenticatedContext.presentingViewController)
|
||||||
|
|
||||||
@@ -59,9 +60,9 @@ final class RemoveAppExtensionsOperation: ResultOperation<Void>
|
|||||||
|
|
||||||
|
|
||||||
private func removeAppExtensions(from targetAppBundle: ALTApplication,
|
private func removeAppExtensions(from targetAppBundle: ALTApplication,
|
||||||
appInDatabase: InstalledApp?,
|
appInDatabase: ALTApplication?,
|
||||||
extensions: Set<ALTApplication>,
|
extensions: Set<ALTApplication>,
|
||||||
_ presentingViewController: UIViewController?)
|
_ presentingViewController: UIViewController?)
|
||||||
{
|
{
|
||||||
|
|
||||||
// target App Bundle doesn't contain extensions so don't bother
|
// target App Bundle doesn't contain extensions so don't bother
|
||||||
@@ -69,47 +70,39 @@ final class RemoveAppExtensionsOperation: ResultOperation<Void>
|
|||||||
return self.finish(.success(()))
|
return self.finish(.success(()))
|
||||||
}
|
}
|
||||||
|
|
||||||
//App-Extensions: Ensure existing app's extensions in DB and currently installing app bundle's extensions must match
|
// process extensionsInfo
|
||||||
let existingAppEx: Set<InstalledExtension> = appInDatabase?.appExtensions ?? Set()
|
let excessExtensions = processExtensionsInfo(from: targetAppBundle, appInDatabase: appInDatabase)
|
||||||
let targetAppEx: Set<ALTApplication> = targetAppBundle.appExtensions
|
|
||||||
|
DispatchQueue.main.async {
|
||||||
let existingAppExNames = existingAppEx.map{ appEx in appEx.bundleIdentifier}
|
guard let presentingViewController: UIViewController = presentingViewController,
|
||||||
let targetAppExNames = targetAppEx.map{ appEx in appEx.bundleIdentifier}
|
presentingViewController.viewIfLoaded?.window != nil else {
|
||||||
|
// background mode: remove only the excess extensions automatically for re-installs
|
||||||
let excessExtensionsInTargetApp = targetAppEx.filter{
|
// keep all extensions for fresh install (appInDatabase = nil)
|
||||||
!(existingAppExNames.contains($0.bundleIdentifier))
|
return self.backgroundModeExtensionsCleanup(excessExtensions: excessExtensions)
|
||||||
}
|
}
|
||||||
|
|
||||||
let necessaryExtensionsInExistingApp = existingAppEx.filter{
|
// present prompt to the user if we have a view context
|
||||||
targetAppExNames.contains($0.bundleIdentifier)
|
let alertController = self.createAlertDialog(from: targetAppBundle, extensions: extensions, presentingViewController)
|
||||||
}
|
presentingViewController.present(alertController, animated: true){
|
||||||
|
|
||||||
// always cleanup existing app (app-in-db) based on incoming app that is targeted for install
|
// if for any reason the view wasn't presented, then just signal that as error
|
||||||
appInDatabase?.appExtensions = necessaryExtensionsInExistingApp
|
if presentingViewController.presentedViewController == nil {
|
||||||
|
let errMsg = "RemoveAppExtensionsOperation: unable to present dialog, view context not available." +
|
||||||
let isMatching = (targetAppEx.count == existingAppEx.count) && excessExtensionsInTargetApp.isEmpty
|
"\nDid you move to different screen or background after starting the operation?"
|
||||||
let diagnosticsMsg = "RemoveAppExtensionsOperation: App Extensions in existingApp and targetAppBundle are matching: \(isMatching)\n"
|
self.finish(.failure(
|
||||||
+ "RemoveAppExtensionsOperation: existingAppEx: \(existingAppExNames); targetAppBundleEx: \(String(describing: targetAppExNames))\n"
|
OperationError.invalidOperationContext(errMsg)
|
||||||
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))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func createAlertDialog(from targetAppBundle: ALTApplication,
|
||||||
|
extensions: Set<ALTApplication>,
|
||||||
|
_ presentingViewController: UIViewController) -> UIAlertController
|
||||||
|
{
|
||||||
|
|
||||||
|
/// Foreground prompt:
|
||||||
let firstSentence: String
|
let firstSentence: String
|
||||||
|
|
||||||
if UserDefaults.standard.activeAppLimitIncludesExtensions
|
if UserDefaults.standard.activeAppLimitIncludesExtensions
|
||||||
@@ -175,8 +168,58 @@ final class RemoveAppExtensionsOperation: ResultOperation<Void>
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
return alertController
|
||||||
presentingViewController.present(alertController, animated: true)
|
}
|
||||||
|
|
||||||
|
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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user