Skip to content

Commit dabe743

Browse files
authored
Merge pull request altstoreio#1161 from altstoreio/1.6.1
AltStore 1.6.1
2 parents 0eeee69 + bb90a57 commit dabe743

File tree

13 files changed

+489
-47
lines changed

13 files changed

+489
-47
lines changed

AltStore.xcodeproj/project.pbxproj

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,7 @@
369369
D58916FE28C7C55C00E39C8B /* LoggedError.swift in Sources */ = {isa = PBXBuildFile; fileRef = D58916FD28C7C55C00E39C8B /* LoggedError.swift */; };
370370
D58D5F2E26DFE68E00E55E38 /* LaunchAtLogin in Frameworks */ = {isa = PBXBuildFile; productRef = D58D5F2D26DFE68E00E55E38 /* LaunchAtLogin */; };
371371
D593F1942717749A006E82DE /* PatchAppOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = D593F1932717749A006E82DE /* PatchAppOperation.swift */; };
372+
D5ACE84528E3B8450021CAB9 /* ClearAppCacheOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5ACE84428E3B8450021CAB9 /* ClearAppCacheOperation.swift */; };
372373
D5CA0C4B280E141900469595 /* ManagedPatron.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5CA0C4A280E141900469595 /* ManagedPatron.swift */; };
373374
D5CA0C4E280E249E00469595 /* AltStore9ToAltStore10.xcmappingmodel in Sources */ = {isa = PBXBuildFile; fileRef = D5CA0C4D280E249E00469595 /* AltStore9ToAltStore10.xcmappingmodel */; };
374375
D5DAE0942804B0B80034D8D4 /* ScreenshotProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5DAE0932804B0B80034D8D4 /* ScreenshotProcessor.swift */; };
@@ -870,6 +871,7 @@
870871
D586D39A28EF58B0000E101F /* AltTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AltTests.swift; sourceTree = "<group>"; };
871872
D58916FD28C7C55C00E39C8B /* LoggedError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoggedError.swift; sourceTree = "<group>"; };
872873
D593F1932717749A006E82DE /* PatchAppOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PatchAppOperation.swift; sourceTree = "<group>"; };
874+
D5ACE84428E3B8450021CAB9 /* ClearAppCacheOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClearAppCacheOperation.swift; sourceTree = "<group>"; };
873875
D5CA0C4A280E141900469595 /* ManagedPatron.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ManagedPatron.swift; sourceTree = "<group>"; };
874876
D5CA0C4C280E242500469595 /* AltStore 10.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "AltStore 10.xcdatamodel"; sourceTree = "<group>"; };
875877
D5CA0C4D280E249E00469595 /* AltStore9ToAltStore10.xcmappingmodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcmappingmodel; path = AltStore9ToAltStore10.xcmappingmodel; sourceTree = "<group>"; };
@@ -1760,6 +1762,7 @@
17601762
D57F2C9026E0070200B9FA39 /* EnableJITOperation.swift */,
17611763
D5DAE0952804DF430034D8D4 /* UpdatePatronsOperation.swift */,
17621764
D5E1E7C028077DE90016FC96 /* FetchTrustedSourcesOperation.swift */,
1765+
D5ACE84428E3B8450021CAB9 /* ClearAppCacheOperation.swift */,
17631766
BF7B44062725A4B8005288A4 /* Patch App */,
17641767
);
17651768
path = Operations;
@@ -2748,6 +2751,7 @@
27482751
BFD6B03322DFF20800B86064 /* MyAppsComponents.swift in Sources */,
27492752
BF41B808233433C100C593A3 /* LoadingState.swift in Sources */,
27502753
BFF0B69A2322D7D0007A79E1 /* UIScreen+CompactHeight.swift in Sources */,
2754+
D5ACE84528E3B8450021CAB9 /* ClearAppCacheOperation.swift in Sources */,
27512755
D5F2F6A92720B7C20081CCF5 /* PatchViewController.swift in Sources */,
27522756
BF8F69C222E659F700049BA1 /* AppContentViewController.swift in Sources */,
27532757
BF08858522DE7EC800DE9F1E /* UpdateCollectionViewCell.swift in Sources */,
@@ -3536,7 +3540,7 @@
35363540
"$(PROJECT_DIR)/Dependencies/fragmentzip",
35373541
"$(PROJECT_DIR)/Dependencies/libcurl",
35383542
);
3539-
MARKETING_VERSION = 1.6;
3543+
MARKETING_VERSION = 1.6.1b;
35403544
PRODUCT_BUNDLE_IDENTIFIER = com.rileytestut.AltStore;
35413545
PRODUCT_NAME = "$(TARGET_NAME)";
35423546
PROVISIONING_PROFILE_SPECIFIER = "";
@@ -3570,7 +3574,7 @@
35703574
"$(PROJECT_DIR)/Dependencies/fragmentzip",
35713575
"$(PROJECT_DIR)/Dependencies/libcurl",
35723576
);
3573-
MARKETING_VERSION = 1.6;
3577+
MARKETING_VERSION = 1.6.1b;
35743578
PRODUCT_BUNDLE_IDENTIFIER = com.rileytestut.AltStore;
35753579
PRODUCT_NAME = "$(TARGET_NAME)";
35763580
PROVISIONING_PROFILE_SPECIFIER = "";

