diff --git a/README.md b/README.md
index 20d2ac5..77a72a1 100644
--- a/README.md
+++ b/README.md
@@ -7,24 +7,17 @@
An on-device debugger/JIT enabler for iOS versions 17.4+ powered by idevice.
-
+
+
+
+
+
+
+
+
## Features
-- **JIT:** Enable Just In Time compilation for sideloaded apps that have the `get-task-allow` entitlement.
+- **JIT:** Enable Just-In-Time compilation for sideloaded apps that have the `get-task-allow` entitlement.
- **App Launching:** Launch every app installed on your device.
- **Console:** Live app and system logs.
- **Scripts:** Manage automation scripts (mainly used for iOS 26 JIT).
@@ -40,7 +33,7 @@
-
+
## Compatibility
@@ -69,12 +62,12 @@ StikDebug enables **JIT** for sideloaded apps on iOS 17.4+ without needing a com
- Launch LocalDevVPN and enable the VPN.
4. **Enable JIT for an app**
- - Launch StikDebug and tapp the `Enable JIT` button.
+ - Launch StikDebug and tap the `Enable JIT` button.
- Select your sideloaded app from the list in StikDebug.
**Troubleshooting**
- "Connection dropped" or loopback errors → Check iOS version compatibility / beta warnings.
-- Heartbeat errors → Ensure that the VPN is on and that you are connecected to Wi-Fi. It may be a pairing file issue.
+- Heartbeat errors → Ensure that the VPN is on and that you are connected to Wi-Fi. It may be a pairing file issue.
- Pairing file issues → Replace file with device unlocked & trusted.
- Still stuck? Join the [Discord](https://discord.gg/ZnNcrRT3M8) with logs/screenshots.
@@ -147,13 +140,11 @@ To propose a new feature, open a feature request issue and provide:
### Code Contributions (Best Practices)
- Follow normal Swift and SwiftUI style.
-- Write clear and easy to understand code.
+- Write clear and easy-to-understand code.
- Keep your changes consistent with how the project is already set up.
- Make sure everything builds and works without errors.
We appreciate your time and effort in helping improve this project.
-## Code Help
-[](https://deepwiki.com/StikDebug/stikdebug)
## License
StikDebug is licensed under **AGPL-3.0**. See [`LICENSE`](LICENSE) for details.
diff --git a/StikDebug.xcodeproj/project.pbxproj b/StikDebug.xcodeproj/project.pbxproj
index b48192d..02930a2 100644
--- a/StikDebug.xcodeproj/project.pbxproj
+++ b/StikDebug.xcodeproj/project.pbxproj
@@ -482,7 +482,7 @@
"$(PROJECT_DIR)/StikDebug/idevice",
);
MACOSX_DEPLOYMENT_TARGET = 15.1;
- MARKETING_VERSION = 3.1.13;
+ MARKETING_VERSION = 3.1.15;
PRODUCT_BUNDLE_IDENTIFIER = com.stik.stikdebug;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
@@ -544,7 +544,7 @@
"$(PROJECT_DIR)/StikDebug/idevice",
);
MACOSX_DEPLOYMENT_TARGET = 15.1;
- MARKETING_VERSION = 3.1.13;
+ MARKETING_VERSION = 3.1.15;
PRODUCT_BUNDLE_IDENTIFIER = com.stik.stikdebug;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
diff --git a/StikDebug/Device/IdeviceFFIBridge.swift b/StikDebug/Device/IdeviceFFIBridge.swift
index 3e1c98e..2c2c805 100644
--- a/StikDebug/Device/IdeviceFFIBridge.swift
+++ b/StikDebug/Device/IdeviceFFIBridge.swift
@@ -39,7 +39,8 @@ private enum IdeviceBridge {
}
let code = Int(ffiError.pointee.code)
- let message = string(from: ffiError.pointee.message) ?? fallback
+ let detail = string(from: ffiError.pointee.message) ?? fallback
+ let message = detail == fallback ? fallback : "\(fallback): \(detail)"
idevice_error_free(ffiError)
return makeError(domain: domain, code: code, message: message)
}
@@ -264,45 +265,50 @@ private enum IdeviceBridge {
extension JITEnableContext {
func isDeveloperDiskImageMounted() throws -> Bool {
try IdeviceBridge.withTunnelHandles(for: self) { adapter, handshake in
- var installed: UnsafeMutablePointer?
- let ffiError = cryptexd_installed_ddi(adapter, handshake, &installed)
- let queryError = ffiError.map {
- IdeviceBridge.consumeFFIError($0, fallback: "Failed to query installed DDI cryptex")
- }
- defer { cryptexd_free_installed_cryptex(installed) }
- if installed != nil {
- return true
+ var cryptexQuerySucceeded = false
+ var cryptexQueryError: NSError?
+ if DeveloperDiskImageService.usesCryptexDDI {
+ var installed: UnsafeMutablePointer?
+ defer { cryptexd_free_installed_cryptex(installed) }
+ if let ffiError = cryptexd_installed_ddi(adapter, handshake, &installed) {
+ cryptexQueryError = IdeviceBridge.consumeFFIError(ffiError, fallback: "Failed to query installed DDI cryptex")
+ } else {
+ cryptexQuerySucceeded = true
+ if installed != nil {
+ return true
+ }
+ }
}
- let legacyMounted = (try? IdeviceBridge.withConnectedClient(
- fallback: "Failed to connect to image mounter",
- missingClientMessage: "Image mounter client was not created",
- connect: { image_mounter_connect_rsd(adapter, handshake, $0) },
- cleanup: { image_mounter_free($0) }
- ) { client in
- var devices: UnsafeMutablePointer?
- var deviceCount = 0
- if let error = image_mounter_copy_devices(client, &devices, &deviceCount) {
- throw IdeviceBridge.consumeFFIError(error, fallback: "Failed to fetch mounted devices")
- }
- if let devices {
- for index in 0..?
+ var deviceCount = 0
+ if let error = image_mounter_copy_devices(client, &devices, &deviceCount) {
+ throw IdeviceBridge.consumeFFIError(error, fallback: "Failed to fetch mounted devices")
}
- idevice_data_free(
- UnsafeMutableRawPointer(devices).assumingMemoryBound(to: UInt8.self),
- UInt(deviceCount * MemoryLayout.stride)
- )
+ if let devices {
+ for index in 0...stride)
+ )
+ }
+ return deviceCount > 0
}
- return deviceCount > 0
- }) ?? false
- if legacyMounted {
- return true
+ } catch {
+ if cryptexQuerySucceeded {
+ return false
+ }
+ throw cryptexQueryError ?? (error as NSError)
}
- if let queryError {
- throw queryError
- }
- return false
}
}
diff --git a/StikDebug/Device/JITEnableContext.swift b/StikDebug/Device/JITEnableContext.swift
index 9a37cf2..d2c65f0 100644
--- a/StikDebug/Device/JITEnableContext.swift
+++ b/StikDebug/Device/JITEnableContext.swift
@@ -117,7 +117,8 @@ final class JITEnableContext {
guard let ffiError else {
return makeError(fallback)
}
- let message = nsString(from: ffiError.pointee.message, fallback: fallback)
+ let detail = nsString(from: ffiError.pointee.message, fallback: fallback)
+ let message = detail == fallback ? fallback : "\(fallback): \(detail)"
let error = makeError(message, code: Int(ffiError.pointee.code))
idevice_error_free(ffiError)
return error
@@ -457,22 +458,19 @@ final class JITEnableContext {
private func withConnectedRemoteServer(
_ body: (OpaquePointer) throws -> T
) throws -> T {
- try ensureTunnel()
- guard let adapter, let handshake else {
- throw makeError("Tunnel is not connected")
- }
+ try withFreshDebugTunnel(hostname: "StikDebugRemoteServer") { adapter, handshake in
+ var remoteServer: OpaquePointer?
+ if let ffiError = remote_server_connect_rsd(adapter, handshake, &remoteServer) {
+ throw error(from: ffiError, fallback: "Failed to connect remote server")
+ }
- var remoteServer: OpaquePointer?
- if let ffiError = remote_server_connect_rsd(adapter, handshake, &remoteServer) {
- throw error(from: ffiError, fallback: "Failed to connect remote server")
- }
+ guard let remoteServer else {
+ throw makeError("Remote server handle was not created")
+ }
- guard let remoteServer else {
- throw makeError("Remote server handle was not created")
+ defer { remote_server_free(remoteServer) }
+ return try body(remoteServer)
}
-
- defer { remote_server_free(remoteServer) }
- return try body(remoteServer)
}
private func withProcessControl(
diff --git a/StikDebug/Device/mountDDI.swift b/StikDebug/Device/mountDDI.swift
index 61128b5..0716ebb 100644
--- a/StikDebug/Device/mountDDI.swift
+++ b/StikDebug/Device/mountDDI.swift
@@ -27,6 +27,7 @@ func checkMountStatus() -> MountCheckResult {
do {
return try JITEnableContext.shared.isDeveloperDiskImageMounted() ? .mounted : .notMounted
} catch {
+ LogManager.shared.addErrorLog("Failed to check DDI mount status: \(error.localizedDescription)")
return .unreachable
}
}
diff --git a/StikDebug/Support/AutoScriptAssignments.swift b/StikDebug/Support/AutoScriptAssignments.swift
index 4c6934f..57eabd9 100644
--- a/StikDebug/Support/AutoScriptAssignments.swift
+++ b/StikDebug/Support/AutoScriptAssignments.swift
@@ -23,7 +23,8 @@ extension ScriptStore {
"HyperHLE",
"Applesauce",
"RPCS3",
- "AetherPS4"
+ "AetherPS4",
+ "Husk"
],
resource: ScriptResource(resourceName: "universal", fileName: "universal.js")
),
diff --git a/StikDebug/Views/SettingsView.swift b/StikDebug/Views/SettingsView.swift
index 871fe31..fc4bc3e 100644
--- a/StikDebug/Views/SettingsView.swift
+++ b/StikDebug/Views/SettingsView.swift
@@ -7,7 +7,7 @@ import SwiftUI
import UIKit
private enum SettingsLinks {
- static let githubStars = URL(string: "https://github.com/StikDebug/StikDebug/stargazers")!
+ static let githubStars = URL(string: "https://github.com/StikDebug/StikDebug")!
static let pairingFileGuide = URL(string: "https://github.com/StikDebug/StikDebug-Guide/blob/main/pairing_file.md")!
static let localDevVPN = URL(string: "https://apps.apple.com/us/app/localdevvpn/id6755608044")!
static let discord = URL(string: "https://discord.gg/qahjXNTDwS")!