Uses UTIs to determine whether apps are installed or not

AltStore now inserts an app-specific UTI when resigning apps, and it periodically checks whether that app has been deleted by checking whether UTTypeCopyDeclaration returns nil for the same app-specific UTI.
This commit is contained in:
Riley Testut
2019-12-17 19:17:45 -08:00
parent 748ad8588d
commit e3ea200ad5
4 changed files with 28 additions and 2 deletions

View File

@@ -18,6 +18,7 @@ public extension Bundle
public static let appGroups = "ALTAppGroups" public static let appGroups = "ALTAppGroups"
public static let urlTypes = "CFBundleURLTypes" public static let urlTypes = "CFBundleURLTypes"
public static let exportedUTIs = "UTExportedTypeDeclarations"
} }
} }

View File

@@ -9,6 +9,7 @@
import Foundation import Foundation
import UIKit import UIKit
import UserNotifications import UserNotifications
import MobileCoreServices
import AltSign import AltSign
import AltKit import AltKit
@@ -55,16 +56,19 @@ extension AppManager
do do
{ {
let installedApps = try context.fetch(fetchRequest) let installedApps = try context.fetch(fetchRequest)
for app in installedApps where app.storeApp != nil for app in installedApps
{ {
let uti = UTTypeCopyDeclaration(app.installedAppUTI as CFString)?.takeRetainedValue() as NSDictionary?
if app.bundleIdentifier == StoreApp.altstoreAppID if app.bundleIdentifier == StoreApp.altstoreAppID
{ {
self.scheduleExpirationWarningLocalNotification(for: app) self.scheduleExpirationWarningLocalNotification(for: app)
} }
else else
{ {
if !UIApplication.shared.canOpenURL(app.openAppURL) if uti == nil
{ {
// This UTI is not declared by any apps, which means this app has been deleted by the user.
context.delete(app) context.delete(app)
} }
} }

View File

@@ -187,6 +187,12 @@ extension InstalledApp
return directoryURL return directoryURL
} }
class func installedAppUTI(forBundleIdentifier bundleIdentifier: String) -> String
{
let installedAppUTI = "io.altstore.Installed." + bundleIdentifier
return installedAppUTI
}
var directoryURL: URL { var directoryURL: URL {
return InstalledApp.directoryURL(for: self) return InstalledApp.directoryURL(for: self)
} }
@@ -198,4 +204,8 @@ extension InstalledApp
var refreshedIPAURL: URL { var refreshedIPAURL: URL {
return InstalledApp.refreshedIPAURL(for: self) return InstalledApp.refreshedIPAURL(for: self)
} }
var installedAppUTI: String {
return InstalledApp.installedAppUTI(forBundleIdentifier: self.resignedBundleIdentifier)
}
} }

View File

@@ -372,6 +372,17 @@ private extension ResignAppOperation
infoDictionary[Bundle.Info.appGroups] = appGroups infoDictionary[Bundle.Info.appGroups] = appGroups
} }
// Add app-specific exported UTI so we can check later if this app (extension) is installed or not.
let installedAppUTI = ["UTTypeConformsTo": [],
"UTTypeDescription": "AltStore Installed App",
"UTTypeIconFiles": [],
"UTTypeIdentifier": InstalledApp.installedAppUTI(forBundleIdentifier: profile.bundleIdentifier),
"UTTypeTagSpecification": [:]] as [String : Any]
var exportedUTIs = infoDictionary[Bundle.Info.exportedUTIs] as? [[String: Any]] ?? []
exportedUTIs.append(installedAppUTI)
infoDictionary[Bundle.Info.exportedUTIs] = exportedUTIs
try (infoDictionary as NSDictionary).write(to: bundle.infoPlistURL) try (infoDictionary as NSDictionary).write(to: bundle.infoPlistURL)
} }