AltStore/Base.lproj/Main.storyboard

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1002,10 +1002,10 @@ World</string>
10021002
</label>
10031003
</subviews>
10041004
<constraints>
1005-
<constraint firstItem="TZv-TM-uJj" firstAttribute="top" secondItem="8N7-JY-mcA" secondAttribute="top" constant="14" id="2zE-UV-24S"/>
1006-
<constraint firstAttribute="bottom" secondItem="TZv-TM-uJj" secondAttribute="bottom" constant="15" id="Aml-PC-dko"/>
1007-
<constraint firstAttribute="trailingMargin" secondItem="TZv-TM-uJj" secondAttribute="trailing" id="V0U-al-5eb"/>
1008-
<constraint firstAttribute="leadingMargin" secondItem="TZv-TM-uJj" secondAttribute="leading" id="aS5-6Y-rMd"/>
1005+
<constraint firstItem="TZv-TM-uJj" firstAttribute="top" secondItem="8N7-JY-mcA" secondAttribute="top" priority="999" constant="14" id="2zE-UV-24S"/>
1006+
<constraint firstAttribute="bottom" secondItem="TZv-TM-uJj" secondAttribute="bottom" priority="999" constant="15" id="Aml-PC-dko"/>
1007+
<constraint firstAttribute="trailingMargin" secondItem="TZv-TM-uJj" secondAttribute="trailing" priority="999" id="V0U-al-5eb"/>
1008+
<constraint firstAttribute="leadingMargin" secondItem="TZv-TM-uJj" secondAttribute="leading" priority="999" id="aS5-6Y-rMd"/>
10091009
</constraints>
10101010
<connections>
10111011
<outlet property="bottomLayoutConstraint" destination="Aml-PC-dko" id="I1s-ae-C8A"/>

AltStore/Managing Apps/AppManager.swift

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,16 @@ extension AppManager
344344
presentingViewController.present(alertController, animated: true, completion: nil)
345345
}
346346
}
347+
348+
func clearAppCache(completion: @escaping (Result<Void, Error>) -> Void)
349+
{
350+
let clearAppCacheOperation = ClearAppCacheOperation()
351+
clearAppCacheOperation.resultHandler = { result in
352+
completion(result)
353+
}
354+
355+
self.run([clearAppCacheOperation], context: nil)
356+
}
347357
}
348358

349359
extension AppManager
@@ -822,6 +832,12 @@ extension AppManager
822832
let progress = self.refreshProgress[app.bundleIdentifier]
823833
return progress
824834
}
835+
836+
func isActivelyManagingApp(withBundleID bundleID: String) -> Bool
837+
{
838+
let isActivelyManaging = self.installationProgress.keys.contains(bundleID) || self.refreshProgress.keys.contains(bundleID)
839+
return isActivelyManaging
840+
}
825841
}
826842

827843
extension AppManager
@@ -889,12 +905,6 @@ private extension AppManager
889905
}
890906
}
891907

