diff --git a/AltStore.xcodeproj/project.pbxproj b/AltStore.xcodeproj/project.pbxproj index a831d74c..90e289e4 100644 --- a/AltStore.xcodeproj/project.pbxproj +++ b/AltStore.xcodeproj/project.pbxproj @@ -20,6 +20,7 @@ 4879A95F2861046500FC1BBD /* AltSign in Frameworks */ = {isa = PBXBuildFile; productRef = 4879A95E2861046500FC1BBD /* AltSign */; }; 4879A9622861049C00FC1BBD /* OpenSSL in Frameworks */ = {isa = PBXBuildFile; productRef = 4879A9612861049C00FC1BBD /* OpenSSL */; }; 990D2AE22A1910CD0055D93C /* UnstableFeatures.swift in Sources */ = {isa = PBXBuildFile; fileRef = 990D2AE12A1910CD0055D93C /* UnstableFeatures.swift */; }; + 990D2AF02A192E060055D93C /* UIApplication+Alert.swift in Sources */ = {isa = PBXBuildFile; fileRef = 990D2AEF2A192E060055D93C /* UIApplication+Alert.swift */; }; 9922FFEC29B501C50020F868 /* Starscream in Frameworks */ = {isa = PBXBuildFile; productRef = 9922FFEB29B501C50020F868 /* Starscream */; }; 99C4EF4D2979132100CB538D /* SemanticVersion in Frameworks */ = {isa = PBXBuildFile; productRef = 99C4EF4C2979132100CB538D /* SemanticVersion */; }; 99F87D0529D8B4E200B40039 /* minimuxer-helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9961EC2D29BE9F2E00AF2C6F /* minimuxer-helpers.swift */; }; @@ -513,6 +514,7 @@ 1920B04E2924AC8300744F60 /* Settings.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = Settings.bundle; sourceTree = ""; }; 19B9B7442845E6DF0076EF69 /* SelectTeamViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectTeamViewController.swift; sourceTree = ""; }; 990D2AE12A1910CD0055D93C /* UnstableFeatures.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnstableFeatures.swift; sourceTree = ""; }; + 990D2AEF2A192E060055D93C /* UIApplication+Alert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIApplication+Alert.swift"; sourceTree = ""; }; 9961EC2D29BE9F2E00AF2C6F /* minimuxer-helpers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "minimuxer-helpers.swift"; path = "Dependencies/minimuxer/minimuxer-helpers.swift"; sourceTree = SOURCE_ROOT; }; 99F87D1629D8E4C900B40039 /* SwiftBridgeCore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SwiftBridgeCore.swift; path = Dependencies/minimuxer/SwiftBridgeCore.swift; sourceTree = SOURCE_ROOT; }; 99F87D1729D8E4C900B40039 /* minimuxer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = minimuxer.swift; path = Dependencies/minimuxer/minimuxer.swift; sourceTree = SOURCE_ROOT; }; @@ -1656,6 +1658,7 @@ BFE00A1F2503097F00EB4D0C /* INInteraction+AltStore.swift */, D57F2C9326E01BC700B9FA39 /* UIDevice+Vibration.swift */, B376FE3D29258C8900E18883 /* OSLog+SideStore.swift */, + 990D2AEF2A192E060055D93C /* UIApplication+Alert.swift */, ); path = Extensions; sourceTree = ""; @@ -2497,6 +2500,7 @@ BF6C336224197D700034FD24 /* NSError+AltStore.swift in Sources */, D5DAE0942804B0B80034D8D4 /* ScreenshotProcessor.swift in Sources */, BFD2476E2284B9A500981D42 /* AppDelegate.swift in Sources */, + 990D2AF02A192E060055D93C /* UIApplication+Alert.swift in Sources */, BF41B806233423AE00C593A3 /* TabBarController.swift in Sources */, BFE00A202503097F00EB4D0C /* INInteraction+AltStore.swift in Sources */, BFDB6A0B22AAEDB7007EA6D6 /* Operation.swift in Sources */, diff --git a/AltStore/Extensions/UIApplication+Alert.swift b/AltStore/Extensions/UIApplication+Alert.swift new file mode 100644 index 00000000..e237eda6 --- /dev/null +++ b/AltStore/Extensions/UIApplication+Alert.swift @@ -0,0 +1,26 @@ +// +// UIApplication+Alert.swift +// SideStore +// +// Created by naturecodevoid on 5/20/23. +// Copyright © 2023 SideStore. All rights reserved. +// + +extension UIApplication { + static func alertOk(title: String?, message: String?) { + let alert = UIAlertController(title: title, message: message, preferredStyle: .alert) + alert.addAction(UIAlertAction(title: NSLocalizedString("Ok", comment: ""), style: .default)) + + DispatchQueue.main.async { + let keyWindow = UIApplication.shared.windows.filter { $0.isKeyWindow }.first + if var topController = keyWindow?.rootViewController { + while let presentedViewController = topController.presentedViewController { + topController = presentedViewController + } + topController.present(alert, animated: true) + } else { + print("No key window!") + } + } + } +} diff --git a/AltStore/SceneDelegate.swift b/AltStore/SceneDelegate.swift index c195f3ea..6b9d2a06 100644 --- a/AltStore/SceneDelegate.swift +++ b/AltStore/SceneDelegate.swift @@ -9,6 +9,7 @@ import UIKit import AltStoreCore import EmotionalDamage +import minimuxer @available(iOS 13, *) final class SceneDelegate: UIResponder, UIWindowSceneDelegate @@ -92,6 +93,7 @@ private extension SceneDelegate { guard let components = URLComponents(url: context.url, resolvingAgainstBaseURL: false) else { return } guard let host = components.host?.lowercased() else { return } + let queryItems = components.queryItems?.reduce(into: [String: String]()) { $0[$1.name.lowercased()] = $1.value } ?? [:] switch host { @@ -107,7 +109,6 @@ private extension SceneDelegate { case "/success": result = .success(()) case "/failure": - let queryItems = components.queryItems?.reduce(into: [String: String]()) { $0[$1.name] = $1.value } ?? [:] guard let errorDomain = queryItems["errorDomain"], let errorCodeString = queryItems["errorCode"], let errorCode = Int(errorCodeString), @@ -125,7 +126,6 @@ private extension SceneDelegate } case "install": - let queryItems = components.queryItems?.reduce(into: [String: String]()) { $0[$1.name.lowercased()] = $1.value } ?? [:] guard let downloadURLString = queryItems["url"], let downloadURL = URL(string: downloadURLString) else { return } DispatchQueue.main.async { @@ -133,13 +133,34 @@ private extension SceneDelegate } case "source": - let queryItems = components.queryItems?.reduce(into: [String: String]()) { $0[$1.name.lowercased()] = $1.value } ?? [:] guard let sourceURLString = queryItems["url"], let sourceURL = URL(string: sourceURLString) else { return } DispatchQueue.main.async { NotificationCenter.default.post(name: AppDelegate.addSourceDeepLinkNotification, object: nil, userInfo: [AppDelegate.addSourceDeepLinkURLKey: sourceURL]) } + case "sidejit-enable": + guard UnstableFeatures.enabled(.jitUrlScheme) else { return UIApplication.alertOk(title: "JIT URL scheme unstable feature is not enabled", message: nil) } + + if let bundleID = queryItems["bid"] { + DispatchQueue.main.async { + do { + try debug_app(bundleID) + } catch { + UIApplication.alertOk(title: "An error occurred when enabling JIT", message: error.localizedDescription) + } + } + } else if let processID = queryItems["pid"] { + DispatchQueue.main.async { + do { + guard let processID = UInt32(processID) else { return UIApplication.alertOk(title: "An error occurred when enabling JIT", message: "Process ID is not a valid unsigned integer") } + try attach_debugger(processID) + } catch { + UIApplication.alertOk(title: "An error occurred when enabling JIT", message: error.localizedDescription) + } + } + } else { return UIApplication.alertOk(title: "An error occurred when enabling JIT", message: "Please specify a bundle ID using the `bid` query parameter or a process ID using `pid` query parameter") } + default: break } } diff --git a/AltStore/Unstable Features/UnstableFeatures.swift b/AltStore/Unstable Features/UnstableFeatures.swift index 975b3449..7c8f8915 100644 --- a/AltStore/Unstable Features/UnstableFeatures.swift +++ b/AltStore/Unstable Features/UnstableFeatures.swift @@ -12,6 +12,8 @@ enum AvailableUnstableFeature: String, CaseIterable { // // Unstable features must have a GitHub Issue for tracking progress, PRs and feedback/commenting. + case jitUrlScheme = "0" + /// Dummy variant to ensure there is always at least one variant. DO NOT USE! case dummy = "dummy" @@ -20,6 +22,7 @@ enum AvailableUnstableFeature: String, CaseIterable { // If your unstable feature is stable enough to be used by nightly users who are not alpha testers or developers, // you may want to have it available in the "Unstable Features" menu in Settings (outside of dev mode). To do so, add this: //case .yourFeature: return true + case .jitUrlScheme: return true default: return false }