From 90b4fa7605c15b391b323e68e766b9d98dff177d Mon Sep 17 00:00:00 2001 From: Riley Testut Date: Mon, 6 Feb 2023 16:30:42 -0600 Subject: [PATCH 01/14] [Apps] Updates Delta beta to 1.4b5 --- AltStore/Resources/apps.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/AltStore/Resources/apps.json b/AltStore/Resources/apps.json index 610bc596..d5dd6eed 100644 --- a/AltStore/Resources/apps.json +++ b/AltStore/Resources/apps.json @@ -134,14 +134,14 @@ "bundleIdentifier": "com.rileytestut.Delta.Beta", "developerName": "Riley Testut", "subtitle": "Classic games in your pocket.", - "version": "1.4b4", - "versionDate": "2022-11-07T12:00:00-06:00", - "versionDescription": "NEW\n• Fixed app freezing when using Hold Button feature\n• Fixed re-activating held inputs\n\nPREVIOUS BETAS\n• Supports Split View and Stage Manager multitasking on iPad\n• Optimized full screen-width controller skins when using Split View, Slide Over, or Stage Manager\n• Fixed using Xbox controller analog sticks as D-pad inputs\n• Fixed remapping analog sticks", - "downloadURL": "https://cdn.altstore.io/file/altstore/apps/delta/1_4_b4.ipa", + "version": "1.4b5", + "versionDate": "2023-02-02T12:00:00-06:00", + "versionDescription": "“Respect Silent Mode” setting\n• When enabled, game audio will only play when your device isn’t muted\n• Automatically mutes game audio if another app is playing audio\n\nCheatBase (thanks Noah Keck @horn978)\n• Browse and add all known cheats for games\n• Limited to DS games for now, but will be expanded in the future", + "downloadURL": "https://cdn.altstore.io/file/altstore/apps/delta/1_4_b5.ipa", "localizedDescription": "The next consoles for Delta are coming: this beta version of Delta brings support for playing Nintendo DS and Sega Genesis games!\n\nPlease report any issues you find to support@altstore.io. Thanks!", "iconURL": "https://user-images.githubusercontent.com/705880/63391976-4d311700-c37a-11e9-91a8-4fb0c454413d.png", "tintColor": "8A28F7", - "size": 42958876, + "size": 46505452, "beta": true, "permissions": [ { From eeea64f7804aafaa6cad2f509de05bfcf6dba27f Mon Sep 17 00:00:00 2001 From: Riley Testut Date: Mon, 6 Feb 2023 17:36:05 -0600 Subject: [PATCH 02/14] Supports sideloading more than 3 apps via MacDirtyCow exploit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The MacDirtyCow exploit allows users to remove the 3 active apps limit on iOS 16.1.2 and earlier. To support this, we’ve added a new (hidden) “Enforce 3-App Limit” setting that can be disabled to allow sideloading more than 3 apps. --- AltStore/My Apps/MyAppsViewController.swift | 8 ++- .../Operations/AuthenticationOperation.swift | 2 +- AltStore/Settings/Settings.storyboard | 41 +++++++++++++++ .../Settings/SettingsViewController.swift | 52 +++++++++++++++++-- .../Extensions/UserDefaults+AltStore.swift | 6 ++- AltStoreCore/Model/InstalledApp.swift | 18 ++++++- .../Policies/InstalledAppPolicy.swift | 2 +- 7 files changed, 118 insertions(+), 11 deletions(-) diff --git a/AltStore/My Apps/MyAppsViewController.swift b/AltStore/My Apps/MyAppsViewController.swift index c659971f..1a21be0f 100644 --- a/AltStore/My Apps/MyAppsViewController.swift +++ b/AltStore/My Apps/MyAppsViewController.swift @@ -991,7 +991,7 @@ private extension MyAppsViewController @objc func presentInactiveAppsAlert() { - let message: String + var message: String if UserDefaults.standard.activeAppLimitIncludesExtensions { @@ -1000,6 +1000,12 @@ private extension MyAppsViewController else { message = NSLocalizedString("Non-developer Apple IDs are limited to 3 apps. Inactive apps are backed up and uninstalled so they don't count towards your total, but will be reinstalled with all their data when activated again.", comment: "") + + if UserDefaults.standard.ignoreActiveAppsLimit + { + message += "\n\n" + message += NSLocalizedString("If you're using the MacDirtyCow exploit to remove the 3-app limit, you can install up to 10 apps and app extensions instead.", comment: "") + } } let alertController = UIAlertController(title: NSLocalizedString("What are inactive apps?", comment: ""), message: message, preferredStyle: .alert) diff --git a/AltStore/Operations/AuthenticationOperation.swift b/AltStore/Operations/AuthenticationOperation.swift index 1fd89ff4..509ea38e 100644 --- a/AltStore/Operations/AuthenticationOperation.swift +++ b/AltStore/Operations/AuthenticationOperation.swift @@ -245,7 +245,7 @@ class AuthenticationOperation: ResultOperation<(ALTTeam, ALTCertificate, ALTAppl let activeAppsMinimumVersion = OperatingSystemVersion(majorVersion: 13, minorVersion: 3, patchVersion: 1) if team.type == .free, ProcessInfo.processInfo.isOperatingSystemAtLeast(activeAppsMinimumVersion) { - UserDefaults.standard.activeAppsLimit = ALTActiveAppsLimit + UserDefaults.standard.activeAppsLimit = InstalledApp.freeAccountActiveAppsLimit } else { diff --git a/AltStore/Settings/Settings.storyboard b/AltStore/Settings/Settings.storyboard index 5415411e..90460b79 100644 --- a/AltStore/Settings/Settings.storyboard +++ b/AltStore/Settings/Settings.storyboard @@ -518,6 +518,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -603,6 +643,7 @@ + diff --git a/AltStore/Settings/SettingsViewController.swift b/AltStore/Settings/SettingsViewController.swift index 9888a521..f1ebef70 100644 --- a/AltStore/Settings/SettingsViewController.swift +++ b/AltStore/Settings/SettingsViewController.swift @@ -25,6 +25,7 @@ extension SettingsViewController case instructions case techyThings case credits + case macDirtyCow case debug } @@ -70,6 +71,7 @@ class SettingsViewController: UITableViewController @IBOutlet private var accountTypeLabel: UILabel! @IBOutlet private var backgroundRefreshSwitch: UISwitch! + @IBOutlet private var enforceThreeAppLimitSwitch: UISwitch! @IBOutlet private var versionLabel: UILabel! @@ -146,6 +148,7 @@ private extension SettingsViewController } self.backgroundRefreshSwitch.isOn = UserDefaults.standard.isBackgroundRefreshEnabled + self.enforceThreeAppLimitSwitch.isOn = !UserDefaults.standard.ignoreActiveAppsLimit if self.isViewLoaded { @@ -209,6 +212,16 @@ private extension SettingsViewController case .credits: settingsHeaderFooterView.primaryLabel.text = NSLocalizedString("CREDITS", comment: "") + case .macDirtyCow: + if isHeader + { + settingsHeaderFooterView.primaryLabel.text = NSLocalizedString("MACDIRTYCOW", comment: "") + } + else + { + settingsHeaderFooterView.secondaryLabel.text = NSLocalizedString("If you've removed the 3-sideloaded app limit via the MacDirtyCow exploit, disable this setting to sideload more than 3 apps at a time.", comment: "") + } + case .debug: settingsHeaderFooterView.primaryLabel.text = NSLocalizedString("DEBUG", comment: "") } @@ -225,6 +238,20 @@ private extension SettingsViewController let size = settingsHeaderFooterView.contentView.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize) return size.height } + + func isSectionHidden(_ section: Section) -> Bool + { + switch section + { + case .macDirtyCow: + let ios16_2 = OperatingSystemVersion(majorVersion: 16, minorVersion: 2, patchVersion: 0) + + let isMacDirtyCowExploitSupported = !ProcessInfo.processInfo.isOperatingSystemAtLeast(ios16_2) + return !(isMacDirtyCowExploitSupported && UserDefaults.standard.isDebugModeEnabled) + + default: return false + } + } } private extension SettingsViewController @@ -279,6 +306,16 @@ private extension SettingsViewController UserDefaults.standard.isBackgroundRefreshEnabled = sender.isOn } + @IBAction func toggleEnforceThreeAppLimit(_ sender: UISwitch) + { + UserDefaults.standard.ignoreActiveAppsLimit = !sender.isOn + + if UserDefaults.standard.activeAppsLimit != nil + { + UserDefaults.standard.activeAppsLimit = InstalledApp.freeAccountActiveAppsLimit + } + } + @available(iOS 14, *) @IBAction func addRefreshAppsShortcut() { @@ -376,6 +413,7 @@ extension SettingsViewController let section = Section.allCases[section] switch section { + case _ where isSectionHidden(section): return 0 case .signIn: return (self.activeTeam == nil) ? 1 : 0 case .account: return (self.activeTeam == nil) ? 0 : 3 case .appRefresh: return AppRefreshRow.allCases.count @@ -404,9 +442,10 @@ extension SettingsViewController let section = Section.allCases[section] switch section { + case _ where isSectionHidden(section): return nil case .signIn where self.activeTeam != nil: return nil case .account where self.activeTeam == nil: return nil - case .signIn, .account, .patreon, .appRefresh, .techyThings, .credits, .debug: + case .signIn, .account, .patreon, .appRefresh, .techyThings, .credits, .macDirtyCow, .debug: let headerView = tableView.dequeueReusableHeaderFooterView(withIdentifier: "HeaderFooterView") as! SettingsHeaderFooterView self.prepare(headerView, for: section, isHeader: true) return headerView @@ -420,8 +459,9 @@ extension SettingsViewController let section = Section.allCases[section] switch section { + case _ where isSectionHidden(section): return nil case .signIn where self.activeTeam != nil: return nil - case .signIn, .patreon, .appRefresh: + case .signIn, .patreon, .appRefresh, .macDirtyCow: let footerView = tableView.dequeueReusableHeaderFooterView(withIdentifier: "HeaderFooterView") as! SettingsHeaderFooterView self.prepare(footerView, for: section, isHeader: false) return footerView @@ -435,9 +475,10 @@ extension SettingsViewController let section = Section.allCases[section] switch section { + case _ where isSectionHidden(section): return 1.0 case .signIn where self.activeTeam != nil: return 1.0 case .account where self.activeTeam == nil: return 1.0 - case .signIn, .account, .patreon, .appRefresh, .techyThings, .credits, .debug: + case .signIn, .account, .patreon, .appRefresh, .techyThings, .credits, .macDirtyCow, .debug: let height = self.preferredHeight(for: self.prototypeHeaderFooterView, in: section, isHeader: true) return height @@ -450,9 +491,10 @@ extension SettingsViewController let section = Section.allCases[section] switch section { + case _ where isSectionHidden(section): return 1.0 case .signIn where self.activeTeam != nil: return 1.0 case .account where self.activeTeam == nil: return 1.0 - case .signIn, .patreon, .appRefresh: + case .signIn, .patreon, .appRefresh, .macDirtyCow: let height = self.preferredHeight(for: self.prototypeHeaderFooterView, in: section, isHeader: false) return height @@ -520,7 +562,7 @@ extension SettingsViewController case .refreshAttempts: break } - case .account, .patreon, .instructions, .techyThings: break + case .account, .patreon, .instructions, .techyThings, .macDirtyCow: break } } } diff --git a/AltStoreCore/Extensions/UserDefaults+AltStore.swift b/AltStoreCore/Extensions/UserDefaults+AltStore.swift index 2b0ac0f4..0f3c512f 100644 --- a/AltStoreCore/Extensions/UserDefaults+AltStore.swift +++ b/AltStoreCore/Extensions/UserDefaults+AltStore.swift @@ -41,6 +41,7 @@ public extension UserDefaults @NSManaged var trustedSourceIDs: [String]? + @nonobjc var activeAppsLimit: Int? { get { return self._activeAppsLimit?.intValue @@ -58,6 +59,8 @@ public extension UserDefaults } @NSManaged @objc(activeAppsLimit) private var _activeAppsLimit: NSNumber? + @NSManaged var ignoreActiveAppsLimit: Bool + class func registerDefaults() { let ios13_5 = OperatingSystemVersion(majorVersion: 13, minorVersion: 5, patchVersion: 0) @@ -72,7 +75,8 @@ public extension UserDefaults #keyPath(UserDefaults.isLegacyDeactivationSupported): isLegacyDeactivationSupported, #keyPath(UserDefaults.activeAppLimitIncludesExtensions): activeAppLimitIncludesExtensions, #keyPath(UserDefaults.localServerSupportsRefreshing): localServerSupportsRefreshing, - #keyPath(UserDefaults.requiresAppGroupMigration): true + #keyPath(UserDefaults.requiresAppGroupMigration): true, + #keyPath(UserDefaults.ignoreActiveAppsLimit): false, ] UserDefaults.standard.register(defaults: defaults) diff --git a/AltStoreCore/Model/InstalledApp.swift b/AltStoreCore/Model/InstalledApp.swift index b23926df..9511e15c 100644 --- a/AltStoreCore/Model/InstalledApp.swift +++ b/AltStoreCore/Model/InstalledApp.swift @@ -11,8 +11,22 @@ import CoreData import AltSign -// Free developer accounts are limited to only 3 active sideloaded apps at a time as of iOS 13.3.1. -public let ALTActiveAppsLimit = 3 +extension InstalledApp +{ + public static var freeAccountActiveAppsLimit: Int { + if UserDefaults.standard.ignoreActiveAppsLimit + { + // MacDirtyCow exploit allows users to remove 3-app limit, so return 10 to match App ID limit per-week. + // Don't return nil because that implies there is no limit, which isn't quite true due to App ID limit. + return 10 + } + else + { + // Free developer accounts are limited to only 3 active sideloaded apps at a time as of iOS 13.3.1. + return 3 + } + } +} public protocol InstalledAppProtocol: Fetchable { diff --git a/AltStoreCore/Model/Migrations/Policies/InstalledAppPolicy.swift b/AltStoreCore/Model/Migrations/Policies/InstalledAppPolicy.swift index 880ae8fe..9c01f15f 100644 --- a/AltStoreCore/Model/Migrations/Policies/InstalledAppPolicy.swift +++ b/AltStoreCore/Model/Migrations/Policies/InstalledAppPolicy.swift @@ -50,7 +50,7 @@ class InstalledAppToInstalledAppMigrationPolicy: NSEntityMigrationPolicy // We can assume there is an active app limit, // but will confirm next time user authenticates. - UserDefaults.standard.activeAppsLimit = ALTActiveAppsLimit + UserDefaults.standard.activeAppsLimit = InstalledApp.freeAccountActiveAppsLimit } return NSNumber(value: isActive) From 3adfc9db6de2f8e05b1d7dee1bbe486493c961c0 Mon Sep 17 00:00:00 2001 From: Riley Testut Date: Tue, 7 Feb 2023 16:11:39 -0600 Subject: [PATCH 03/14] =?UTF-8?q?Adds=20=E2=80=9CClear=20Cache=E2=80=9D=20?= =?UTF-8?q?button=20to=20remove=20temporary=20files=20and=20uninstalled=20?= =?UTF-8?q?app=20backups?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AltStore.xcodeproj/project.pbxproj | 4 + AltStore/Managing Apps/AppManager.swift | 22 +- .../Operations/ClearAppCacheOperation.swift | 203 ++++++++++++++++++ AltStore/Settings/Settings.storyboard | 28 +++ .../Settings/SettingsViewController.swift | 44 +++- 5 files changed, 294 insertions(+), 7 deletions(-) create mode 100644 AltStore/Operations/ClearAppCacheOperation.swift diff --git a/AltStore.xcodeproj/project.pbxproj b/AltStore.xcodeproj/project.pbxproj index 30c28bdf..2b140712 100644 --- a/AltStore.xcodeproj/project.pbxproj +++ b/AltStore.xcodeproj/project.pbxproj @@ -369,6 +369,7 @@ D58916FE28C7C55C00E39C8B /* LoggedError.swift in Sources */ = {isa = PBXBuildFile; fileRef = D58916FD28C7C55C00E39C8B /* LoggedError.swift */; }; D58D5F2E26DFE68E00E55E38 /* LaunchAtLogin in Frameworks */ = {isa = PBXBuildFile; productRef = D58D5F2D26DFE68E00E55E38 /* LaunchAtLogin */; }; D593F1942717749A006E82DE /* PatchAppOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = D593F1932717749A006E82DE /* PatchAppOperation.swift */; }; + D5ACE84528E3B8450021CAB9 /* ClearAppCacheOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5ACE84428E3B8450021CAB9 /* ClearAppCacheOperation.swift */; }; D5CA0C4B280E141900469595 /* ManagedPatron.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5CA0C4A280E141900469595 /* ManagedPatron.swift */; }; D5CA0C4E280E249E00469595 /* AltStore9ToAltStore10.xcmappingmodel in Sources */ = {isa = PBXBuildFile; fileRef = D5CA0C4D280E249E00469595 /* AltStore9ToAltStore10.xcmappingmodel */; }; D5DAE0942804B0B80034D8D4 /* ScreenshotProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5DAE0932804B0B80034D8D4 /* ScreenshotProcessor.swift */; }; @@ -870,6 +871,7 @@ D586D39A28EF58B0000E101F /* AltTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AltTests.swift; sourceTree = ""; }; D58916FD28C7C55C00E39C8B /* LoggedError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoggedError.swift; sourceTree = ""; }; D593F1932717749A006E82DE /* PatchAppOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PatchAppOperation.swift; sourceTree = ""; }; + D5ACE84428E3B8450021CAB9 /* ClearAppCacheOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClearAppCacheOperation.swift; sourceTree = ""; }; D5CA0C4A280E141900469595 /* ManagedPatron.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ManagedPatron.swift; sourceTree = ""; }; D5CA0C4C280E242500469595 /* AltStore 10.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "AltStore 10.xcdatamodel"; sourceTree = ""; }; D5CA0C4D280E249E00469595 /* AltStore9ToAltStore10.xcmappingmodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcmappingmodel; path = AltStore9ToAltStore10.xcmappingmodel; sourceTree = ""; }; @@ -1760,6 +1762,7 @@ D57F2C9026E0070200B9FA39 /* EnableJITOperation.swift */, D5DAE0952804DF430034D8D4 /* UpdatePatronsOperation.swift */, D5E1E7C028077DE90016FC96 /* FetchTrustedSourcesOperation.swift */, + D5ACE84428E3B8450021CAB9 /* ClearAppCacheOperation.swift */, BF7B44062725A4B8005288A4 /* Patch App */, ); path = Operations; @@ -2748,6 +2751,7 @@ BFD6B03322DFF20800B86064 /* MyAppsComponents.swift in Sources */, BF41B808233433C100C593A3 /* LoadingState.swift in Sources */, BFF0B69A2322D7D0007A79E1 /* UIScreen+CompactHeight.swift in Sources */, + D5ACE84528E3B8450021CAB9 /* ClearAppCacheOperation.swift in Sources */, D5F2F6A92720B7C20081CCF5 /* PatchViewController.swift in Sources */, BF8F69C222E659F700049BA1 /* AppContentViewController.swift in Sources */, BF08858522DE7EC800DE9F1E /* UpdateCollectionViewCell.swift in Sources */, diff --git a/AltStore/Managing Apps/AppManager.swift b/AltStore/Managing Apps/AppManager.swift index f1417aae..6425ac60 100644 --- a/AltStore/Managing Apps/AppManager.swift +++ b/AltStore/Managing Apps/AppManager.swift @@ -344,6 +344,16 @@ extension AppManager presentingViewController.present(alertController, animated: true, completion: nil) } } + + func clearAppCache(completion: @escaping (Result) -> Void) + { + let clearAppCacheOperation = ClearAppCacheOperation() + clearAppCacheOperation.resultHandler = { result in + completion(result) + } + + self.run([clearAppCacheOperation], context: nil) + } } extension AppManager @@ -822,6 +832,12 @@ extension AppManager let progress = self.refreshProgress[app.bundleIdentifier] return progress } + + func isActivelyManagingApp(withBundleID bundleID: String) -> Bool + { + let isActivelyManaging = self.installationProgress.keys.contains(bundleID) || self.refreshProgress.keys.contains(bundleID) + return isActivelyManaging + } } extension AppManager @@ -889,12 +905,6 @@ private extension AppManager } } - func isActivelyManagingApp(withBundleID bundleID: String) -> Bool - { - let isActivelyManaging = self.installationProgress.keys.contains(bundleID) || self.refreshProgress.keys.contains(bundleID) - return isActivelyManaging - } - @discardableResult private func perform(_ operations: [AppOperation], presentingViewController: UIViewController?, group: RefreshGroup) -> RefreshGroup { diff --git a/AltStore/Operations/ClearAppCacheOperation.swift b/AltStore/Operations/ClearAppCacheOperation.swift new file mode 100644 index 00000000..614dc75f --- /dev/null +++ b/AltStore/Operations/ClearAppCacheOperation.swift @@ -0,0 +1,203 @@ +// +// ClearAppCacheOperation.swift +// AltStore +// +// Created by Riley Testut on 9/27/22. +// Copyright © 2022 Riley Testut. All rights reserved. +// + +import Foundation +import AltStoreCore + +struct BatchError: ALTLocalizedError +{ + enum Code: Int, ALTErrorCode + { + typealias Error = BatchError + + case batchError + } + + var code: Code = .batchError + var underlyingErrors: [Error] + + var errorTitle: String? + var errorFailure: String? + + init(errors: [Error]) + { + self.underlyingErrors = errors + } + + var errorFailureReason: String { + guard !self.underlyingErrors.isEmpty else { return NSLocalizedString("An unknown error occured.", comment: "") } + + let errorMessages = self.underlyingErrors.map { $0.localizedDescription } + + let message = errorMessages.joined(separator: "\n\n") + return message + } +} + +@objc(ClearAppCacheOperation) +class ClearAppCacheOperation: ResultOperation +{ + private let coordinator = NSFileCoordinator() + private let coordinatorQueue = OperationQueue() + + override init() + { + self.coordinatorQueue.name = "AltStore - ClearAppCacheOperation Queue" + } + + override func main() + { + super.main() + + var allErrors = [Error]() + + self.clearTemporaryDirectory { result in + switch result + { + case .failure(let batchError as BatchError): allErrors.append(contentsOf: batchError.underlyingErrors) + case .failure(let error): allErrors.append(error) + case .success: break + } + + self.removeUninstalledAppBackupDirectories { result in + switch result + { + case .failure(let batchError as BatchError): allErrors.append(contentsOf: batchError.underlyingErrors) + case .failure(let error): allErrors.append(error) + case .success: break + } + + if allErrors.isEmpty + { + self.finish(.success(())) + } + else + { + let error = BatchError(errors: allErrors) + self.finish(.failure(error)) + } + } + } + } +} + +private extension ClearAppCacheOperation +{ + func clearTemporaryDirectory(completion: @escaping (Result) -> Void) + { + let intent = NSFileAccessIntent.writingIntent(with: FileManager.default.temporaryDirectory, options: [.forDeleting]) + self.coordinator.coordinate(with: [intent], queue: self.coordinatorQueue) { (error) in + do + { + if let error + { + throw error + } + + let fileURLs = try FileManager.default.contentsOfDirectory(at: intent.url, + includingPropertiesForKeys: [], + options: [.skipsSubdirectoryDescendants, .skipsHiddenFiles]) + var errors = [Error]() + + for fileURL in fileURLs + { + do + { + print("[ALTLog] Removing item from temporary directory:", fileURL.lastPathComponent) + try FileManager.default.removeItem(at: fileURL) + } + catch + { + print("[ALTLog] Failed to remove \(fileURL.lastPathComponent) from temporary directory.", error) + errors.append(error) + } + } + + if !errors.isEmpty + { + let error = BatchError(errors: errors) + completion(.failure(error)) + } + else + { + completion(.success(())) + } + } + catch + { + completion(.failure(error)) + } + } + } + + func removeUninstalledAppBackupDirectories(completion: @escaping (Result) -> Void) + { + guard let backupsDirectory = FileManager.default.appBackupsDirectory else { return completion(.failure(OperationError.missingAppGroup)) } + + DatabaseManager.shared.persistentContainer.performBackgroundTask { context in + let installedAppBundleIDs = Set(InstalledApp.all(in: context).map { $0.bundleIdentifier }) + + let intent = NSFileAccessIntent.writingIntent(with: backupsDirectory, options: [.forDeleting]) + self.coordinator.coordinate(with: [intent], queue: self.coordinatorQueue) { (error) in + do + { + if let error + { + throw error + } + + var isDirectory: ObjCBool = false + guard FileManager.default.fileExists(atPath: intent.url.path, isDirectory: &isDirectory), isDirectory.boolValue else { + completion(.success(())) + return + } + + let fileURLs = try FileManager.default.contentsOfDirectory(at: intent.url, + includingPropertiesForKeys: [.isDirectoryKey, .nameKey], + options: [.skipsSubdirectoryDescendants, .skipsHiddenFiles]) + var errors = [Error]() + + for backupDirectory in fileURLs + { + do + { + let resourceValues = try backupDirectory.resourceValues(forKeys: [.isDirectoryKey, .nameKey]) + guard let isDirectory = resourceValues.isDirectory, let bundleID = resourceValues.name else { continue } + + if isDirectory && !installedAppBundleIDs.contains(bundleID) && !AppManager.shared.isActivelyManagingApp(withBundleID: bundleID) + { + print("[ALTLog] Removing backup directory for uninstalled app:", bundleID) + try FileManager.default.removeItem(at: backupDirectory) + } + } + catch + { + print("[ALTLog] Failed to remove app backup directory:", error) + errors.append(error) + } + } + + if !errors.isEmpty + { + let error = BatchError(errors: errors) + completion(.failure(error)) + } + else + { + completion(.success(())) + } + } + catch + { + print("[ALTLog] Failed to remove app backup directory:", error) + completion(.failure(error)) + } + } + } + } +} diff --git a/AltStore/Settings/Settings.storyboard b/AltStore/Settings/Settings.storyboard index 90460b79..684032b8 100644 --- a/AltStore/Settings/Settings.storyboard +++ b/AltStore/Settings/Settings.storyboard @@ -344,6 +344,34 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/AltStore/Settings/SettingsViewController.swift b/AltStore/Settings/SettingsViewController.swift index f1ebef70..550e79dc 100644 --- a/AltStore/Settings/SettingsViewController.swift +++ b/AltStore/Settings/SettingsViewController.swift @@ -50,6 +50,12 @@ extension SettingsViewController case softwareLicenses } + fileprivate enum TechyThingsRow: Int, CaseIterable + { + case errorLog + case clearCache + } + fileprivate enum DebugRow: Int, CaseIterable { case sendFeedback @@ -327,6 +333,34 @@ private extension SettingsViewController self.present(viewController, animated: true, completion: nil) } + func clearCache() + { + let alertController = UIAlertController(title: NSLocalizedString("Are you sure you want to clear AltStore's cache?", comment: ""), + message: NSLocalizedString("This will remove all temporary files as well as backups for uninstalled apps.", comment: ""), + preferredStyle: .actionSheet) + alertController.addAction(UIAlertAction(title: UIAlertAction.cancel.title, style: UIAlertAction.cancel.style) { [weak self] _ in + self?.tableView.indexPathForSelectedRow.map { self?.tableView.deselectRow(at: $0, animated: true) } + }) + alertController.addAction(UIAlertAction(title: NSLocalizedString("Clear Cache", comment: ""), style: .destructive) { [weak self] _ in + AppManager.shared.clearAppCache { result in + DispatchQueue.main.async { + self?.tableView.indexPathForSelectedRow.map { self?.tableView.deselectRow(at: $0, animated: true) } + + switch result + { + case .success: break + case .failure(let error): + let alertController = UIAlertController(title: NSLocalizedString("Unable to Clear Cache", comment: ""), message: error.localizedDescription, preferredStyle: .alert) + alertController.addAction(.ok) + self?.present(alertController, animated: true) + } + } + } + }) + + self.present(alertController, animated: true) + } + @IBAction func handleDebugModeGesture(_ gestureRecognizer: UISwipeGestureRecognizer) { self.debugGestureCounter += 1 @@ -521,6 +555,14 @@ extension SettingsViewController self.addRefreshAppsShortcut() } + case .techyThings: + let row = TechyThingsRow.allCases[indexPath.row] + switch row + { + case .errorLog: break + case .clearCache: self.clearCache() + } + case .credits: let row = CreditsRow.allCases[indexPath.row] switch row @@ -562,7 +604,7 @@ extension SettingsViewController case .refreshAttempts: break } - case .account, .patreon, .instructions, .techyThings, .macDirtyCow: break + case .account, .patreon, .instructions, .macDirtyCow: break } } } From 053cc9fedae793f742f5b741ea55ed026535e7ec Mon Sep 17 00:00:00 2001 From: Riley Testut Date: Tue, 7 Feb 2023 16:35:15 -0600 Subject: [PATCH 04/14] =?UTF-8?q?Force-enables=20=E2=80=9CEnforce=203-App?= =?UTF-8?q?=20Limit=E2=80=9D=20if=20iOS=20version=20does=20not=20support?= =?UTF-8?q?=20MacDirtyCow=20exploit?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Prevents “Enforce 3-App Limit” remaining enabled after updating iOS version without a way to disable it. --- AltStore/Settings/SettingsViewController.swift | 6 ++---- AltStoreCore/Extensions/UserDefaults+AltStore.swift | 11 +++++++++++ 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/AltStore/Settings/SettingsViewController.swift b/AltStore/Settings/SettingsViewController.swift index 550e79dc..3df54d91 100644 --- a/AltStore/Settings/SettingsViewController.swift +++ b/AltStore/Settings/SettingsViewController.swift @@ -250,10 +250,8 @@ private extension SettingsViewController switch section { case .macDirtyCow: - let ios16_2 = OperatingSystemVersion(majorVersion: 16, minorVersion: 2, patchVersion: 0) - - let isMacDirtyCowExploitSupported = !ProcessInfo.processInfo.isOperatingSystemAtLeast(ios16_2) - return !(isMacDirtyCowExploitSupported && UserDefaults.standard.isDebugModeEnabled) + let isHidden = !(UserDefaults.standard.isMacDirtyCowSupported && UserDefaults.standard.isDebugModeEnabled) + return isHidden default: return false } diff --git a/AltStoreCore/Extensions/UserDefaults+AltStore.swift b/AltStoreCore/Extensions/UserDefaults+AltStore.swift index 0f3c512f..a5d98e55 100644 --- a/AltStoreCore/Extensions/UserDefaults+AltStore.swift +++ b/AltStoreCore/Extensions/UserDefaults+AltStore.swift @@ -60,6 +60,7 @@ public extension UserDefaults @NSManaged @objc(activeAppsLimit) private var _activeAppsLimit: NSNumber? @NSManaged var ignoreActiveAppsLimit: Bool + @NSManaged var isMacDirtyCowSupported: Bool class func registerDefaults() { @@ -70,6 +71,9 @@ public extension UserDefaults let ios14 = OperatingSystemVersion(majorVersion: 14, minorVersion: 0, patchVersion: 0) let localServerSupportsRefreshing = !ProcessInfo.processInfo.isOperatingSystemAtLeast(ios14) + let ios16_2 = OperatingSystemVersion(majorVersion: 16, minorVersion: 2, patchVersion: 0) + let isMacDirtyCowSupported = ProcessInfo.processInfo.isOperatingSystemAtLeast(ios14) && !ProcessInfo.processInfo.isOperatingSystemAtLeast(ios16_2) // MacDirtyCow supports iOS 14.0 - 16.1.2 + let defaults = [ #keyPath(UserDefaults.isBackgroundRefreshEnabled): true, #keyPath(UserDefaults.isLegacyDeactivationSupported): isLegacyDeactivationSupported, @@ -77,9 +81,16 @@ public extension UserDefaults #keyPath(UserDefaults.localServerSupportsRefreshing): localServerSupportsRefreshing, #keyPath(UserDefaults.requiresAppGroupMigration): true, #keyPath(UserDefaults.ignoreActiveAppsLimit): false, + #keyPath(UserDefaults.isMacDirtyCowSupported): isMacDirtyCowSupported, ] UserDefaults.standard.register(defaults: defaults) UserDefaults.shared.register(defaults: defaults) + + if !isMacDirtyCowSupported + { + // Disable ignoreActiveAppsLimit if running iOS version that doesn't support MacDirtyCow. + UserDefaults.standard.ignoreActiveAppsLimit = false + } } } From 46341773e65c3672ea0aaaa951522b5d921aee5a Mon Sep 17 00:00:00 2001 From: Riley Testut Date: Tue, 7 Feb 2023 16:37:10 -0600 Subject: [PATCH 05/14] Updates app version to 1.6.1b --- AltStore.xcodeproj/project.pbxproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/AltStore.xcodeproj/project.pbxproj b/AltStore.xcodeproj/project.pbxproj index 2b140712..a1594ba8 100644 --- a/AltStore.xcodeproj/project.pbxproj +++ b/AltStore.xcodeproj/project.pbxproj @@ -3540,7 +3540,7 @@ "$(PROJECT_DIR)/Dependencies/fragmentzip", "$(PROJECT_DIR)/Dependencies/libcurl", ); - MARKETING_VERSION = 1.6; + MARKETING_VERSION = 1.6.1b; PRODUCT_BUNDLE_IDENTIFIER = com.rileytestut.AltStore; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -3574,7 +3574,7 @@ "$(PROJECT_DIR)/Dependencies/fragmentzip", "$(PROJECT_DIR)/Dependencies/libcurl", ); - MARKETING_VERSION = 1.6; + MARKETING_VERSION = 1.6.1b; PRODUCT_BUNDLE_IDENTIFIER = com.rileytestut.AltStore; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; From 94a8d79a5f8d7a5636dc607e169030c1b1079e03 Mon Sep 17 00:00:00 2001 From: Riley Testut Date: Wed, 8 Feb 2023 13:01:49 -0600 Subject: [PATCH 06/14] =?UTF-8?q?Fixes=20incorrect=20=E2=80=9CView=20Error?= =?UTF-8?q?=20Log=E2=80=9D=20cell=20appearance?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AltStore/Settings/Settings.storyboard | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AltStore/Settings/Settings.storyboard b/AltStore/Settings/Settings.storyboard index 684032b8..5fa23e6d 100644 --- a/AltStore/Settings/Settings.storyboard +++ b/AltStore/Settings/Settings.storyboard @@ -336,7 +336,7 @@ - + From 7c1b951098a029d8315cd9bcd9978986d3cf44e3 Mon Sep 17 00:00:00 2001 From: Riley Testut Date: Wed, 8 Feb 2023 13:06:44 -0600 Subject: [PATCH 07/14] Fixes SourcesViewController crash on iOS 12 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Apparently changing NSLayoutConstraint priorities from required to optional (and vice versa) isn’t supported, even though it works on iOS 13+. Who knew! --- AltStore/Base.lproj/Main.storyboard | 8 ++++---- AltStore/Sources/SourcesViewController.swift | 11 ++++++----- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/AltStore/Base.lproj/Main.storyboard b/AltStore/Base.lproj/Main.storyboard index ae81859b..c4d8ce11 100644 --- a/AltStore/Base.lproj/Main.storyboard +++ b/AltStore/Base.lproj/Main.storyboard @@ -1002,10 +1002,10 @@ World - - - - + + + + diff --git a/AltStore/Sources/SourcesViewController.swift b/AltStore/Sources/SourcesViewController.swift index 584995c5..33cceab6 100644 --- a/AltStore/Sources/SourcesViewController.swift +++ b/AltStore/Sources/SourcesViewController.swift @@ -480,11 +480,12 @@ extension SourcesViewController: UICollectionViewDelegateFlowLayout headerView.layoutMargins.left = self.view.layoutMargins.left headerView.layoutMargins.right = self.view.layoutMargins.right - let almostRequiredPriority = UILayoutPriority(UILayoutPriority.required.rawValue - 1) // Can't be required or else we can't satisfy constraints when hidden (size = 0). - headerView.leadingLayoutConstraint?.priority = almostRequiredPriority - headerView.trailingLayoutConstraint?.priority = almostRequiredPriority - headerView.topLayoutConstraint?.priority = almostRequiredPriority - headerView.bottomLayoutConstraint?.priority = almostRequiredPriority + /* Changing NSLayoutConstraint priorities from required to optional (and vice versa) isn’t supported, and crashes on iOS 12. */ + // let almostRequiredPriority = UILayoutPriority(UILayoutPriority.required.rawValue - 1) // Can't be required or else we can't satisfy constraints when hidden (size = 0). + // headerView.leadingLayoutConstraint?.priority = almostRequiredPriority + // headerView.trailingLayoutConstraint?.priority = almostRequiredPriority + // headerView.topLayoutConstraint?.priority = almostRequiredPriority + // headerView.bottomLayoutConstraint?.priority = almostRequiredPriority switch kind { From c6476b6b4a5a4f15576b3c1e67d23c7ac3692ce3 Mon Sep 17 00:00:00 2001 From: Riley Testut Date: Wed, 8 Feb 2023 13:47:54 -0600 Subject: [PATCH 08/14] Hides MacDirtyCow settings on iOS 15.7.2 MacDirtyCow supports iOS 14.0 - 15.7.1 and 16.0 - 16.1.2, but not 15.7.2 --- AltStoreCore/Extensions/UserDefaults+AltStore.swift | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/AltStoreCore/Extensions/UserDefaults+AltStore.swift b/AltStoreCore/Extensions/UserDefaults+AltStore.swift index a5d98e55..b778d2a6 100644 --- a/AltStoreCore/Extensions/UserDefaults+AltStore.swift +++ b/AltStoreCore/Extensions/UserDefaults+AltStore.swift @@ -71,8 +71,14 @@ public extension UserDefaults let ios14 = OperatingSystemVersion(majorVersion: 14, minorVersion: 0, patchVersion: 0) let localServerSupportsRefreshing = !ProcessInfo.processInfo.isOperatingSystemAtLeast(ios14) + let ios16 = OperatingSystemVersion(majorVersion: 16, minorVersion: 0, patchVersion: 0) let ios16_2 = OperatingSystemVersion(majorVersion: 16, minorVersion: 2, patchVersion: 0) - let isMacDirtyCowSupported = ProcessInfo.processInfo.isOperatingSystemAtLeast(ios14) && !ProcessInfo.processInfo.isOperatingSystemAtLeast(ios16_2) // MacDirtyCow supports iOS 14.0 - 16.1.2 + let ios15_7_2 = OperatingSystemVersion(majorVersion: 15, minorVersion: 7, patchVersion: 2) + + // MacDirtyCow supports iOS 14.0 - 15.7.1 OR 16.0 - 16.1.2 + let isMacDirtyCowSupported = + (ProcessInfo.processInfo.isOperatingSystemAtLeast(ios14) && !ProcessInfo.processInfo.isOperatingSystemAtLeast(ios15_7_2)) || + (ProcessInfo.processInfo.isOperatingSystemAtLeast(ios16) && !ProcessInfo.processInfo.isOperatingSystemAtLeast(ios16_2)) let defaults = [ #keyPath(UserDefaults.isBackgroundRefreshEnabled): true, From 913db5131be741a8d2f498b85a426ac2e43cf8c5 Mon Sep 17 00:00:00 2001 From: Riley Testut Date: Wed, 8 Feb 2023 13:50:29 -0600 Subject: [PATCH 09/14] =?UTF-8?q?Adds=20=E2=80=9CClear=20Cache=E2=80=9D=20?= =?UTF-8?q?description=20to=20Techy=20Things=20section=20footer?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AltStore/Settings/SettingsViewController.swift | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/AltStore/Settings/SettingsViewController.swift b/AltStore/Settings/SettingsViewController.swift index 3df54d91..f175aba1 100644 --- a/AltStore/Settings/SettingsViewController.swift +++ b/AltStore/Settings/SettingsViewController.swift @@ -213,7 +213,14 @@ private extension SettingsViewController break case .techyThings: - settingsHeaderFooterView.primaryLabel.text = NSLocalizedString("TECHY THINGS", comment: "") + if isHeader + { + settingsHeaderFooterView.primaryLabel.text = NSLocalizedString("TECHY THINGS", comment: "") + } + else + { + settingsHeaderFooterView.secondaryLabel.text = NSLocalizedString("Free up disk space by removing non-essential data, such as temporary files and backups for uninstalled apps.", comment: "") + } case .credits: settingsHeaderFooterView.primaryLabel.text = NSLocalizedString("CREDITS", comment: "") @@ -493,12 +500,12 @@ extension SettingsViewController { case _ where isSectionHidden(section): return nil case .signIn where self.activeTeam != nil: return nil - case .signIn, .patreon, .appRefresh, .macDirtyCow: + case .signIn, .patreon, .appRefresh, .techyThings, .macDirtyCow: let footerView = tableView.dequeueReusableHeaderFooterView(withIdentifier: "HeaderFooterView") as! SettingsHeaderFooterView self.prepare(footerView, for: section, isHeader: false) return footerView - case .account, .credits, .debug, .instructions, .techyThings: return nil + case .account, .credits, .debug, .instructions: return nil } } @@ -526,11 +533,11 @@ extension SettingsViewController case _ where isSectionHidden(section): return 1.0 case .signIn where self.activeTeam != nil: return 1.0 case .account where self.activeTeam == nil: return 1.0 - case .signIn, .patreon, .appRefresh, .macDirtyCow: + case .signIn, .patreon, .appRefresh, .techyThings, .macDirtyCow: let height = self.preferredHeight(for: self.prototypeHeaderFooterView, in: section, isHeader: false) return height - case .account, .credits, .debug, .instructions, .techyThings: return 0.0 + case .account, .credits, .debug, .instructions: return 0.0 } } } From 28d7fd0f6c33ebde70f7100a1951f7f79d348fe8 Mon Sep 17 00:00:00 2001 From: Riley Testut Date: Wed, 8 Feb 2023 14:25:11 -0600 Subject: [PATCH 10/14] [Apps] Updates AltStore beta to 1.6.1b --- AltStore/Resources/apps.json | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/AltStore/Resources/apps.json b/AltStore/Resources/apps.json index d5dd6eed..b412c4a1 100644 --- a/AltStore/Resources/apps.json +++ b/AltStore/Resources/apps.json @@ -46,6 +46,14 @@ "developerName": "Riley Testut", "subtitle": "An alternative App Store for iOS.", "versions": [ + { + "version": "1.6.1b", + "date": "2023-02-09T11:00:00-06:00", + "localizedDescription": "• Sideload more than 3 apps via MacDirtyCow exploit*\n• “Clear Cache” button removes non-essential data to free up disk space\n• Fixes crash when viewing Sources on iOS 12\n\n*Requires iOS 14.0 - 15.7.1 or 16.0 - 16.1.2. See Patreon post for instructions.", + "downloadURL": "https://cdn.altstore.io/file/altstore/apps/altstore/1_6_1_b.ipa", + "size": 5627282, + "minOSVersion": "12.2" + }, { "version": "1.6rc3", "date": "2023-01-23T11:30:00-06:00", @@ -78,14 +86,14 @@ "size": 5608964 } ], - "version": "1.6rc2", - "versionDate": "2023-01-18T11:30:00-06:00", - "versionDescription": "• Fixed outdated AltStore updates appearing in My Apps tab\n\nPREVIOUS RC\n• Moved Error Log from \"Debug\" settings to \"Techy Things\"\n• Improved misc. error messages\n• Fixed repeated \"AltServer could not be found\" notifications\n• Fixed missing metadata for errors received from AltServer\n• Fixed decoding nested error metadata values", - "downloadURL": "https://cdn.altstore.io/file/altstore/apps/altstore/1_6_rc2.ipa", + "version": "1.6.1b", + "versionDate": "2023-02-09T11:00:00-06:00", + "versionDescription": "• Sideload more than 3 apps via MacDirtyCow exploit*\n• “Clear Cache” button removes non-essential data to free up disk space\n• Fixes crash when viewing Sources on iOS 12\n\n*Requires iOS 14.0 - 15.7.1 or 16.0 - 16.1.2. See Patreon post for instructions.", + "downloadURL": "https://cdn.altstore.io/file/altstore/apps/altstore/1_6_1_b.ipa", "localizedDescription": "AltStore is an alternative app store for non-jailbroken devices. \n\nThis beta release of AltStore adds support for 3rd party sources, allowing you to download apps from other developers directly through AltStore.", "iconURL": "https://user-images.githubusercontent.com/705880/65270980-1eb96f80-dad1-11e9-9367-78ccd25ceb02.png", "tintColor": "018084", - "size": 5614213, + "size": 5627282, "beta": true, "screenshotURLs": [ "https://user-images.githubusercontent.com/705880/78942028-acf54300-7a6d-11ea-821c-5bb7a9b3e73a.PNG", From e93457de2fe96a62540ce163738fdcdf8b75def2 Mon Sep 17 00:00:00 2001 From: Riley Testut Date: Tue, 14 Feb 2023 14:07:48 -0800 Subject: [PATCH 11/14] Fixes triggering false positives with some malware detectors Renames UserDefaults.isMacDirtyCowSupported to .isCowExploitSupported --- AltStore/Settings/SettingsViewController.swift | 2 +- AltStoreCore/Extensions/UserDefaults+AltStore.swift | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/AltStore/Settings/SettingsViewController.swift b/AltStore/Settings/SettingsViewController.swift index f175aba1..5d97e065 100644 --- a/AltStore/Settings/SettingsViewController.swift +++ b/AltStore/Settings/SettingsViewController.swift @@ -257,7 +257,7 @@ private extension SettingsViewController switch section { case .macDirtyCow: - let isHidden = !(UserDefaults.standard.isMacDirtyCowSupported && UserDefaults.standard.isDebugModeEnabled) + let isHidden = !(UserDefaults.standard.isCowExploitSupported && UserDefaults.standard.isDebugModeEnabled) return isHidden default: return false diff --git a/AltStoreCore/Extensions/UserDefaults+AltStore.swift b/AltStoreCore/Extensions/UserDefaults+AltStore.swift index b778d2a6..a59e7a3b 100644 --- a/AltStoreCore/Extensions/UserDefaults+AltStore.swift +++ b/AltStoreCore/Extensions/UserDefaults+AltStore.swift @@ -60,7 +60,9 @@ public extension UserDefaults @NSManaged @objc(activeAppsLimit) private var _activeAppsLimit: NSNumber? @NSManaged var ignoreActiveAppsLimit: Bool - @NSManaged var isMacDirtyCowSupported: Bool + + // Including "MacDirtyCow" in name triggers false positives with malware detectors 🤷‍♂️ + @NSManaged var isCowExploitSupported: Bool class func registerDefaults() { @@ -87,7 +89,7 @@ public extension UserDefaults #keyPath(UserDefaults.localServerSupportsRefreshing): localServerSupportsRefreshing, #keyPath(UserDefaults.requiresAppGroupMigration): true, #keyPath(UserDefaults.ignoreActiveAppsLimit): false, - #keyPath(UserDefaults.isMacDirtyCowSupported): isMacDirtyCowSupported, + #keyPath(UserDefaults.isCowExploitSupported): isMacDirtyCowSupported, ] UserDefaults.standard.register(defaults: defaults) From 696745df4d2e8d8f20424085b6918c10b92e7375 Mon Sep 17 00:00:00 2001 From: Riley Testut Date: Mon, 20 Feb 2023 09:19:48 -0600 Subject: [PATCH 12/14] [Apps] Updates Delta beta to 1.4rc --- AltStore/Resources/apps.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/AltStore/Resources/apps.json b/AltStore/Resources/apps.json index b412c4a1..0ff4b42f 100644 --- a/AltStore/Resources/apps.json +++ b/AltStore/Resources/apps.json @@ -142,14 +142,14 @@ "bundleIdentifier": "com.rileytestut.Delta.Beta", "developerName": "Riley Testut", "subtitle": "Classic games in your pocket.", - "version": "1.4b5", - "versionDate": "2023-02-02T12:00:00-06:00", - "versionDescription": "“Respect Silent Mode” setting\n• When enabled, game audio will only play when your device isn’t muted\n• Automatically mutes game audio if another app is playing audio\n\nCheatBase (thanks Noah Keck @horn978)\n• Browse and add all known cheats for games\n• Limited to DS games for now, but will be expanded in the future", - "downloadURL": "https://cdn.altstore.io/file/altstore/apps/delta/1_4_b5.ipa", + "version": "1.4rc", + "versionDate": "2023-02-13T09:41:00-08:00", + "versionDescription": "\"Contributors\" screen\n• See everyone who has contributed to Delta\n• View associated Pull Requests for contributed features\n\nPREVIOUS BETA\n• “Respect Silent Mode” setting\n• Automatically mutes game audio if another app is playing audio\n• Browse CheatBase for all known cheats for games (thanks Noah Keck @horn978)", + "downloadURL": "https://cdn.altstore.io/file/altstore/apps/delta/1_4_rc.ipa", "localizedDescription": "The next consoles for Delta are coming: this beta version of Delta brings support for playing Nintendo DS and Sega Genesis games!\n\nPlease report any issues you find to support@altstore.io. Thanks!", "iconURL": "https://user-images.githubusercontent.com/705880/63391976-4d311700-c37a-11e9-91a8-4fb0c454413d.png", "tintColor": "8A28F7", - "size": 46505452, + "size": 46299284, "beta": true, "permissions": [ { From 1769b2152a984c1f46251dd3c55e26047f0b1635 Mon Sep 17 00:00:00 2001 From: Riley Testut Date: Mon, 20 Feb 2023 11:47:28 -0600 Subject: [PATCH 13/14] [Apps] Updates AltStore beta to 1.6.1b2 --- AltStore/Resources/apps.json | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/AltStore/Resources/apps.json b/AltStore/Resources/apps.json index 0ff4b42f..7b9dfd6c 100644 --- a/AltStore/Resources/apps.json +++ b/AltStore/Resources/apps.json @@ -46,6 +46,14 @@ "developerName": "Riley Testut", "subtitle": "An alternative App Store for iOS.", "versions": [ + { + "version": "1.6.1b2", + "date": "2023-02-17T12:00:00-06:00", + "localizedDescription": "This beta fixes Windows Defender blocking AltStore from updating.\n\nPREVIOUS BETA:\n• Sideload more than 3 apps via MacDirtyCow exploit*\n• “Clear Cache” button removes non-essential data to free up disk space\n• Fixes crash when viewing Sources on iOS 12\n\n*Requires iOS 14.0 - 15.7.1 or 16.0 - 16.1.2. See Patreon post for instructions.", + "downloadURL": "https://cdn.altstore.io/file/altstore/apps/altstore/1_6_1_b2.ipa", + "size": 5626076, + "minOSVersion": "12.2" + }, { "version": "1.6.1b", "date": "2023-02-09T11:00:00-06:00", @@ -86,14 +94,14 @@ "size": 5608964 } ], - "version": "1.6.1b", - "versionDate": "2023-02-09T11:00:00-06:00", - "versionDescription": "• Sideload more than 3 apps via MacDirtyCow exploit*\n• “Clear Cache” button removes non-essential data to free up disk space\n• Fixes crash when viewing Sources on iOS 12\n\n*Requires iOS 14.0 - 15.7.1 or 16.0 - 16.1.2. See Patreon post for instructions.", - "downloadURL": "https://cdn.altstore.io/file/altstore/apps/altstore/1_6_1_b.ipa", + "version": "1.6.1b2", + "versionDate": "2023-02-17T12:00:00-06:00", + "versionDescription": "This beta fixes Windows Defender blocking AltStore from updating.\n\nPREVIOUS BETA:\n• Sideload more than 3 apps via MacDirtyCow exploit*\n• “Clear Cache” button removes non-essential data to free up disk space\n• Fixes crash when viewing Sources on iOS 12\n\n*Requires iOS 14.0 - 15.7.1 or 16.0 - 16.1.2. See Patreon post for instructions.", + "downloadURL": "https://cdn.altstore.io/file/altstore/apps/altstore/1_6_1_b2.ipa", "localizedDescription": "AltStore is an alternative app store for non-jailbroken devices. \n\nThis beta release of AltStore adds support for 3rd party sources, allowing you to download apps from other developers directly through AltStore.", "iconURL": "https://user-images.githubusercontent.com/705880/65270980-1eb96f80-dad1-11e9-9367-78ccd25ceb02.png", "tintColor": "018084", - "size": 5627282, + "size": 5626076, "beta": true, "screenshotURLs": [ "https://user-images.githubusercontent.com/705880/78942028-acf54300-7a6d-11ea-821c-5bb7a9b3e73a.PNG", From bb90a5709df89d788af81cbd0eda740272b96249 Mon Sep 17 00:00:00 2001 From: Riley Testut Date: Mon, 20 Feb 2023 13:34:09 -0600 Subject: [PATCH 14/14] [Apps] Updates AltStore to 1.6.1 --- AltStore/Resources/apps.json | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/AltStore/Resources/apps.json b/AltStore/Resources/apps.json index 7b9dfd6c..0720e834 100644 --- a/AltStore/Resources/apps.json +++ b/AltStore/Resources/apps.json @@ -8,6 +8,13 @@ "bundleIdentifier": "com.rileytestut.AltStore", "developerName": "Riley Testut", "versions": [ + { + "version": "1.6.1", + "date": "2023-02-20T12:00:00-06:00", + "localizedDescription": "• “Clear Cache” button removes non-essential data to free up disk space\n• Sideload more than 3 apps via MacDirtyCow exploit*†\n• Fixes crash when viewing Sources on iOS 12\n\n*Requires iOS 14.0 - 16.1.2 (excluding 15.7.2). iOS 16.2+ not supported.\n†Visit faq.altstore.io for detailed instructions.\n", + "downloadURL": "https://cdn.altstore.io/file/altstore/apps/altstore/1_6_1.ipa", + "size": 5626583 + }, { "version": "1.6", "date": "2023-01-30T10:00:00-06:00", @@ -16,14 +23,14 @@ "size": 5614798 } ], - "version": "1.6", - "versionDate": "2023-01-30T10:00:00-06:00", - "versionDescription": "NEW\n\nLock Screen Widget (iOS 16+)\n• Counts down days until AltStore expires\n• Available in 2 different styles: \"icon\" and \"text\"\n\nError Log\n• View past errors in more detail\n• Tap an error to copy the error message or error code\n• Search for error code directly in AltStore FAQ\n• Access via new \"Techy Things\" section in settings\n\niOS Version Compatibility\n• Checks iOS compatibility for updates and installs \n• Asks user to install latest compatible version if not supported\n• View unsupported updates from My Apps tab\n\nRevamped Error Handling\n• Completely revamped error handling to make debugging issues as easy as possible\n• Revised error messages to be more descriptive and provide more suggestions\n• Includes more information with errors\n\nMisc.\n• Pull-to-refresh to check for updates\n• Supports new “versions” key in source JSON\n• Supports updating apps from app detail page\n\nFIXED\n• Fixed \"more\" button appearing by mistake on app detail pages\n• Fixed widgets potentially not updating after refreshing apps\n• Fixed incorrect app name and iOS version in \"Unsupported iOS version\" error message\n• Fixed refreshing tweaked apps with removed app extensions\n• Fixed incorrect error toast view color when sign-in fails", - "downloadURL": "https://cdn.altstore.io/file/altstore/apps/altstore/1_6.ipa", + "version": "1.6.1", + "versionDate": "2023-02-20T12:00:00-06:00", + "versionDescription": "• “Clear Cache” button removes non-essential data to free up disk space\n• Sideload more than 3 apps via MacDirtyCow exploit*†\n• Fixes crash when viewing Sources on iOS 12\n\n*Requires iOS 14.0 - 16.1.2 (excluding 15.7.2). iOS 16.2+ not supported.\n†Visit faq.altstore.io for detailed instructions.\n", + "downloadURL": "https://cdn.altstore.io/file/altstore/apps/altstore/1_6_1.ipa", "localizedDescription": "AltStore is an alternative app store for non-jailbroken devices. \n\nThis version of AltStore allows you to install Delta, an all-in-one emulator for iOS, as well as sideload other .ipa files from the Files app.", "iconURL": "https://user-images.githubusercontent.com/705880/65270980-1eb96f80-dad1-11e9-9367-78ccd25ceb02.png", "tintColor": "018084", - "size": 5614798, + "size": 5626583, "screenshotURLs": [ "https://user-images.githubusercontent.com/705880/78942028-acf54300-7a6d-11ea-821c-5bb7a9b3e73a.PNG", "https://user-images.githubusercontent.com/705880/78942222-0fe6da00-7a6e-11ea-9f2a-dda16157583c.PNG",