// // StikJITApp.swift // StikJIT // // Created by Stephen on 3/26/25. // import SwiftUI import Network import UniformTypeIdentifiers struct AccentColorKey: EnvironmentKey { static let defaultValue: Color = .blue } extension EnvironmentValues { var accentColor: Color { get { self[AccentColorKey.self] } set { self[AccentColorKey.self] = newValue } } } let fileManager = FileManager.default func httpGet(_ urlString: String, result: @escaping (String?) -> Void) { if let url = URL(string: urlString) { let task = URLSession.shared.dataTask(with: url) { data, response, error in if let error = error { print("Error: \(error.localizedDescription)") result(nil) return } if let data = data, let httpResponse = response as? HTTPURLResponse { if httpResponse.statusCode == 200 { print("Response: \(httpResponse.statusCode)") if let dataString = String(data: data, encoding: .utf8) { result(dataString) } } else { print("Received non-200 status code: \(httpResponse.statusCode)") } } } task.resume() } } func UpdateRetrieval() -> Bool { var ver: String { let marketingVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "1.0" return marketingVersion } let urlString = "https://raw.githubusercontent.com/0-Blu/StikJIT/refs/heads/main/version.txt" var res = false httpGet(urlString) { result in if let fc = result { if ver != fc { res = true } } } return res } class DNSChecker: ObservableObject { @Published var appleIP: String? @Published var controlIP: String? @Published var dnsError: String? func checkDNS() { checkIfConnectedToWifi { [weak self] wifiConnected in guard let self = self else { return } if wifiConnected { let group = DispatchGroup() group.enter() self.lookupIPAddress(for: "gs.apple.com") { ip in DispatchQueue.main.async { self.appleIP = ip } group.leave() } group.enter() self.lookupIPAddress(for: "google.com") { ip in DispatchQueue.main.async { self.controlIP = ip } group.leave() } group.notify(queue: DispatchQueue.main) { if self.controlIP == nil { self.dnsError = "No internet connection." print("Control host lookup failed, so no internet connection.") } else if self.appleIP == nil { self.dnsError = "Apple DNS blocked. Your network might be filtering Apple traffic." print("Control lookup succeeded, but Apple lookup failed: likely blocked.") } else { self.dnsError = nil print("DNS lookups succeeded: Apple -> \(self.appleIP!), Control -> \(self.controlIP!)") } } } else { DispatchQueue.main.async { self.dnsError = "Not connected to WiFi." print("Not connected to WiFi; skipping DNS check.") } } } } private func checkIfConnectedToWifi(completion: @escaping (Bool) -> Void) { let monitor = NWPathMonitor(requiredInterfaceType: .wifi) monitor.pathUpdateHandler = { path in if path.status == .satisfied { completion(true) } else { completion(false) } monitor.cancel() } let queue = DispatchQueue.global(qos: .background) monitor.start(queue: queue) } private func lookupIPAddress(for host: String, completion: @escaping (String?) -> Void) { DispatchQueue.global(qos: .background).async { var hints = addrinfo( ai_flags: 0, ai_family: AF_UNSPEC, ai_socktype: SOCK_STREAM, ai_protocol: 0, ai_addrlen: 0, ai_canonname: nil, ai_addr: nil, ai_next: nil ) var res: UnsafeMutablePointer? let err = getaddrinfo(host, nil, &hints, &res) if err != 0 { DispatchQueue.main.async { completion(nil) } return } var ipAddress: String? var ptr = res while ptr != nil { if let addr = ptr?.pointee.ai_addr { var hostBuffer = [CChar](repeating: 0, count: Int(NI_MAXHOST)) if getnameinfo(addr, ptr!.pointee.ai_addrlen, &hostBuffer, socklen_t(hostBuffer.count), nil, 0, NI_NUMERICHOST) == 0 { ipAddress = String(cString: hostBuffer) break } } ptr = ptr?.pointee.ai_next } freeaddrinfo(res) DispatchQueue.main.async { completion(ipAddress) } } } } @main struct HeartbeatApp: App { @State private var isLoading2 = true @State private var isPairing = false @State private var heartBeat = false @State private var error: Int32? = nil @State private var show_alert = false @State private var alert_string = "" @State private var alert_title = "" @StateObject private var mount = MountingProgress.shared @StateObject private var dnsChecker = DNSChecker() // New DNS check state object @AppStorage("appTheme") private var appTheme: String = "system" @Environment(\.scenePhase) private var scenePhase // Observe scene lifecycle let urls: [String] = [ "https://github.com/doronz88/DeveloperDiskImage/raw/refs/heads/main/PersonalizedImages/Xcode_iOS_DDI_Personalized/BuildManifest.plist", "https://github.com/doronz88/DeveloperDiskImage/raw/refs/heads/main/PersonalizedImages/Xcode_iOS_DDI_Personalized/Image.dmg", "https://github.com/doronz88/DeveloperDiskImage/raw/refs/heads/main/PersonalizedImages/Xcode_iOS_DDI_Personalized/Image.dmg.trustcache" ] let outputDir: String = "DDI" let outputFiles: [String] = [ "DDI/BuildManifest.plist", "DDI/Image.dmg", "DDI/Image.dmg.trustcache" ] init() { newVerCheck() let fixMethod = class_getInstanceMethod(UIDocumentPickerViewController.self, #selector(UIDocumentPickerViewController.fix_init(forOpeningContentTypes:asCopy:)))! let origMethod = class_getInstanceMethod(UIDocumentPickerViewController.self, #selector(UIDocumentPickerViewController.init(forOpeningContentTypes:asCopy:)))! method_exchangeImplementations(origMethod, fixMethod) if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene, let window = windowScene.windows.first { switch appTheme { case "dark": window.overrideUserInterfaceStyle = .dark case "light": window.overrideUserInterfaceStyle = .light default: window.overrideUserInterfaceStyle = .unspecified } } } func newVerCheck() { let currentDate = Calendar.current.startOfDay(for: Date()) let VUA = UserDefaults.standard.object(forKey: "VersionUpdateAlert") as? Date ?? Date.distantPast if currentDate > Calendar.current.startOfDay(for: VUA) { if UpdateRetrieval() { alert_title = "Update Avaliable!" let urlString = "https://raw.githubusercontent.com/0-Blu/StikJIT/refs/heads/main/version.txt" httpGet(urlString) { result in if result == nil { return } alert_string = "Update to: version \(result!)!" show_alert = true } } UserDefaults.standard.set(currentDate, forKey: "VersionUpdateAlert") } } private func applyTheme() { if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene, let window = windowScene.windows.first { switch appTheme { case "dark": window.overrideUserInterfaceStyle = .dark case "light": window.overrideUserInterfaceStyle = .light default: window.overrideUserInterfaceStyle = .unspecified } } } var body: some Scene { WindowGroup { if isLoading2 { LoadingView() .onAppear { dnsChecker.checkDNS() startProxy() { result, error in if result { checkVPNConnection() { result, vpn_error in if result { if FileManager.default.fileExists(atPath: URL.documentsDirectory.appendingPathComponent("pairingFile.plist").path) { Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true) { timer in if pubHeartBeat { isLoading2 = false timer.invalidate() } else { if let error { if error == InvalidHostID.rawValue { isPairing = true } else { startHeartbeatInBackground() } self.error = nil } } } startHeartbeatInBackground() } else { isLoading2 = false } } else if let vpn_error { showAlert(title: "Error", message: "EM Proxy failed to connect: \(vpn_error)", showOk: true) { _ in exit(0) } } } } else if let error { showAlert(title: "Error", message: "EM Proxy Failed to start \(error)", showOk: true) { _ in } } } } .fileImporter(isPresented: $isPairing, allowedContentTypes: [UTType(filenameExtension: "mobiledevicepairing", conformingTo: .data)!, .propertyList]) { result in switch result { case .success(let url): let fileManager = FileManager.default let accessing = url.startAccessingSecurityScopedResource() if fileManager.fileExists(atPath: url.path) { do { if fileManager.fileExists(atPath: URL.documentsDirectory.appendingPathComponent("pairingFile.plist").path) { try fileManager.removeItem(at: URL.documentsDirectory.appendingPathComponent("pairingFile.plist")) } try fileManager.copyItem(at: url, to: URL.documentsDirectory.appendingPathComponent("pairingFile.plist")) print("File copied successfully!") startHeartbeatInBackground() } catch { print("Error copying file: \(error)") } } else { print("Source file does not exist.") } if accessing { url.stopAccessingSecurityScopedResource() } case .failure(_): print("Failed") } } } else { MainTabView() .onAppear { applyTheme() let fileManager = FileManager.default for (index, urlString) in urls.enumerated() { let destinationURL = URL.documentsDirectory.appendingPathComponent(outputFiles[index]) if !fileManager.fileExists(atPath: destinationURL.path) { downloadFile(from: urlString, to: destinationURL) { result in if (result != "") { alert_title = "An Error has Occurred" alert_string = "[Download DDI Error]: " + result show_alert = true } } } } } .overlay( ZStack { if show_alert { CustomErrorView( title: alert_title, message: alert_string, onDismiss: { show_alert = false }, showButton: true, primaryButtonText: "OK" ) } } ) } } .onChange(of: scenePhase) { newPhase in if newPhase == .active { print("App became active – restarting heartbeat") startHeartbeatInBackground() } } .onChange(of: dnsChecker.dnsError) { newError in if let errorMsg = newError { alert_title = "Network Issue" alert_string = errorMsg show_alert = true } } } func startProxy(callback: @escaping (Bool, Int?) -> Void) { let port = 51820 let bindAddr = "127.0.0.1:\(port)" DispatchQueue.global(qos: .background).async { let result = start_emotional_damage(bindAddr) DispatchQueue.main.async { if result == 0 { print("DEBUG: em_proxy started successfully on port \(port)") callback(true, nil) } else { print("DEBUG: Failed to start em_proxy") callback(false, Int(result)) } } } } private func checkVPNConnection(callback: @escaping (Bool, String?) -> Void) { let host = NWEndpoint.Host("10.7.0.1") let port = NWEndpoint.Port(rawValue: 62078)! let connection = NWConnection(host: host, port: port, using: .tcp) var timeoutWorkItem: DispatchWorkItem? timeoutWorkItem = DispatchWorkItem { [weak connection] in if connection?.state != .ready { connection?.cancel() DispatchQueue.main.async { if timeoutWorkItem?.isCancelled == false { callback(false, "[TIMEOUT] The loopback VPN is not connected. Try closing this app, turn it off and back on.") } } } } connection.stateUpdateHandler = { [weak connection] state in switch state { case .ready: timeoutWorkItem?.cancel() connection?.cancel() DispatchQueue.main.async { callback(true, nil) } case .failed(let error): timeoutWorkItem?.cancel() connection?.cancel() DispatchQueue.main.async { if error == NWError.posix(.ETIMEDOUT) { callback(false, "The loopback VPN is not connected. Try closing the app, turn it off and back on.") } else if error == NWError.posix(.ECONNREFUSED) { callback(false, "Wifi is not connected. StikJIT won't work on cellular data.") } else { callback(false, "em proxy check error: \(error.localizedDescription)") } } default: break } } connection.start(queue: .global()) if let workItem = timeoutWorkItem { DispatchQueue.global().asyncAfter(deadline: .now() + 20, execute: workItem) } } } var pubHeartBeat = false actor FunctionGuard { private var runningTask: Task? func execute(_ work: @escaping @Sendable () -> T) async -> T { if let task = runningTask { return await task.value // Return existing task's result if running } let task = Task.detached { work() } runningTask = task let result = await task.value runningTask = nil return result } } class MountingProgress: ObservableObject { static var shared = MountingProgress() @Published var mountProgress: Double = 0.0 @Published var mountingThread: Thread? @Published var coolisMounted: Bool = false func checkforMounted() { DispatchQueue.main.async { self.coolisMounted = isMounted() } } func progressCallback(progress: size_t, total: size_t, context: UnsafeMutableRawPointer?) { let percentage = Double(progress) / Double(total) * 100.0 print("Mounting progress: \(percentage)%") DispatchQueue.main.async { self.mountProgress = percentage } } func pubMount() { mount() } private func mount() { self.coolisMounted = isMounted() let fileManager = FileManager.default let pairingpath = URL.documentsDirectory.appendingPathComponent("pairingFile.plist").path if isPairing(), !isMounted() { if let mountingThread = mountingThread { mountingThread.cancel() self.mountingThread = nil } mountingThread = Thread { let mountResult = mountPersonalDDI( imagePath: URL.documentsDirectory.appendingPathComponent("DDI/Image.dmg").path, trustcachePath: URL.documentsDirectory.appendingPathComponent("DDI/Image.dmg.trustcache").path, manifestPath: URL.documentsDirectory.appendingPathComponent("DDI/BuildManifest.plist").path, pairingFilePath: pairingpath ) if mountResult != 0 { showAlert(title: "Error", message: "An Error Occured when Mounting the DDI\nError Code: \(mountResult)", showOk: true, showTryAgain: true) { shouldTryAgain in if shouldTryAgain { self.mount() } } } else { DispatchQueue.main.async { self.coolisMounted = isMounted() } } } mountingThread!.qualityOfService = .background mountingThread!.name = "mounting" mountingThread!.start() } } } func isPairing() -> Bool { let pairingpath = URL.documentsDirectory.appendingPathComponent("pairingFile.plist").path var pairingFile: IdevicePairingFile? let err = idevice_pairing_file_read(pairingpath, &pairingFile) if err != IdeviceSuccess { print("Failed to read pairing file: \(err)") return false } return true } func startHeartbeatInBackground() { let heartBeatThread = Thread { let completionHandler: @convention(block) (Int32, String?) -> Void = { result, message in if result == 0 { print("Heartbeat started successfully: \(message ?? "")") pubHeartBeat = true if FileManager.default.fileExists(atPath: URL.documentsDirectory.appendingPathComponent("DDI/Image.dmg.trustcache").path) { MountingProgress.shared.pubMount() } Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true) { timer in if !isHeartbeat { startHeartbeatInBackground() timer.invalidate() } } } else { print("Error: \(message ?? "") (Code: \(result))") DispatchQueue.main.async { showAlert( title: "Heartbeat Error", message: "Failed to connect to Heartbeat (\(result))", showOk: false, showTryAgain: true ) { shouldTryAgain in if shouldTryAgain { startHeartbeatInBackground() } } } } } JITEnableContext.shared.startHeartbeat(completionHandler: completionHandler, logger: nil) } heartBeatThread.qualityOfService = .background heartBeatThread.name = "Heartbeat" heartBeatThread.start() } struct LoadingView: View { @State private var animate = false @Environment(\.colorScheme) private var colorScheme @AppStorage("customAccentColor") private var customAccentColorHex: String = "" @AppStorage("appTheme") private var appTheme: String = "system" private var accentColor: Color { if customAccentColorHex.isEmpty { return .blue } else { return Color(hex: customAccentColorHex) ?? .blue } } private var isDarkMode: Bool { switch appTheme { case "dark": return true case "light": return false default: return colorScheme == .dark } } var body: some View { ZStack { Color(isDarkMode ? .black : .white) .ignoresSafeArea() VStack { ZStack { Circle() .stroke(lineWidth: 8) .foregroundColor(isDarkMode ? Color.white.opacity(0.3) : Color.black.opacity(0.1)) .frame(width: 80, height: 80) Circle() .trim(from: 0, to: 0.7) .stroke(AngularGradient( gradient: Gradient(colors: [ accentColor.opacity(0.8), accentColor.opacity(0.3) ]), center: .center ), style: StrokeStyle(lineWidth: 6, lineCap: .round)) .rotationEffect(.degrees(animate ? 360 : 0)) .frame(width: 80, height: 80) .animation(Animation.linear(duration: 1.2).repeatForever(autoreverses: false), value: animate) } .shadow(color: accentColor.opacity(0.4), radius: 10, x: 0, y: 0) .onAppear { animate = true } Text("Loading...") .font(.system(size: 20, weight: .medium, design: .rounded)) .foregroundColor(isDarkMode ? .white.opacity(0.8) : .black.opacity(0.8)) .padding(.top, 20) .opacity(animate ? 1.0 : 0.5) .animation(Animation.easeInOut(duration: 1.5).repeatForever(autoreverses: true), value: animate) } } } } public func showAlert(title: String, message: String, showOk: Bool, showTryAgain: Bool = false, completion: @escaping (Bool) -> Void) { DispatchQueue.main.async { let rootViewController = UIApplication.shared.windows.last?.rootViewController if showTryAgain { let customErrorView = CustomErrorView( title: title, message: message, onDismiss: { rootViewController?.presentedViewController?.dismiss(animated: true) completion(false) }, showButton: true, primaryButtonText: "Try Again", onPrimaryButtonTap: { completion(true) } ) let hostingController = UIHostingController(rootView: customErrorView) hostingController.modalPresentationStyle = .overFullScreen hostingController.modalTransitionStyle = .crossDissolve hostingController.view.backgroundColor = .clear rootViewController?.present(hostingController, animated: true) } else if showOk { let customErrorView = CustomErrorView( title: title, message: message, onDismiss: { rootViewController?.presentedViewController?.dismiss(animated: true) completion(true) }, showButton: true, primaryButtonText: "OK", onPrimaryButtonTap: { rootViewController?.presentedViewController?.dismiss(animated: true) completion(true) } ) let hostingController = UIHostingController(rootView: customErrorView) hostingController.modalPresentationStyle = .overFullScreen hostingController.modalTransitionStyle = .crossDissolve hostingController.view.backgroundColor = .clear rootViewController?.present(hostingController, animated: true) } else { let customErrorView = CustomErrorView( title: title, message: message, onDismiss: { rootViewController?.presentedViewController?.dismiss(animated: true) completion(false) }, showButton: false ) let hostingController = UIHostingController(rootView: customErrorView) hostingController.modalPresentationStyle = .overFullScreen hostingController.modalTransitionStyle = .crossDissolve hostingController.view.backgroundColor = .clear rootViewController?.present(hostingController, animated: true) } } } func downloadFile(from urlString: String, to destinationURL: URL, completion: @escaping (String) -> Void) { let fileManager = FileManager.default let documentsDirectory = URL.documentsDirectory guard let url = URL(string: urlString) else { print("Invalid URL: \(urlString)") completion("[Internal Invalid URL error]") return } let task = URLSession.shared.downloadTask(with: url) { (tempLocalUrl, response, error) in guard let tempLocalUrl = tempLocalUrl, error == nil else { print("Error downloading file from \(urlString): \(String(describing: error))") completion("Are you connected to the internet? [Download Failed]") return } do { try fileManager.createDirectory(at: destinationURL.deletingLastPathComponent(), withIntermediateDirectories: true, attributes: nil) try fileManager.moveItem(at: tempLocalUrl, to: destinationURL) print("Downloaded \(urlString) to \(destinationURL.path)") } catch { print("Error saving file: \(error)") } } task.resume() completion("") }