892-
func isActivelyManagingApp(withBundleID bundleID: String) -> Bool
893-
{
894-
let isActivelyManaging = self.installationProgress.keys.contains(bundleID) || self.refreshProgress.keys.contains(bundleID)
895-
return isActivelyManaging
896-
}
897-
898908
@discardableResult
899909
private func perform(_ operations: [AppOperation], presentingViewController: UIViewController?, group: RefreshGroup) -> RefreshGroup
900910
{

AltStore/My Apps/MyAppsViewController.swift

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -991,7 +991,7 @@ private extension MyAppsViewController
991991

992992
@objc func presentInactiveAppsAlert()
993993
{
994-
let message: String
994+
var message: String
995995

996996
if UserDefaults.standard.activeAppLimitIncludesExtensions
997997
{
@@ -1000,6 +1000,12 @@ private extension MyAppsViewController
10001000
else
10011001
{
10021002
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: "")
1003+
1004+
if UserDefaults.standard.ignoreActiveAppsLimit
1005+
{
1006+
message += "\n\n"
1007+
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: "")
1008+
}
10031009
}
10041010

10051011
let alertController = UIAlertController(title: NSLocalizedString("What are inactive apps?", comment: ""), message: message, preferredStyle: .alert)

AltStore/Operations/AuthenticationOperation.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,7 @@ class AuthenticationOperation: ResultOperation<(ALTTeam, ALTCertificate, ALTAppl
245245
let activeAppsMinimumVersion = OperatingSystemVersion(majorVersion: 13, minorVersion: 3, patchVersion: 1)
246246
if team.type == .free, ProcessInfo.processInfo.isOperatingSystemAtLeast(activeAppsMinimumVersion)
247247
{
248-
UserDefaults.standard.activeAppsLimit = ALTActiveAppsLimit
248+
UserDefaults.standard.activeAppsLimit = InstalledApp.freeAccountActiveAppsLimit
249249
}
250250
else
251251
{
Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
//
2+
// ClearAppCacheOperation.swift
3+
// AltStore
4+
//
5+
// Created by Riley Testut on 9/27/22.
6+
// Copyright © 2022 Riley Testut. All rights reserved.
7+
//
8+
9+
import Foundation
10+
import AltStoreCore
11+
12+
struct BatchError: ALTLocalizedError
13+
{
14+
enum Code: Int, ALTErrorCode
15+
{
16+
typealias Error = BatchError
17+
18+
case batchError
19+
}
20+
21+
var code: Code = .batchError
22+
var underlyingErrors: [Error]
23+
24+
var errorTitle: String?
25+
var errorFailure: String?
26+
27+
init(errors: [Error])
28+
{
29+
self.underlyingErrors = errors
30+
}
31+
32+
var errorFailureReason: String {
33+
guard !self.underlyingErrors.isEmpty else { return NSLocalizedString("An unknown error occured.", comment: "") }
34+
35+
let errorMessages = self.underlyingErrors.map { $0.localizedDescription }
36+
37+
let message = errorMessages.joined(separator: "\n\n")
38+
return message
39+
}
40+
}
41+
42+
@objc(ClearAppCacheOperation)
43+
class ClearAppCacheOperation: ResultOperation<Void>
44+
{
45+
private let coordinator = NSFileCoordinator()
46+
private let coordinatorQueue = OperationQueue()
47+
48+
override init()
49+
{
50+
self.coordinatorQueue.name = "AltStore - ClearAppCacheOperation Queue"
51+
}
52+
53+
override func main()
54+
{
55+
super.main()
56+
57+
var allErrors = [Error]()
58+
59+
self.clearTemporaryDirectory { result in
60+
switch result
61+
{
62+
case .failure(let batchError as BatchError): allErrors.append(contentsOf: batchError.underlyingErrors)
63+
case .failure(let error): allErrors.append(error)
64+
case .success: break
65+
}
66+
67+
self.removeUninstalledAppBackupDirectories { result in
68+
switch result
69+
{
70+
case .failure(let batchError as BatchError): allErrors.append(contentsOf: batchError.underlyingErrors)
71+
case .failure(let error): allErrors.append(error)
72+
case .success: break
73+
}
74+
75+
if allErrors.isEmpty
76+
{
77+
self.finish(.success(()))
78+
}
79+
else
80+
{
81+
let error = BatchError(errors: allErrors)
82+
self.finish(.failure(error))
83+
}
84+
}
85+
}
86+
}
87+
}
88+
89+
private extension ClearAppCacheOperation
90+
{
91+
func clearTemporaryDirectory(completion: @escaping (Result<Void, Error>) -> Void)
92+
{
93+
let intent = NSFileAccessIntent.writingIntent(with: FileManager.default.temporaryDirectory, options: [.forDeleting])
94+
self.coordinator.coordinate(with: [intent], queue: self.coordinatorQueue) { (error) in
95+
do
96+
{
97+
if let error
98+
{
99+
throw error
100+
}
101+
102+
let fileURLs = try FileManager.default.contentsOfDirectory(at: intent.url,
103+
includingPropertiesForKeys: [],
104+
options: [.skipsSubdirectoryDescendants, .skipsHiddenFiles])
105+
var errors = [Error]()
106+
107+
for fileURL in fileURLs
108+
{
109+
do
110+
{
111+
print("[ALTLog] Removing item from temporary directory:", fileURL.lastPathComponent)
112+
try FileManager.default.removeItem(at: fileURL)
113+
}
114+
catch
115+
{
116+
print("[ALTLog] Failed to remove \(fileURL.lastPathComponent) from temporary directory.", error)
117+
errors.append(error)
118+
}
119+
}
120+
121+
if !errors.isEmpty
122+
{
123+
let error = BatchError(errors: errors)
124+
completion(.failure(error))
125+
}
126+
else
127+
{
128+
completion(.success(()))
129+
}
130+
}
131+
catch
132+
{
133+
completion(.failure(error))
134+
}
135+
}
136+
}
137+
138+
func removeUninstalledAppBackupDirectories(completion: @escaping (Result<Void, Error>) -> Void)
139+
{
140+
guard let backupsDirectory = FileManager.default.appBackupsDirectory else { return completion(.failure(OperationError.missingAppGroup)) }
141+
142+
DatabaseManager.shared.persistentContainer.performBackgroundTask { context in
143+
let installedAppBundleIDs = Set(InstalledApp.all(in: context).map { $0.bundleIdentifier })
144+
145+
let intent = NSFileAccessIntent.writingIntent(with: backupsDirectory, options: [.forDeleting])
146+
self.coordinator.coordinate(with: [intent], queue: self.coordinatorQueue) { (error) in
147+
do
148+
{
149+
if let error
150+
{
151+
throw error
152+
}
153+
154+
var isDirectory: ObjCBool = false
155+
guard FileManager.default.fileExists(atPath: intent.url.path, isDirectory: &isDirectory), isDirectory.boolValue else {
156+
completion(.success(()))
157+
return
158+
}
159+
160+
let fileURLs = try FileManager.default.contentsOfDirectory(at: intent.url,
161+
includingPropertiesForKeys: [.isDirectoryKey, .nameKey],
162+
options: [.skipsSubdirectoryDescendants, .skipsHiddenFiles])
163+
var errors = [Error]()
164+
165+
for backupDirectory in fileURLs
166+
{
167+
do
168+
{
169+
let resourceValues = try backupDirectory.resourceValues(forKeys: [.isDirectoryKey, .nameKey])
170+
guard let isDirectory = resourceValues.isDirectory, let bundleID = resourceValues.name else { continue }
171+
172+
if isDirectory && !installedAppBundleIDs.contains(bundleID) && !AppManager.shared.isActivelyManagingApp(withBundleID: bundleID)
173+
{
174+
print("[ALTLog] Removing backup directory for uninstalled app:", bundleID)
175+
try FileManager.default.removeItem(at: backupDirectory)
176+
}
177+
}
178+
catch
179+
{
180+
print("[ALTLog] Failed to remove app backup directory:", error)
181+
errors.append(error)
182+
}
183+
}
184+
185+
if !errors.isEmpty
186+
{
187+
let error = BatchError(errors: errors)
188+
completion(.failure(error))
189+
}
190+
else
191+
{
192+
completion(.success(()))
193+
}
194+
}
195+
catch
196+
{
197+
print("[ALTLog] Failed to remove app backup directory:", error)
198+
completion(.failure(error))
199+
}
200+
}
201+
}
202+
}
203+
}

0 commit comments

Comments
 (0)