diff --git a/.gitignore b/.gitignore
index 7451ca6b..152b1fc5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -41,5 +41,4 @@ Payload
*.ipa.zip
xcodebuild.log
-Dependencies/em_proxy.xcodeproj/project.pbxproj
-Dependencies/minimuxer.xcodeproj/project.pbxproj
+build.sh
diff --git a/AltStore/Info.plist b/AltStore/Info.plist
index 2b4b48de..186097b6 100644
--- a/AltStore/Info.plist
+++ b/AltStore/Info.plist
@@ -2,6 +2,8 @@
+ ALTAnisetteURL
+ https://sideloadly.io/anisette/irGb3Quww8zrhgqnzmrx
ALTAppGroups
group.$(APP_GROUP_IDENTIFIER)
@@ -9,12 +11,10 @@
ALTDeviceID
00008101-000129D63698001E
- ALTServerID
- 1F7D5B55-79CE-4546-A029-D4DDC4AF3B6D
ALTPairingFile
<insert pairing file here>
- ALTAnisetteURL
- https://sideloadly.io/anisette/irGb3Quww8zrhgqnzmrx
+ ALTServerID
+ 1F7D5B55-79CE-4546-A029-D4DDC4AF3B6D
CFBundleDevelopmentRegion
$(DEVELOPMENT_LANGUAGE)
CFBundleDocumentTypes
@@ -44,8 +44,6 @@
$(PRODUCT_NAME)
CFBundlePackageType
APPL
- LSSupportsOpeningDocumentsInPlace
-
CFBundleShortVersionString
$(MARKETING_VERSION)
CFBundleURLTypes
@@ -93,6 +91,15 @@
LSRequiresIPhoneOS
+ LSSupportsOpeningDocumentsInPlace
+
+ NSAppTransportSecurity
+
+ NSAllowsArbitraryLoads
+
+
+ NSAppleMusicUsageDescription
+ So that we can bypass the 3 app limit and disable revokes using MDC and the tccd exploit.
NSBonjourServices
_altserver._tcp
@@ -131,13 +138,10 @@
fetch
remote-notification
+ UIFileSharingEnabled
+
UILaunchStoryboardName
LaunchScreen
- NSAppTransportSecurity
-
- NSAllowsArbitraryLoads
-
-
UIMainStoryboardFile
Main
UIRequiredDeviceCapabilities
@@ -204,7 +208,5 @@
- UIFileSharingEnabled
-
diff --git a/AltStore/LaunchViewController.swift b/AltStore/LaunchViewController.swift
index 50008be0..5a09fb4a 100644
--- a/AltStore/LaunchViewController.swift
+++ b/AltStore/LaunchViewController.swift
@@ -46,38 +46,8 @@ final class LaunchViewController: RSTLaunchViewController, UIDocumentPickerDeleg
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(true)
#if !targetEnvironment(simulator)
- let dialogMessage = UIAlertController(title: "MDC Patch", message: "please confirm you would like to patch MDC/three-app-limit, press patch to patch, dont patch to launch sidestore without patch", preferredStyle: .alert)
-
- // Create OK button with action handler
- let patch = UIAlertAction(title: "Patch", style: .default, handler: { _ in
- patch3AppLimit { result in
- switch result {
- case .success:
- print("patched sucessfully")
- case .failure(let err):
- switch err {
- case .NoFDA(let msg):
- self.displayError("Failed to get full disk access: \(msg)")
- return
- case .FailedPatchd:
- self.displayError("Failed to install patchd.")
- return
- }
- }
- }
- })
-
- let noPatch = UIAlertAction(title: "Continue without Patch", style: .default, handler: { _ in
- print("starting SS without mdc patch")
- })
-
- dialogMessage.addAction(patch)
- dialogMessage.addAction(noPatch)
-
- self.present(dialogMessage, animated: true, completion: nil)
-
start_em_proxy(bind_addr: Consts.Proxy.serverURL)
-
+
guard let pf = fetchPairingFile() else {
self.displayError("Device pairing file not found.")
return
@@ -118,7 +88,7 @@ final class LaunchViewController: RSTLaunchViewController, UIDocumentPickerDeleg
types.append(contentsOf: UTType.types(tag: "mobiledevicepairing", tagClass: UTTagClass.filenameExtension, conformingTo: UTType.data))
types.append(.xml)
let documentPickerController = UIDocumentPickerViewController(forOpeningContentTypes: types)
- documentPickerController.shouldShowFileExtensions = true
+// documentPickerController.shouldShowFileExtensions = true
documentPickerController.delegate = self
self.present(documentPickerController, animated: true, completion: nil)
})
@@ -242,6 +212,37 @@ extension LaunchViewController {
self.destinationViewController.view.alpha = 1.0
}
+ if UserDefaults.standard.enableMacDirtyCowExploit, UserDefaults.standard.isMacDirtyCowSupported {
+ if let previous_exploit_time = UserDefaults.standard.object(forKey: "mdcRanBootTime") {
+ let last_rantime = previous_exploit_time as! Date
+ if last_rantime == bootTime() {
+ return print("exploit has ran this boot - \(last_rantime)")
+ }
+ }
+ self.runExploit()
+ }
+
self.didFinishLaunching = true
}
+
+ func runExploit() {
+ if UserDefaults.standard.enableMacDirtyCowExploit && UserDefaults.standard.isMacDirtyCowSupported {
+ patch3AppLimit { result in
+ switch result {
+ case .success:
+ UserDefaults.standard.set(bootTime(), forKey: "mdcRanBootTime")
+ print("patched sucessfully")
+ case .failure(let err):
+ switch err {
+ case .NoFDA(let msg):
+ self.displayError("Failed to get full disk access: \(msg)")
+ return
+ case .FailedPatchd:
+ self.displayError("Failed to install patchd.")
+ return
+ }
+ }
+ }
+ }
+ }
}
diff --git a/AltStore/MDCExploit/MDCExploits.swift b/AltStore/MDCExploit/MDCExploits.swift
index c1010d77..021fed19 100644
--- a/AltStore/MDCExploit/MDCExploits.swift
+++ b/AltStore/MDCExploit/MDCExploits.swift
@@ -16,16 +16,26 @@ func patch3AppLimit(completion: @escaping (PatchResult) -> ()) {
if let error = error {
completion(.failure(PatchError.NoFDA(msg: "Failed to get full disk access: \(error)")))
}
- DispatchQueue.global(qos: .userInitiated).async {
- print("This is run on a background queue")
- if !patch_installd() {
- completion(.failure(PatchError.FailedPatchd))
- }
+// DispatchQueue.global(qos: .userInitiated).async {
+ print("This is run on a background queue")
+ if !patch_installd() {
+ completion(.failure(PatchError.FailedPatchd))
}
+// }
completion(.success)
}
}
+func bootTime() -> Date? {
+ var tv = timeval()
+ var tvSize = MemoryLayout.size
+ let err = sysctlbyname("kern.boottime", &tv, &tvSize, nil, 0);
+ guard err == 0, tvSize == MemoryLayout.size else {
+ return nil
+ }
+ return Date(timeIntervalSince1970: Double(tv.tv_sec) + Double(tv.tv_usec) / 1_000_000.0)
+}
+
enum WhitelistPatchResult {
case success, failure
}
diff --git a/AltStore/MDCExploit/grant_full_disk_access.m b/AltStore/MDCExploit/grant_full_disk_access.m
index 9563412d..828ebbe8 100644
--- a/AltStore/MDCExploit/grant_full_disk_access.m
+++ b/AltStore/MDCExploit/grant_full_disk_access.m
@@ -580,35 +580,31 @@ static NSData* make_patch_installd(void* executableMap, size_t executableLength)
}
bool patch_installd() {
- dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
- const char* targetPath = "/usr/libexec/installd";
- int fd = open(targetPath, O_RDONLY | O_CLOEXEC);
- off_t targetLength = lseek(fd, 0, SEEK_END);
- lseek(fd, 0, SEEK_SET);
- void* targetMap = mmap(nil, targetLength, PROT_READ, MAP_SHARED, fd, 0);
+ const char* targetPath = "/usr/libexec/installd";
+ int fd = open(targetPath, O_RDONLY | O_CLOEXEC);
+ off_t targetLength = lseek(fd, 0, SEEK_END);
+ lseek(fd, 0, SEEK_SET);
+ void* targetMap = mmap(nil, targetLength, PROT_READ, MAP_SHARED, fd, 0);
- NSData* originalData = [NSData dataWithBytes:targetMap length:targetLength];
- NSData* sourceData = make_patch_installd(targetMap, targetLength);
- if (!sourceData) {
- NSLog(@"can't patchfind");
-// return ;
- }
+ NSData* originalData = [NSData dataWithBytes:targetMap length:targetLength];
+ NSData* sourceData = make_patch_installd(targetMap, targetLength);
+ if (!sourceData) {
+ NSLog(@"can't patchfind");
+ return false;
+ }
- if (!overwrite_file(fd, sourceData)) {
- overwrite_file(fd, originalData);
- munmap(targetMap, targetLength);
- NSLog(@"can't overwrite");
-// return ;
- }
- munmap(targetMap, targetLength);
- xpc_crasher("com.apple.mobile.installd");
- sleep(1);
+ if (!overwrite_file(fd, sourceData)) {
+ overwrite_file(fd, originalData);
+ munmap(targetMap, targetLength);
+ NSLog(@"can't overwrite");
+ return false;
+ }
+ munmap(targetMap, targetLength);
+ xpc_crasher("com.apple.mobile.installd");
+ sleep(1);
- // TODO(zhuowei): for now we revert it once installd starts
- // so the change will only last until when this installd exits
-// overwrite_file(fd, originalData);
- NSLog(@"patched");
-// return;
- });
- return true;
+ // TODO(zhuowei): for now we revert it once installd starts
+ // so the change will only last until when this installd exits
+// overwrite_file(fd, originalData);
+ return true;
}
diff --git a/AltStore/My Apps/MyAppsViewController.swift b/AltStore/My Apps/MyAppsViewController.swift
index 9f968e16..c97532fa 100644
--- a/AltStore/My Apps/MyAppsViewController.swift
+++ b/AltStore/My Apps/MyAppsViewController.swift
@@ -6,13 +6,13 @@
// Copyright © 2019 Riley Testut. All rights reserved.
//
-import UIKit
-import MobileCoreServices
-import Intents
import Combine
+import Intents
+import MobileCoreServices
+import UIKit
-import AltStoreCore
import AltSign
+import AltStoreCore
import Roxas
import Nuke
@@ -151,8 +151,7 @@ final class MyAppsViewController: UICollectionViewController
}
@IBAction func unwindToMyAppsViewController(_ segue: UIStoryboardSegue)
- {
- }
+ {}
}
private extension MyAppsViewController
@@ -170,7 +169,7 @@ private extension MyAppsViewController
dynamicDataSource.numberOfSectionsHandler = { 1 }
dynamicDataSource.numberOfItemsHandler = { _ in self.updatesDataSource.itemCount == 0 ? 1 : 0 }
dynamicDataSource.cellIdentifierHandler = { _ in "NoUpdatesCell" }
- dynamicDataSource.cellConfigurationHandler = { (cell, _, indexPath) in
+ dynamicDataSource.cellConfigurationHandler = { cell, _, _ in
let cell = cell as! NoUpdatesCollectionViewCell
cell.layoutMargins.left = self.view.layoutMargins.left
cell.layoutMargins.right = self.view.layoutMargins.right
@@ -193,7 +192,7 @@ private extension MyAppsViewController
let dataSource = RSTFetchedResultsCollectionViewPrefetchingDataSource(fetchRequest: fetchRequest, managedObjectContext: DatabaseManager.shared.viewContext)
dataSource.liveFetchLimit = maximumCollapsedUpdatesCount
dataSource.cellIdentifierHandler = { _ in "UpdateCell" }
- dataSource.cellConfigurationHandler = { [weak self] (cell, installedApp, indexPath) in
+ dataSource.cellConfigurationHandler = { [weak self] cell, installedApp, _ in
guard let self = self else { return }
guard let app = installedApp.storeApp, let latestVersion = app.latestVersion else { return }
@@ -245,11 +244,12 @@ private extension MyAppsViewController
cell.setNeedsLayout()
}
- dataSource.prefetchHandler = { (installedApp, indexPath, completionHandler) in
+ dataSource.prefetchHandler = { installedApp, _, completionHandler in
guard let iconURL = installedApp.storeApp?.iconURL else { return nil }
- return RSTAsyncBlockOperation() { (operation) in
- ImagePipeline.shared.loadImage(with: iconURL, progress: nil, completion: { (response, error) in
+ return RSTAsyncBlockOperation
+ { operation in
+ ImagePipeline.shared.loadImage(with: iconURL, progress: nil, completion: { response, error in
guard !operation.isCancelled else { return operation.finish() }
if let image = response?.image
@@ -263,7 +263,7 @@ private extension MyAppsViewController
})
}
}
- dataSource.prefetchCompletionHandler = { (cell, image, indexPath, error) in
+ dataSource.prefetchCompletionHandler = { cell, image, _, error in
let cell = cell as! UpdateCollectionViewCell
cell.bannerView.iconImageView.isIndicatingActivity = false
cell.bannerView.iconImageView.image = image
@@ -288,7 +288,7 @@ private extension MyAppsViewController
let dataSource = RSTFetchedResultsCollectionViewPrefetchingDataSource(fetchRequest: fetchRequest, managedObjectContext: DatabaseManager.shared.viewContext)
dataSource.cellIdentifierHandler = { _ in "AppCell" }
- dataSource.cellConfigurationHandler = { (cell, installedApp, indexPath) in
+ dataSource.cellConfigurationHandler = { cell, installedApp, indexPath in
let tintColor = installedApp.storeApp?.tintColor ?? .altPrimary
let cell = cell as! InstalledAppCollectionViewCell
@@ -363,10 +363,13 @@ private extension MyAppsViewController
cell.bannerView.button.progress = nil
}
}
- dataSource.prefetchHandler = { (item, indexPath, completion) in
- RSTAsyncBlockOperation { (operation) in
- item.managedObjectContext?.perform {
- item.loadIcon { (result) in
+ dataSource.prefetchHandler = { item, _, completion in
+ RSTAsyncBlockOperation
+ { _ in
+ item.managedObjectContext?.perform
+ {
+ item.loadIcon
+ { result in
switch result
{
case .failure(let error): completion(nil, error)
@@ -376,7 +379,7 @@ private extension MyAppsViewController
}
}
}
- dataSource.prefetchCompletionHandler = { (cell, image, indexPath, error) in
+ dataSource.prefetchCompletionHandler = { cell, image, _, _ in
let cell = cell as! InstalledAppCollectionViewCell
cell.bannerView.iconImageView.image = image
cell.bannerView.iconImageView.isIndicatingActivity = false
@@ -397,7 +400,7 @@ private extension MyAppsViewController
let dataSource = RSTFetchedResultsCollectionViewPrefetchingDataSource(fetchRequest: fetchRequest, managedObjectContext: DatabaseManager.shared.viewContext)
dataSource.cellIdentifierHandler = { _ in "AppCell" }
- dataSource.cellConfigurationHandler = { (cell, installedApp, indexPath) in
+ dataSource.cellConfigurationHandler = { cell, installedApp, _ in
let tintColor = installedApp.storeApp?.tintColor ?? .altPrimary
let cell = cell as! InstalledAppCollectionViewCell
@@ -437,10 +440,13 @@ private extension MyAppsViewController
cell.bannerView.button.progress = nil
}
}
- dataSource.prefetchHandler = { (item, indexPath, completion) in
- RSTAsyncBlockOperation { (operation) in
- item.managedObjectContext?.perform {
- item.loadIcon { (result) in
+ dataSource.prefetchHandler = { item, _, completion in
+ RSTAsyncBlockOperation
+ { _ in
+ item.managedObjectContext?.perform
+ {
+ item.loadIcon
+ { result in
switch result
{
case .failure(let error): completion(nil, error)
@@ -450,7 +456,7 @@ private extension MyAppsViewController
}
}
}
- dataSource.prefetchCompletionHandler = { (cell, image, indexPath, error) in
+ dataSource.prefetchCompletionHandler = { cell, image, _, _ in
let cell = cell as! InstalledAppCollectionViewCell
cell.bannerView.iconImageView.image = image
cell.bannerView.iconImageView.isIndicatingActivity = false
@@ -461,10 +467,7 @@ private extension MyAppsViewController
func updateDataSource()
{
-
- self.dataSource.predicate = nil
-
-
+ self.dataSource.predicate = nil
}
}
@@ -485,15 +488,17 @@ private extension MyAppsViewController
if self.isViewLoaded
{
- UIView.performWithoutAnimation {
+ UIView.performWithoutAnimation
+ {
self.collectionView.reloadSections(IndexSet(integer: Section.updates.rawValue))
}
- }
+ }
}
func fetchAppIDs()
{
- AppManager.shared.fetchAppIDs { (result) in
+ AppManager.shared.fetchAppIDs
+ { result in
do
{
let (_, context) = try result.get()
@@ -506,12 +511,14 @@ private extension MyAppsViewController
}
}
- func refresh(_ installedApps: [InstalledApp], completionHandler: @escaping ([String : Result]) -> Void)
+ func refresh(_ installedApps: [InstalledApp], completionHandler: @escaping ([String: Result]) -> Void)
{
let group = AppManager.shared.refresh(installedApps, presentingViewController: self, group: self.refreshGroup)
- group.completionHandler = { (results) in
- DispatchQueue.main.async {
- let failures = results.compactMapValues { (result) -> Error? in
+ group.completionHandler = { results in
+ DispatchQueue.main.async
+ {
+ let failures = results.compactMapValues
+ { result -> Error? in
switch result
{
case .failure(OperationError.cancelled): return nil
@@ -557,7 +564,8 @@ private extension MyAppsViewController
self.refreshGroup = group
- UIView.performWithoutAnimation {
+ UIView.performWithoutAnimation
+ {
self.collectionView.reloadSections([Section.activeApps.rawValue, Section.inactiveApps.rawValue])
}
}
@@ -570,7 +578,6 @@ private extension MyAppsViewController
let visibleCells = self.collectionView.visibleCells
self.collectionView.performBatchUpdates({
-
self.isUpdateSectionCollapsed.toggle()
UIView.animate(withDuration: 0.3, animations: {
@@ -644,8 +651,10 @@ private extension MyAppsViewController
let installedApps = InstalledApp.fetchAppsForRefreshingAll(in: DatabaseManager.shared.viewContext)
- self.refresh(installedApps) { (result) in
- DispatchQueue.main.async {
+ self.refresh(installedApps)
+ { _ in
+ DispatchQueue.main.async
+ {
self.isRefreshingAllApps = false
self.collectionView.reloadSections([Section.activeApps.rawValue, Section.inactiveApps.rawValue])
}
@@ -654,7 +663,8 @@ private extension MyAppsViewController
if #available(iOS 14, *)
{
let interaction = INInteraction.refreshAllApps()
- interaction.donate { (error) in
+ interaction.donate
+ { error in
guard let error = error else { return }
print("Failed to donate intent \(interaction.intent).", error)
}
@@ -669,13 +679,17 @@ private extension MyAppsViewController
let installedApp = self.dataSource.item(at: indexPath)
let previousProgress = AppManager.shared.installationProgress(for: installedApp)
- guard previousProgress == nil else {
+ guard previousProgress == nil
+ else
+ {
previousProgress?.cancel()
return
}
- _ = AppManager.shared.update(installedApp, presentingViewController: self) { (result) in
- DispatchQueue.main.async {
+ _ = AppManager.shared.update(installedApp, presentingViewController: self)
+ { result in
+ DispatchQueue.main.async
+ {
switch result
{
case .failure(OperationError.cancelled):
@@ -727,11 +741,14 @@ private extension MyAppsViewController
{
var fileURL: URL?
var application: ALTApplication?
- var installedApp: InstalledApp? {
- didSet {
+ var installedApp: InstalledApp?
+ {
+ didSet
+ {
self.installedAppContext = self.installedApp?.managedObjectContext
}
}
+
private var installedAppContext: NSManagedObjectContext?
var error: Error?
@@ -753,8 +770,10 @@ private extension MyAppsViewController
else
{
let downloadProgress = Progress.discreteProgress(totalUnitCount: 100)
- downloadOperation = RSTAsyncBlockOperation { (operation) in
- let downloadTask = URLSession.shared.downloadTask(with: url) { (fileURL, response, error) in
+ downloadOperation = RSTAsyncBlockOperation
+ { operation in
+ let downloadTask = URLSession.shared.downloadTask(with: url)
+ { fileURL, response, error in
do
{
let (fileURL, _) = try Result((fileURL, response), error).get()
@@ -779,7 +798,8 @@ private extension MyAppsViewController
}
let unzipProgress = Progress.discreteProgress(totalUnitCount: 1)
- let unzipAppOperation = BlockOperation {
+ let unzipAppOperation = BlockOperation
+ {
do
{
if let error = context.error
@@ -788,7 +808,8 @@ private extension MyAppsViewController
}
guard let fileURL = context.fileURL else { throw OperationError.invalidParameters }
- defer {
+ defer
+ {
try? FileManager.default.removeItem(at: fileURL)
}
@@ -813,7 +834,8 @@ private extension MyAppsViewController
}
let removeAppExtensionsProgress = Progress.discreteProgress(totalUnitCount: 1)
- let removeAppExtensionsOperation = RSTAsyncBlockOperation { [weak self] (operation) in
+ let removeAppExtensionsOperation = RSTAsyncBlockOperation
+ { [weak self] operation in
do
{
if let error = context.error
@@ -823,8 +845,10 @@ private extension MyAppsViewController
guard let application = context.application else { throw OperationError.invalidParameters }
- DispatchQueue.main.async {
- self?.removeAppExtensions(from: application) { (result) in
+ DispatchQueue.main.async
+ {
+ self?.removeAppExtensions(from: application)
+ { result in
switch result
{
case .success: removeAppExtensionsProgress.completedUnitCount = 1
@@ -844,7 +868,8 @@ private extension MyAppsViewController
progress.addChild(removeAppExtensionsProgress, withPendingUnitCount: 5)
let installProgress = Progress.discreteProgress(totalUnitCount: 100)
- let installAppOperation = RSTAsyncBlockOperation { (operation) in
+ let installAppOperation = RSTAsyncBlockOperation
+ { operation in
do
{
if let error = context.error
@@ -854,7 +879,8 @@ private extension MyAppsViewController
guard let application = context.application else { throw OperationError.invalidParameters }
- let group = AppManager.shared.install(application, presentingViewController: self) { (result) in
+ let group = AppManager.shared.install(application, presentingViewController: self)
+ { result in
switch result
{
case .success(let installedApp): context.installedApp = installedApp
@@ -873,7 +899,8 @@ private extension MyAppsViewController
installAppOperation.completionBlock = {
try? FileManager.default.removeItem(at: temporaryDirectory)
- DispatchQueue.main.async {
+ DispatchQueue.main.async
+ {
self.navigationItem.leftBarButtonItem?.isIndicatingActivity = false
self.sideloadingProgressView.observedProgress = nil
self.sideloadingProgressView.setHidden(true, animated: true)
@@ -883,12 +910,13 @@ private extension MyAppsViewController
case .success(let app):
completion(.success(()))
- app.managedObjectContext?.perform {
+ app.managedObjectContext?.perform
+ {
print("Successfully installed app:", app.bundleIdentifier)
}
case .failure(OperationError.cancelled):
- completion(.failure((OperationError.cancelled)))
+ completion(.failure(OperationError.cancelled))
case .failure(let error):
let toastView = ToastView(error: error)
@@ -930,11 +958,17 @@ private extension MyAppsViewController
@objc func presentInactiveAppsAlert()
{
- let message: String
+ var message: String
if UserDefaults.standard.activeAppLimitIncludesExtensions
{
message = NSLocalizedString("Non-developer Apple IDs are limited to 3 apps and app extensions. Inactive apps don't count towards your total, but cannot be opened until activated.", comment: "")
+
+ if UserDefaults.standard.enableMacDirtyCowExploit
+ {
+ 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 per Apple ID instead.", comment: "")
+ }
}
else
{
@@ -974,13 +1008,15 @@ private extension MyAppsViewController
let message = firstSentence + " " + NSLocalizedString("Would you like to remove this app's extensions so they don't count towards your limit?", comment: "")
let alertController = UIAlertController(title: NSLocalizedString("App Contains Extensions", comment: ""), message: message, preferredStyle: .alert)
- alertController.addAction(UIAlertAction(title: UIAlertAction.cancel.title, style: UIAlertAction.cancel.style, handler: { (action) in
+ alertController.addAction(UIAlertAction(title: UIAlertAction.cancel.title, style: UIAlertAction.cancel.style, handler: { _ in
completion(.failure(OperationError.cancelled))
}))
- alertController.addAction(UIAlertAction(title: NSLocalizedString("Keep App Extensions", comment: ""), style: .default) { (action) in
+ alertController.addAction(UIAlertAction(title: NSLocalizedString("Keep App Extensions", comment: ""), style: .default)
+ { _ in
completion(.success(()))
})
- alertController.addAction(UIAlertAction(title: NSLocalizedString("Remove App Extensions", comment: ""), style: .destructive) { (action) in
+ alertController.addAction(UIAlertAction(title: NSLocalizedString("Remove App Extensions", comment: ""), style: .destructive)
+ { _ in
do
{
for appExtension in application.appExtensions
@@ -1004,7 +1040,8 @@ private extension MyAppsViewController
{
func open(_ installedApp: InstalledApp)
{
- UIApplication.shared.open(installedApp.openAppURL) { success in
+ UIApplication.shared.open(installedApp.openAppURL)
+ { success in
guard !success else { return }
let toastView = ToastView(error: OperationError.openAppFailed(name: installedApp.name))
@@ -1015,16 +1052,20 @@ private extension MyAppsViewController
func refresh(_ installedApp: InstalledApp)
{
let previousProgress = AppManager.shared.refreshProgress(for: installedApp)
- guard previousProgress == nil else {
+ guard previousProgress == nil
+ else
+ {
previousProgress?.cancel()
return
}
- self.refresh([installedApp]) { (results) in
+ self.refresh([installedApp])
+ { results in
// If an error occured, reload the section so the progress bar is no longer visible.
if results.values.contains(where: { $0.error != nil })
{
- DispatchQueue.main.async {
+ DispatchQueue.main.async
+ {
self.collectionView.reloadSections([Section.activeApps.rawValue, Section.inactiveApps.rawValue])
}
}
@@ -1040,7 +1081,8 @@ private extension MyAppsViewController
do
{
let app = try result.get()
- app.managedObjectContext?.perform {
+ app.managedObjectContext?.perform
+ {
try? app.managedObjectContext?.save()
}
}
@@ -1052,7 +1094,8 @@ private extension MyAppsViewController
{
print("Failed to activate app:", error)
- DispatchQueue.main.async {
+ DispatchQueue.main.async
+ {
installedApp.isActive = false
let toastView = ToastView(error: error)
@@ -1073,11 +1116,13 @@ private extension MyAppsViewController
.filter(\.isActive)
.map { $0.publisher(for: \.isActive) }
.collect()
- .flatMap { publishers in
+ .flatMap
+ { publishers in
Publishers.MergeMany(publishers)
}
.first { isActive in !isActive }
- .sink { _ in
+ .sink
+ { _ in
// A previously active app is now inactive,
// which means there are now enough slots to activate the app,
// so pre-emptively mark it as active to provide visual feedback sooner.
@@ -1085,9 +1130,11 @@ private extension MyAppsViewController
cancellable?.cancel()
}
- AppManager.shared.deactivateApps(for: app, presentingViewController: self) { result in
+ AppManager.shared.deactivateApps(for: app, presentingViewController: self)
+ { result in
cancellable?.cancel()
- installedApp.managedObjectContext?.perform {
+ installedApp.managedObjectContext?.perform
+ {
switch result
{
case .failure(let error):
@@ -1113,7 +1160,8 @@ private extension MyAppsViewController
guard installedApp.isActive else { return }
installedApp.isActive = false
- AppManager.shared.deactivate(installedApp, presentingViewController: self) { (result) in
+ AppManager.shared.deactivate(installedApp, presentingViewController: self)
+ { result in
do
{
let app = try result.get()
@@ -1125,7 +1173,8 @@ private extension MyAppsViewController
{
print("Failed to activate app:", error)
- DispatchQueue.main.async {
+ DispatchQueue.main.async
+ {
installedApp.isActive = true
let toastView = ToastView(error: error)
@@ -1153,13 +1202,15 @@ private extension MyAppsViewController
let alertController = UIAlertController(title: title, message: message, preferredStyle: .actionSheet)
alertController.addAction(.cancel)
- alertController.addAction(UIAlertAction(title: NSLocalizedString("Remove", comment: ""), style: .destructive, handler: { (action) in
- AppManager.shared.remove(installedApp) { (result) in
+ alertController.addAction(UIAlertAction(title: NSLocalizedString("Remove", comment: ""), style: .destructive, handler: { _ in
+ AppManager.shared.remove(installedApp)
+ { result in
switch result
{
case .success: break
case .failure(let error):
- DispatchQueue.main.async {
+ DispatchQueue.main.async
+ {
let toastView = ToastView(error: error)
toastView.show(in: self)
}
@@ -1179,8 +1230,9 @@ private extension MyAppsViewController
alertController.addAction(.cancel)
let actionTitle = String(format: NSLocalizedString("Back Up %@", comment: ""), installedApp.name)
- alertController.addAction(UIAlertAction(title: actionTitle, style: .default, handler: { (action) in
- AppManager.shared.backup(installedApp, presentingViewController: self) { (result) in
+ alertController.addAction(UIAlertAction(title: actionTitle, style: .default, handler: { _ in
+ AppManager.shared.backup(installedApp, presentingViewController: self)
+ { result in
do
{
let app = try result.get()
@@ -1192,7 +1244,8 @@ private extension MyAppsViewController
{
print("Failed to back up app:", error)
- DispatchQueue.main.async {
+ DispatchQueue.main.async
+ {
let toastView = ToastView(error: error)
toastView.show(in: self)
@@ -1201,7 +1254,8 @@ private extension MyAppsViewController
}
}
- DispatchQueue.main.async {
+ DispatchQueue.main.async
+ {
self.collectionView.reloadSections([Section.activeApps.rawValue, Section.inactiveApps.rawValue])
}
}))
@@ -1214,8 +1268,9 @@ private extension MyAppsViewController
let message = String(format: NSLocalizedString("This will replace all data you currently have in %@.", comment: ""), installedApp.name)
let alertController = UIAlertController(title: NSLocalizedString("Are you sure you want to restore this backup?", comment: ""), message: message, preferredStyle: .actionSheet)
alertController.addAction(.cancel)
- alertController.addAction(UIAlertAction(title: NSLocalizedString("Restore Backup", comment: ""), style: .destructive, handler: { (action) in
- AppManager.shared.restore(installedApp, presentingViewController: self) { (result) in
+ alertController.addAction(UIAlertAction(title: NSLocalizedString("Restore Backup", comment: ""), style: .destructive, handler: { _ in
+ AppManager.shared.restore(installedApp, presentingViewController: self)
+ { result in
do
{
let app = try result.get()
@@ -1227,14 +1282,16 @@ private extension MyAppsViewController
{
print("Failed to restore app:", error)
- DispatchQueue.main.async {
+ DispatchQueue.main.async
+ {
let toastView = ToastView(error: error)
toastView.show(in: self)
}
}
}
- DispatchQueue.main.async {
+ DispatchQueue.main.async
+ {
self.collectionView.reloadSections([Section.activeApps.rawValue])
}
}))
@@ -1267,7 +1324,8 @@ private extension MyAppsViewController
self.activeAppsDataSource.prefetchItemCache.removeObject(forKey: installedApp)
self.inactiveAppsDataSource.prefetchItemCache.removeObject(forKey: installedApp)
- DatabaseManager.shared.persistentContainer.performBackgroundTask { (context) in
+ DatabaseManager.shared.persistentContainer.performBackgroundTask
+ { context in
do
{
let tempApp = context.object(with: installedApp.objectID) as! InstalledApp
@@ -1291,7 +1349,8 @@ private extension MyAppsViewController
if tempApp.isActive
{
- DispatchQueue.main.async {
+ DispatchQueue.main.async
+ {
self.refresh(installedApp)
}
}
@@ -1300,7 +1359,8 @@ private extension MyAppsViewController
{
print("Failed to change app icon.", error)
- DispatchQueue.main.async {
+ DispatchQueue.main.async
+ {
let toastView = ToastView(error: error)
toastView.show(in: self)
}
@@ -1311,8 +1371,10 @@ private extension MyAppsViewController
@available(iOS 14, *)
func enableJIT(for installedApp: InstalledApp)
{
- AppManager.shared.enableJIT(for: installedApp) { result in
- DispatchQueue.main.async {
+ AppManager.shared.enableJIT(for: installedApp)
+ { result in
+ DispatchQueue.main.async
+ {
switch result
{
case .success: break
@@ -1329,7 +1391,8 @@ private extension MyAppsViewController
{
@objc func didFetchSource(_ notification: Notification)
{
- DispatchQueue.main.async {
+ DispatchQueue.main.async
+ {
if self.updatesDataSource.fetchedResultsController.fetchedObjects == nil
{
do { try self.updatesDataSource.fetchedResultsController.performFetch() }
@@ -1347,7 +1410,8 @@ private extension MyAppsViewController
guard let url = notification.userInfo?[AppDelegate.importAppDeepLinkURLKey] as? URL else { return }
- self.sideloadApp(at: url) { (result) in
+ self.sideloadApp(at: url)
+ { _ in
guard url.isFileURL else { return }
do
@@ -1374,7 +1438,8 @@ extension MyAppsViewController
case .updates:
let headerView = collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "UpdatesHeader", for: indexPath) as! UpdatesCollectionHeaderView
- UIView.performWithoutAnimation {
+ UIView.performWithoutAnimation
+ {
headerView.button.backgroundColor = UIColor.altPrimary.withAlphaComponent(0.15)
headerView.button.setTitle("▾", for: .normal)
headerView.button.titleLabel?.font = UIFont.boldSystemFont(ofSize: 28)
@@ -1400,7 +1465,8 @@ extension MyAppsViewController
case .activeApps where kind == UICollectionView.elementKindSectionHeader:
let headerView = collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "ActiveAppsHeader", for: indexPath) as! InstalledAppsCollectionHeaderView
- UIView.performWithoutAnimation {
+ UIView.performWithoutAnimation
+ {
headerView.layoutMargins.left = self.view.layoutMargins.left
headerView.layoutMargins.right = self.view.layoutMargins.right
@@ -1438,7 +1504,8 @@ extension MyAppsViewController
case .inactiveApps where kind == UICollectionView.elementKindSectionHeader:
let headerView = collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "InactiveAppsHeader", for: indexPath) as! InstalledAppsCollectionHeaderView
- UIView.performWithoutAnimation {
+ UIView.performWithoutAnimation
+ {
headerView.layoutMargins.left = self.view.layoutMargins.left
headerView.layoutMargins.right = self.view.layoutMargins.right
@@ -1507,50 +1574,61 @@ extension MyAppsViewController
{
var actions = [UIMenuElement]()
- let openAction = UIAction(title: NSLocalizedString("Open", comment: ""), image: UIImage(systemName: "arrow.up.forward.app")) { (action) in
+ let openAction = UIAction(title: NSLocalizedString("Open", comment: ""), image: UIImage(systemName: "arrow.up.forward.app"))
+ { _ in
self.open(installedApp)
}
let openMenu = UIMenu(title: "", options: .displayInline, children: [openAction])
- let refreshAction = UIAction(title: NSLocalizedString("Refresh", comment: ""), image: UIImage(systemName: "arrow.clockwise")) { (action) in
+ let refreshAction = UIAction(title: NSLocalizedString("Refresh", comment: ""), image: UIImage(systemName: "arrow.clockwise"))
+ { _ in
self.refresh(installedApp)
}
- let activateAction = UIAction(title: NSLocalizedString("Activate", comment: ""), image: UIImage(systemName: "checkmark.circle")) { (action) in
+ let activateAction = UIAction(title: NSLocalizedString("Activate", comment: ""), image: UIImage(systemName: "checkmark.circle"))
+ { _ in
self.activate(installedApp)
}
- let deactivateAction = UIAction(title: NSLocalizedString("Deactivate", comment: ""), image: UIImage(systemName: "xmark.circle"), attributes: .destructive) { (action) in
+ let deactivateAction = UIAction(title: NSLocalizedString("Deactivate", comment: ""), image: UIImage(systemName: "xmark.circle"), attributes: .destructive)
+ { _ in
self.deactivate(installedApp)
}
- let removeAction = UIAction(title: NSLocalizedString("Remove", comment: ""), image: UIImage(systemName: "trash"), attributes: .destructive) { (action) in
+ let removeAction = UIAction(title: NSLocalizedString("Remove", comment: ""), image: UIImage(systemName: "trash"), attributes: .destructive)
+ { _ in
self.remove(installedApp)
}
- let jitAction = UIAction(title: NSLocalizedString("Enable JIT", comment: ""), image: UIImage(systemName: "bolt")) { (action) in
+ let jitAction = UIAction(title: NSLocalizedString("Enable JIT", comment: ""), image: UIImage(systemName: "bolt"))
+ { _ in
guard #available(iOS 14, *) else { return }
self.enableJIT(for: installedApp)
}
- let backupAction = UIAction(title: NSLocalizedString("Back Up", comment: ""), image: UIImage(systemName: "doc.on.doc")) { (action) in
+ let backupAction = UIAction(title: NSLocalizedString("Back Up", comment: ""), image: UIImage(systemName: "doc.on.doc"))
+ { _ in
self.backup(installedApp)
}
- let exportBackupAction = UIAction(title: NSLocalizedString("Export Backup", comment: ""), image: UIImage(systemName: "arrow.up.doc")) { (action) in
+ let exportBackupAction = UIAction(title: NSLocalizedString("Export Backup", comment: ""), image: UIImage(systemName: "arrow.up.doc"))
+ { _ in
self.exportBackup(for: installedApp)
}
- let restoreBackupAction = UIAction(title: NSLocalizedString("Restore Backup", comment: ""), image: UIImage(systemName: "arrow.down.doc")) { (action) in
+ let restoreBackupAction = UIAction(title: NSLocalizedString("Restore Backup", comment: ""), image: UIImage(systemName: "arrow.down.doc"))
+ { _ in
self.restore(installedApp)
}
- let chooseIconAction = UIAction(title: NSLocalizedString("Photos", comment: ""), image: UIImage(systemName: "photo")) { (action) in
+ let chooseIconAction = UIAction(title: NSLocalizedString("Photos", comment: ""), image: UIImage(systemName: "photo"))
+ { _ in
self.chooseIcon(for: installedApp)
}
- let removeIconAction = UIAction(title: NSLocalizedString("Remove Custom Icon", comment: ""), image: UIImage(systemName: "trash"), attributes: [.destructive]) { (action) in
+ let removeIconAction = UIAction(title: NSLocalizedString("Remove Custom Icon", comment: ""), image: UIImage(systemName: "trash"), attributes: [.destructive])
+ { _ in
self.changeIcon(for: installedApp, to: nil)
}
@@ -1562,7 +1640,9 @@ extension MyAppsViewController
let changeIconMenu = UIMenu(title: NSLocalizedString("Change Icon", comment: ""), image: UIImage(systemName: "photo"), children: changeIconActions)
- guard installedApp.bundleIdentifier != StoreApp.altstoreAppID else {
+ guard installedApp.bundleIdentifier != StoreApp.altstoreAppID
+ else
+ {
#if BETA
return [refreshAction, changeIconMenu]
#else
@@ -1605,9 +1685,10 @@ extension MyAppsViewController
if let backupDirectoryURL = FileManager.default.backupDirectoryURL(for: installedApp)
{
var backupExists = false
- var outError: NSError? = nil
+ var outError: NSError?
- self.coordinator.coordinate(readingItemAt: backupDirectoryURL, options: [.withoutChanges], error: &outError) { (backupDirectoryURL) in
+ self.coordinator.coordinate(readingItemAt: backupDirectoryURL, options: [.withoutChanges], error: &outError)
+ { backupDirectoryURL in
#if DEBUG
backupExists = true
#else
@@ -1649,7 +1730,7 @@ extension MyAppsViewController
// Legacy sideloaded app, so can't detect if it's deleted.
actions.append(removeAction)
}
- else if !UserDefaults.standard.isLegacyDeactivationSupported && !installedApp.isActive
+ else if !UserDefaults.standard.isLegacyDeactivationSupported, !installedApp.isActive
{
// Inactive apps are actually deleted, so we need another way
// for user to remove them from AltStore.
@@ -1670,7 +1751,8 @@ extension MyAppsViewController
case .activeApps, .inactiveApps:
let installedApp = self.dataSource.item(at: indexPath)
- return UIContextMenuConfiguration(identifier: indexPath as NSIndexPath, previewProvider: nil) { (suggestedActions) -> UIMenu? in
+ return UIContextMenuConfiguration(identifier: indexPath as NSIndexPath, previewProvider: nil)
+ { _ -> UIMenu? in
let actions = self.actions(for: installedApp)
let menu = UIMenu(title: "", children: actions)
@@ -1868,7 +1950,7 @@ extension MyAppsViewController: UICollectionViewDropDelegate
let inactiveAppsHeaderAttributes = collectionView.layoutAttributesForSupplementaryElement(ofKind: UICollectionView.elementKindSectionHeader, at: IndexPath(item: 0, section: Section.inactiveApps.rawValue))
else { return UICollectionViewDropProposal(operation: .cancel) }
- var dropDestinationIndexPath: IndexPath? = nil
+ var dropDestinationIndexPath: IndexPath?
defer
{
@@ -1881,7 +1963,8 @@ extension MyAppsViewController: UICollectionViewDropDelegate
let indexPaths = [previousIndexPath, dropDestinationIndexPath].compactMap { $0 }
- let propertyAnimator = UIViewPropertyAnimator(springTimingParameters: UISpringTimingParameters()) {
+ let propertyAnimator = UIViewPropertyAnimator(springTimingParameters: UISpringTimingParameters())
+ {
for indexPath in indexPaths
{
// Access cell directly so we can animate it correctly.
@@ -1917,12 +2000,16 @@ extension MyAppsViewController: UICollectionViewDropDelegate
{
// Activating
- guard point.y > activeAppsHeaderAttributes.frame.minY else {
+ guard point.y > activeAppsHeaderAttributes.frame.minY
+ else
+ {
// Above active apps section.
return UICollectionViewDropProposal(operation: .cancel)
}
- guard point.y < inactiveAppsHeaderAttributes.frame.minY else {
+ guard point.y < inactiveAppsHeaderAttributes.frame.minY
+ else
+ {
// Inactive apps section.
return UICollectionViewDropProposal(operation: .move, intent: .insertAtDestinationIndexPath)
}
@@ -1940,13 +2027,17 @@ extension MyAppsViewController: UICollectionViewDropDelegate
// Not enough active app slots, so we need to deactivate an app.
// Provided destinationIndexPath is inaccurate.
- guard let indexPath = collectionView.indexPathForItem(at: point), indexPath.section == Section.activeApps.rawValue else {
+ guard let indexPath = collectionView.indexPathForItem(at: point), indexPath.section == Section.activeApps.rawValue
+ else
+ {
// Invalid destination index path.
return UICollectionViewDropProposal(operation: .cancel)
}
let installedApp = self.dataSource.item(at: indexPath)
- guard installedApp.bundleIdentifier != StoreApp.altstoreAppID else {
+ guard installedApp.bundleIdentifier != StoreApp.altstoreAppID
+ else
+ {
// Can't deactivate AltStore.
return UICollectionViewDropProposal(operation: .forbidden, intent: .insertIntoDestinationIndexPath)
}
@@ -1978,8 +2069,10 @@ extension MyAppsViewController: UICollectionViewDropDelegate
installedApp.isActive = true
let previousInstalledApp = self.dataSource.item(at: destinationIndexPath)
- self.deactivate(previousInstalledApp) { (result) in
- installedApp.managedObjectContext?.perform {
+ self.deactivate(previousInstalledApp)
+ { result in
+ installedApp.managedObjectContext?.perform
+ {
switch result
{
case .failure: installedApp.isActive = false
@@ -2053,7 +2146,8 @@ extension MyAppsViewController: UIDocumentPickerDelegate
switch controller.documentPickerMode
{
case .import, .open:
- self.sideloadApp(at: fileURL) { (result) in
+ self.sideloadApp(at: fileURL)
+ { result in
print("Sideloaded app at \(fileURL) with result:", result)
}
@@ -2079,7 +2173,7 @@ extension MyAppsViewController: UIViewControllerPreviewingDelegate
previewingContext.sourceRect = cell.frame
let app = self.dataSource.item(at: indexPath)
- guard let storeApp = app.storeApp else { return nil}
+ guard let storeApp = app.storeApp else { return nil }
let appViewController = AppViewController.makeAppViewController(app: storeApp)
return appViewController
@@ -2099,9 +2193,10 @@ extension MyAppsViewController: UIViewControllerPreviewingDelegate
extension MyAppsViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate
{
- func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any])
+ func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any])
{
- defer {
+ defer
+ {
picker.dismiss(animated: true, completion: nil)
self._imagePickerInstalledApp = nil
}
diff --git a/AltStore/Operations/AuthenticationOperation.swift b/AltStore/Operations/AuthenticationOperation.swift
index af510de2..b155ac20 100644
--- a/AltStore/Operations/AuthenticationOperation.swift
+++ b/AltStore/Operations/AuthenticationOperation.swift
@@ -7,11 +7,11 @@
//
import Foundation
-import Roxas
import Network
+import Roxas
-import AltStoreCore
import AltSign
+import AltStoreCore
enum AuthenticationError: LocalizedError
{
@@ -22,8 +22,10 @@ enum AuthenticationError: LocalizedError
case missingPrivateKey
case missingCertificate
- var errorDescription: String? {
- switch self {
+ var errorDescription: String?
+ {
+ switch self
+ {
case .noTeam: return NSLocalizedString("Developer team could not be found.", comment: "")
case .teamSelectorError: return NSLocalizedString("Error presenting team selector view.", comment: "")
case .noCertificate: return NSLocalizedString("Developer certificate could not be found.", comment: "")
@@ -82,7 +84,8 @@ final class AuthenticationOperation: ResultOperation<(ALTTeam, ALTCertificate, A
}
// Sign In
- self.signIn() { (result) in
+ self.signIn
+ { result in
guard !self.isCancelled else { return self.finish(.failure(OperationError.cancelled)) }
switch result
@@ -93,7 +96,8 @@ final class AuthenticationOperation: ResultOperation<(ALTTeam, ALTCertificate, A
self.progress.completedUnitCount += 1
// Fetch Team
- self.fetchTeam(for: account, session: session) { (result) in
+ self.fetchTeam(for: account, session: session)
+ { result in
guard !self.isCancelled else { return self.finish(.failure(OperationError.cancelled)) }
switch result
@@ -104,7 +108,8 @@ final class AuthenticationOperation: ResultOperation<(ALTTeam, ALTCertificate, A
self.progress.completedUnitCount += 1
// Fetch Certificate
- self.fetchCertificate(for: team, session: session) { (result) in
+ self.fetchCertificate(for: team, session: session)
+ { result in
guard !self.isCancelled else { return self.finish(.failure(OperationError.cancelled)) }
switch result
@@ -115,7 +120,8 @@ final class AuthenticationOperation: ResultOperation<(ALTTeam, ALTCertificate, A
self.progress.completedUnitCount += 1
// Register Device
- self.registerCurrentDevice(for: team, session: session) { (result) in
+ self.registerCurrentDevice(for: team, session: session)
+ { result in
guard !self.isCancelled else { return self.finish(.failure(OperationError.cancelled)) }
switch result
@@ -125,7 +131,8 @@ final class AuthenticationOperation: ResultOperation<(ALTTeam, ALTCertificate, A
self.progress.completedUnitCount += 1
// Save account/team to disk.
- self.save(team) { (result) in
+ self.save(team)
+ { result in
guard !self.isCancelled else { return self.finish(.failure(OperationError.cancelled)) }
switch result
@@ -133,7 +140,8 @@ final class AuthenticationOperation: ResultOperation<(ALTTeam, ALTCertificate, A
case .failure(let error): self.finish(.failure(error))
case .success:
// Must cache App IDs _after_ saving account/team to disk.
- self.cacheAppIDs(team: team, session: session) { (result) in
+ self.cacheAppIDs(team: team, session: session)
+ { result in
let result = result.map { _ in (team, certificate, session) }
self.finish(result)
}
@@ -152,7 +160,8 @@ final class AuthenticationOperation: ResultOperation<(ALTTeam, ALTCertificate, A
func save(_ altTeam: ALTTeam, completionHandler: @escaping (Result) -> Void)
{
let context = DatabaseManager.shared.persistentContainer.newBackgroundContext()
- context.performAndWait {
+ context.performAndWait
+ {
do
{
let account: Account
@@ -204,7 +213,8 @@ final class AuthenticationOperation: ResultOperation<(ALTTeam, ALTCertificate, A
print("Finished authenticating with result:", result.error?.localizedDescription ?? "success")
let context = DatabaseManager.shared.persistentContainer.newBackgroundContext()
- context.perform {
+ context.perform
+ {
do
{
let (altTeam, altCertificate, session) = try result.get()
@@ -241,7 +251,7 @@ final class AuthenticationOperation: ResultOperation<(ALTTeam, ALTCertificate, A
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
{
@@ -258,14 +268,16 @@ final class AuthenticationOperation: ResultOperation<(ALTTeam, ALTCertificate, A
Keychain.shared.signingCertificate = altCertificate.p12Data()
Keychain.shared.signingCertificatePassword = altCertificate.machineIdentifier
- self.showInstructionsIfNecessary() { (didShowInstructions) in
-
+ self.showInstructionsIfNecessary
+ { _ in
let signer = ALTSigner(team: altTeam, certificate: altCertificate)
// Refresh screen must go last since a successful refresh will cause the app to quit.
- self.showRefreshScreenIfNecessary(signer: signer, session: session) { (didShowRefreshAlert) in
+ self.showRefreshScreenIfNecessary(signer: signer, session: session)
+ { _ in
super.finish(result)
- DispatchQueue.main.async {
+ DispatchQueue.main.async
+ {
self.navigationController.dismiss(animated: true, completion: nil)
}
}
@@ -275,7 +287,8 @@ final class AuthenticationOperation: ResultOperation<(ALTTeam, ALTCertificate, A
{
super.finish(result)
- DispatchQueue.main.async {
+ DispatchQueue.main.async
+ {
self.navigationController.dismiss(animated: true, completion: nil)
}
}
@@ -295,7 +308,7 @@ private extension AuthenticationOperation
{
guard presentingViewController.presentedViewController == nil else { return false }
- self.navigationController.setViewControllers([viewController], animated: false)
+ self.navigationController.setViewControllers([viewController], animated: false)
presentingViewController.present(self.navigationController, animated: true, completion: nil)
}
else
@@ -314,14 +327,16 @@ private extension AuthenticationOperation
{
func authenticate()
{
- DispatchQueue.main.async {
+ DispatchQueue.main.async
+ {
let authenticationViewController = self.storyboard.instantiateViewController(withIdentifier: "authenticationViewController") as! AuthenticationViewController
- authenticationViewController.authenticationHandler = { (appleID, password, completionHandler) in
- self.authenticate(appleID: appleID, password: password) { (result) in
+ authenticationViewController.authenticationHandler = { appleID, password, completionHandler in
+ self.authenticate(appleID: appleID, password: password)
+ { result in
completionHandler(result)
}
}
- authenticationViewController.completionHandler = { (result) in
+ authenticationViewController.completionHandler = { result in
if let (account, session, password) = result
{
// We presented the Auth UI and the user signed in.
@@ -346,7 +361,8 @@ private extension AuthenticationOperation
if let appleID = Keychain.shared.appleIDEmailAddress, let password = Keychain.shared.appleIDPassword
{
- self.authenticate(appleID: appleID, password: password) { (result) in
+ self.authenticate(appleID: appleID, password: password)
+ { result in
switch result
{
case .success((let account, let session)):
@@ -372,7 +388,7 @@ private extension AuthenticationOperation
self.appleIDEmailAddress = appleID
let fetchAnisetteDataOperation = FetchAnisetteDataOperation(context: self.context)
- fetchAnisetteDataOperation.resultHandler = { (result) in
+ fetchAnisetteDataOperation.resultHandler = { result in
switch result
{
case .failure(let error): completionHandler(.failure(error))
@@ -381,10 +397,12 @@ private extension AuthenticationOperation
if let presentingViewController = self.presentingViewController
{
- verificationHandler = { (completionHandler) in
- DispatchQueue.main.async {
+ verificationHandler = { completionHandler in
+ DispatchQueue.main.async
+ {
let alertController = UIAlertController(title: NSLocalizedString("Please enter the 6-digit verification code that was sent to your Apple devices.", comment: ""), message: nil, preferredStyle: .alert)
- alertController.addTextField { (textField) in
+ alertController.addTextField
+ { textField in
textField.autocorrectionType = .no
textField.autocapitalizationType = .none
textField.keyboardType = .numberPad
@@ -392,7 +410,8 @@ private extension AuthenticationOperation
NotificationCenter.default.addObserver(self, selector: #selector(AuthenticationOperation.textFieldTextDidChange(_:)), name: UITextField.textDidChangeNotification, object: textField)
}
- let submitAction = UIAlertAction(title: NSLocalizedString("Continue", comment: ""), style: .default) { (action) in
+ let submitAction = UIAlertAction(title: NSLocalizedString("Continue", comment: ""), style: .default)
+ { _ in
let textField = alertController.textFields?.first
let code = textField?.text ?? ""
@@ -402,7 +421,8 @@ private extension AuthenticationOperation
alertController.addAction(submitAction)
self.submitCodeAction = submitAction
- alertController.addAction(UIAlertAction(title: RSTSystemLocalizedString("Cancel"), style: .cancel) { (action) in
+ alertController.addAction(UIAlertAction(title: RSTSystemLocalizedString("Cancel"), style: .cancel)
+ { _ in
completionHandler(nil)
})
@@ -424,7 +444,8 @@ private extension AuthenticationOperation
}
ALTAppleAPI.shared.authenticate(appleID: appleID, password: password, anisetteData: anisetteData,
- verificationHandler: verificationHandler) { (account, session, error) in
+ verificationHandler: verificationHandler)
+ { account, session, error in
if let account = account, let session = session
{
completionHandler(.success((account, session)))
@@ -443,34 +464,43 @@ private extension AuthenticationOperation
func fetchTeam(for account: ALTAccount, session: ALTAppleAPISession, completionHandler: @escaping (Result) -> Void)
{
func selectTeam(from teams: [ALTTeam])
- {
- if teams.count <= 1 {
- if let team = teams.first {
- return completionHandler(.success(team))
- } else {
- return completionHandler(.failure(AuthenticationError.noTeam))
- }
- } else {
- DispatchQueue.main.async {
- let selectTeamViewController = self.storyboard.instantiateViewController(withIdentifier: "selectTeamViewController") as! SelectTeamViewController
+ {
+ if teams.count <= 1
+ {
+ if let team = teams.first
+ {
+ return completionHandler(.success(team))
+ }
+ else
+ {
+ return completionHandler(.failure(AuthenticationError.noTeam))
+ }
+ }
+ else
+ {
+ DispatchQueue.main.async
+ {
+ let selectTeamViewController = self.storyboard.instantiateViewController(withIdentifier: "selectTeamViewController") as! SelectTeamViewController
- selectTeamViewController.teams = teams
- selectTeamViewController.completionHandler = completionHandler
+ selectTeamViewController.teams = teams
+ selectTeamViewController.completionHandler = completionHandler
- if !self.present(selectTeamViewController)
- {
- return completionHandler(.failure(AuthenticationError.noTeam))
- }
- }
- }
- }
+ if !self.present(selectTeamViewController)
+ {
+ return completionHandler(.failure(AuthenticationError.noTeam))
+ }
+ }
+ }
+ }
- ALTAppleAPI.shared.fetchTeams(for: account, session: session) { (teams, error) in
+ ALTAppleAPI.shared.fetchTeams(for: account, session: session)
+ { teams, error in
switch Result(teams, error)
{
case .failure(let error): completionHandler(.failure(error))
case .success(let teams):
- DatabaseManager.shared.persistentContainer.performBackgroundTask { (context) in
+ DatabaseManager.shared.persistentContainer.performBackgroundTask
+ { context in
if let activeTeam = DatabaseManager.shared.activeTeam(in: context), let altTeam = teams.first(where: { $0.identifier == activeTeam.identifier })
{
completionHandler(.success(altTeam))
@@ -489,18 +519,22 @@ private extension AuthenticationOperation
func requestCertificate()
{
let machineName = "AltStore - " + UIDevice.current.name
- ALTAppleAPI.shared.addCertificate(machineName: machineName, to: team, session: session) { (certificate, error) in
+ ALTAppleAPI.shared.addCertificate(machineName: machineName, to: team, session: session)
+ { certificate, error in
do
{
let certificate = try Result(certificate, error).get()
guard let privateKey = certificate.privateKey else { throw AuthenticationError.missingPrivateKey }
- ALTAppleAPI.shared.fetchCertificates(for: team, session: session) { (certificates, error) in
+ ALTAppleAPI.shared.fetchCertificates(for: team, session: session)
+ { certificates, error in
do
{
let certificates = try Result(certificates, error).get()
- guard let certificate = certificates.first(where: { $0.serialNumber == certificate.serialNumber }) else {
+ guard let certificate = certificates.first(where: { $0.serialNumber == certificate.serialNumber })
+ else
+ {
throw AuthenticationError.missingCertificate
}
@@ -524,7 +558,8 @@ private extension AuthenticationOperation
{
guard let certificate = certificates.first(where: { $0.machineName?.starts(with: "AltStore") == true }) ?? certificates.first else { return completionHandler(.failure(AuthenticationError.noCertificate)) }
- ALTAppleAPI.shared.revoke(certificate, for: team, session: session) { (success, error) in
+ ALTAppleAPI.shared.revoke(certificate, for: team, session: session)
+ { success, error in
if let error = error, !success
{
completionHandler(.failure(error))
@@ -536,7 +571,8 @@ private extension AuthenticationOperation
}
}
- ALTAppleAPI.shared.fetchCertificates(for: team, session: session) { (certificates, error) in
+ ALTAppleAPI.shared.fetchCertificates(for: team, session: session)
+ { certificates, error in
do
{
let certificates = try Result(certificates, error).get()
@@ -593,11 +629,14 @@ private extension AuthenticationOperation
func registerCurrentDevice(for team: ALTTeam, session: ALTAppleAPISession, completionHandler: @escaping (Result) -> Void)
{
- guard let udid = Bundle.main.object(forInfoDictionaryKey: Bundle.Info.deviceID) as? String else {
+ guard let udid = Bundle.main.object(forInfoDictionaryKey: Bundle.Info.deviceID) as? String
+ else
+ {
return completionHandler(.failure(OperationError.unknownUDID))
}
- ALTAppleAPI.shared.fetchDevices(for: team, types: [.iphone, .ipad], session: session) { (devices, error) in
+ ALTAppleAPI.shared.fetchDevices(for: team, types: [.iphone, .ipad], session: session)
+ { devices, error in
do
{
let devices = try Result(devices, error).get()
@@ -608,7 +647,8 @@ private extension AuthenticationOperation
}
else
{
- ALTAppleAPI.shared.registerDevice(name: UIDevice.current.name, identifier: udid, type: .iphone, team: team, session: session) { (device, error) in
+ ALTAppleAPI.shared.registerDevice(name: UIDevice.current.name, identifier: udid, type: .iphone, team: team, session: session)
+ { device, error in
completionHandler(Result(device, error))
}
}
@@ -623,7 +663,7 @@ private extension AuthenticationOperation
func cacheAppIDs(team: ALTTeam, session: ALTAppleAPISession, completionHandler: @escaping (Result) -> Void)
{
let fetchAppIDsOperation = FetchAppIDsOperation(context: self.context)
- fetchAppIDsOperation.resultHandler = { (result) in
+ fetchAppIDsOperation.resultHandler = { result in
do
{
let (_, context) = try result.get()
@@ -644,7 +684,8 @@ private extension AuthenticationOperation
{
guard self.shouldShowInstructions else { return completionHandler(false) }
- DispatchQueue.main.async {
+ DispatchQueue.main.async
+ {
let instructionsViewController = self.storyboard.instantiateViewController(withIdentifier: "instructionsViewController") as! InstructionsViewController
instructionsViewController.showsBottomButton = true
instructionsViewController.completionHandler = {
@@ -668,7 +709,8 @@ private extension AuthenticationOperation
#if DEBUG
completionHandler(false)
#else
- DispatchQueue.main.async {
+ DispatchQueue.main.async
+ {
let context = AuthenticatedOperationContext(context: self.context)
context.operations.removeAllObjects() // Prevent deadlock due to endless waiting on previous operations to finish.
diff --git a/AltStore/Operations/InstallAppOperation.swift b/AltStore/Operations/InstallAppOperation.swift
index f10c1272..068efe19 100644
--- a/AltStore/Operations/InstallAppOperation.swift
+++ b/AltStore/Operations/InstallAppOperation.swift
@@ -8,8 +8,8 @@
import Foundation
import Network
-import AltStoreCore
import AltSign
+import AltStoreCore
import Roxas
@objc(InstallAppOperation)
@@ -44,8 +44,8 @@ final class InstallAppOperation: ResultOperation
else { return self.finish(.failure(OperationError.invalidParameters)) }
let backgroundContext = DatabaseManager.shared.persistentContainer.newBackgroundContext()
- backgroundContext.perform {
-
+ backgroundContext.perform
+ {
/* App */
let installedApp: InstalledApp
@@ -141,8 +141,9 @@ final class InstallAppOperation: ResultOperation
installedApp.isActive = false
}
}
-
- activeProfiles = Set(activeApps.flatMap { (installedApp) -> [String] in
+
+ activeProfiles = Set(activeApps.flatMap
+ { installedApp -> [String] in
let appExtensionProfiles = installedApp.appExtensions.map { $0.resignedBundleIdentifier }
return [installedApp.resignedBundleIdentifier] + appExtensionProfiles
})
@@ -152,11 +153,50 @@ final class InstallAppOperation: ResultOperation
let ns_bundle_ptr = UnsafeMutablePointer(mutating: ns_bundle.utf8String)
let res = minimuxer_install_ipa(ns_bundle_ptr)
- if res == 0 {
+ if res == 0
+ {
installedApp.refreshedDate = Date()
self.finish(.success(installedApp))
-
- } else {
+ }
+ else if res == -15
+ {
+ // try again
+ if UserDefaults.standard.enableMacDirtyCowExploit && UserDefaults.standard.isMacDirtyCowSupported
+ {
+ patch3AppLimit
+ { result in
+ switch result
+ {
+ case .success:
+ UserDefaults.standard.set(bootTime(), forKey: "mdcRanBootTime")
+ print("patched sucessfully")
+ case .failure(let err):
+ switch err
+ {
+ case .NoFDA:
+ self.finish(.failure(OperationError.mdcNoFDA))
+ return
+ case .FailedPatchd:
+ self.finish(.failure(OperationError.mdcFailedPatchd))
+ return
+ }
+ }
+ }
+
+ let res_try_again = minimuxer_install_ipa(ns_bundle_ptr)
+ if res_try_again == 0
+ {
+ installedApp.refreshedDate = Date()
+ self.finish(.success(installedApp))
+ }
+ else
+ {
+ self.finish(.failure(minimuxer_to_operation(code: res_try_again)))
+ }
+ }
+ }
+ else
+ {
self.finish(.failure(minimuxer_to_operation(code: res)))
}
}
diff --git a/AltStore/Operations/OperationError.swift b/AltStore/Operations/OperationError.swift
index 9d118a82..464e9cb5 100644
--- a/AltStore/Operations/OperationError.swift
+++ b/AltStore/Operations/OperationError.swift
@@ -6,11 +6,10 @@
// Copyright © 2019 Riley Testut. All rights reserved.
//
-import Foundation
import AltSign
+import Foundation
-enum OperationError: LocalizedError
-{
+enum OperationError: LocalizedError {
static let domain = OperationError.unknown._domain
case unknown
@@ -45,6 +44,8 @@ enum OperationError: LocalizedError
case functionArguments
case profileInstall
case noConnection
+ case mdcNoFDA
+ case mdcFailedPatchd
var failureReason: String? {
switch self {
@@ -73,22 +74,21 @@ enum OperationError: LocalizedError
case .functionArguments: return NSLocalizedString("A function was passed invalid arguments", comment: "")
case .profileInstall: return NSLocalizedString("Unable to manage profiles on the device", comment: "")
case .noConnection: return NSLocalizedString("Unable to connect to the device, make sure Wireguard is enabled and you're connected to WiFi", comment: "")
+ case .mdcNoFDA: return NSLocalizedString("Unable to get Full Disk Access using MDC.", comment: "")
+ case .mdcFailedPatchd: return NSLocalizedString("Unable to patch installd using MDC.", comment: "")
}
}
var recoverySuggestion: String? {
- switch self
- {
+ switch self {
case .maximumAppIDLimitReached(let application, let requiredAppIDs, let availableAppIDs, let date):
let baseMessage = NSLocalizedString("Delete sideloaded apps to free up App ID slots.", comment: "")
let message: String
- if requiredAppIDs > 1
- {
+ if requiredAppIDs > 1 {
let availableText: String
- switch availableAppIDs
- {
+ switch availableAppIDs {
case 0: availableText = NSLocalizedString("none are available", comment: "")
case 1: availableText = NSLocalizedString("only 1 is available", comment: "")
default: availableText = String(format: NSLocalizedString("only %@ are available", comment: ""), NSNumber(value: availableAppIDs))
@@ -97,8 +97,7 @@ enum OperationError: LocalizedError
let prefixMessage = String(format: NSLocalizedString("%@ requires %@ App IDs, but %@.", comment: ""), application.name, NSNumber(value: requiredAppIDs), availableText)
message = prefixMessage + " " + baseMessage
}
- else
- {
+ else {
let dateComponents = Calendar.current.dateComponents([.day, .hour, .minute], from: Date(), to: date)
let dateComponentsFormatter = DateComponentsFormatter()
diff --git a/AltStore/Settings/Settings.storyboard b/AltStore/Settings/Settings.storyboard
index ccf9515c..f9042599 100644
--- a/AltStore/Settings/Settings.storyboard
+++ b/AltStore/Settings/Settings.storyboard
@@ -21,7 +21,7 @@