diff --git a/StikJIT/StikJITApp.swift b/StikJIT/StikJITApp.swift index 9200e429..2be8cdd8 100644 --- a/StikJIT/StikJITApp.swift +++ b/StikJIT/StikJITApp.swift @@ -7,24 +7,77 @@ import SwiftUI import em_proxy +import UniformTypeIdentifiers @main struct HeartbeatApp: App { @State private var isLoading = true + @State private var isPairing = false + @State private var heartBeat = false + @State private var error: Int32? = nil + + init() { + 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) + } var body: some Scene { WindowGroup { if isLoading { LoadingView() .onAppear { - DispatchQueue.main.asyncAfter(deadline: .now() + 5) { - withAnimation(.easeInOut(duration: 1.0)) { - isLoading = false - } - } startProxy() if FileManager.default.fileExists(atPath: URL.documentsDirectory.appendingPathComponent("pairingFile.plist").path) { + Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true) { timer in + if heartBeat { + isLoading = false + timer.invalidate() + } else { + if let error { + if error == InvalidHostID.rawValue { + isPairing = true + } else { + startHeartbeatInBackground() + } + self.error = nil + } + } + } + startHeartbeatInBackground() + } else { + isLoading = false + } + } + .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 { @@ -50,16 +103,57 @@ struct HeartbeatApp: App { } } } + + func startHeartbeatInBackground() { + let heartBeat = Thread { + let cCompletionHandler: @convention(block) (Int32, UnsafePointer?) -> Void = { result, messagePointer in + let message: String? = messagePointer != nil ? String(cString: messagePointer!) : nil + + if result == 0 { + print("Heartbeat started successfully: \(message ?? "")") + + self.heartBeat = true + } else { + print("Error: \(result == InvalidHostID.rawValue ? "Invalid host ID, Please Selecr New Pairing File" : message ?? "") (Code: \(result))") + + showAlert(title: "HeartBeat Error", message: "\(message ?? "") (\(result))", showOk: true) { _ in + self.error = result + } + } + } + + startHeartbeat(cCompletionHandler) + } + + heartBeat.qualityOfService = .background + heartBeat.name = "HeartBeat" + heartBeat.start() + } + } func startHeartbeatInBackground() { let heartBeat = Thread { - startHeartbeat() + let cCompletionHandler: @convention(block) (Int32, UnsafePointer?) -> Void = { result, messagePointer in + let message: String? = messagePointer != nil ? String(cString: messagePointer!) : nil + + if result == 0 { + print("Heartbeat started successfully: \(message ?? "")") + } else { + print("Error: \(message ?? "") (Code: \(result))") + + showAlert(title: "HeartBeat Error", message: "\(message ?? "") (\(result))", showOk: true) { _ in + startHeartbeatInBackground() + } + } + } + + startHeartbeat(cCompletionHandler) } heartBeat.qualityOfService = .background - heartBeat.name = "HeartBeat" + heartBeat.name = "Heartbeat" heartBeat.start() } @@ -106,3 +200,23 @@ struct LoadingView: View { } } } + +public func showAlert(title: String, message: String, showOk: Bool, completion: @escaping (Bool) -> Void) { + DispatchQueue.main.async { + if let mainWindow = UIApplication.shared.windows.last { + let alert = UIAlertController(title: title, message: message, preferredStyle: .alert) + + if showOk { + let okAction = UIAlertAction(title: "OK", style: .default) { _ in + completion(true) + } + + alert.addAction(okAction) + } else { + completion(false) + } + + mainWindow.rootViewController?.present(alert, animated: true, completion: nil) + } + } +} diff --git a/StikJIT/Views/HomeView.swift b/StikJIT/Views/HomeView.swift index 027f02b6..b536e075 100644 --- a/StikJIT/Views/HomeView.swift +++ b/StikJIT/Views/HomeView.swift @@ -6,6 +6,13 @@ // import SwiftUI +import UniformTypeIdentifiers + +extension UIDocumentPickerViewController { + @objc func fix_init(forOpeningContentTypes contentTypes: [UTType], asCopy: Bool) -> UIDocumentPickerViewController { + return fix_init(forOpeningContentTypes: contentTypes, asCopy: true) + } +} struct HomeView: View { @AppStorage("username") private var username = "User" @@ -61,7 +68,7 @@ struct HomeView: View { .onReceive(timer) { _ in refreshBackground() } - .fileImporter(isPresented: $isShowingPairingFilePicker, allowedContentTypes: [.item]) {result in + .fileImporter(isPresented: $isShowingPairingFilePicker, allowedContentTypes: [UTType(filenameExtension: "mobiledevicepairing", conformingTo: .data)!, .propertyList]) {result in switch result { case .success(let url): diff --git a/StikJIT/Views/SettingsView.swift b/StikJIT/Views/SettingsView.swift index 09877f42..3790c017 100644 --- a/StikJIT/Views/SettingsView.swift +++ b/StikJIT/Views/SettingsView.swift @@ -6,6 +6,7 @@ // import SwiftUI +import UniformTypeIdentifiers struct SettingsView: View { @AppStorage("username") private var username = "User" @@ -111,7 +112,7 @@ struct SettingsView: View { .font(.bodyFont) .accentColor(.accentColor) } - .fileImporter(isPresented: $isShowingPairingFilePicker, allowedContentTypes: [.item]) {result in + .fileImporter(isPresented: $isShowingPairingFilePicker, allowedContentTypes: [UTType(filenameExtension: "mobiledevicepairing", conformingTo: .data)!, .propertyList]) {result in switch result { case .success(let url): diff --git a/StikJIT/idevice/heartbeat.c b/StikJIT/idevice/heartbeat.c index 864eec62..a36908d6 100644 --- a/StikJIT/idevice/heartbeat.c +++ b/StikJIT/idevice/heartbeat.c @@ -10,7 +10,9 @@ #include #include -void startHeartbeat() { +typedef void (^HeartbeatCompletionHandler)(int result, const char *message); + +void startHeartbeat(HeartbeatCompletionHandler completion) { printf("DEBUG: Initializing logger...\n"); idevice_init_logger(Debug, Disabled, NULL); @@ -43,6 +45,7 @@ void startHeartbeat() { IdeviceErrorCode err = idevice_pairing_file_read(pairingFilePath, &pairing_file); if (err != IdeviceSuccess) { fprintf(stderr, "DEBUG: Failed to read pairing file: %d\n", err); + completion(err, "Failed to read pairing file"); return; } printf("DEBUG: Pairing file read successfully.\n"); @@ -54,6 +57,7 @@ void startHeartbeat() { if (err != IdeviceSuccess) { fprintf(stderr, "DEBUG: Failed to create TCP provider: %d\n", err); idevice_pairing_file_free(pairing_file); + completion(err, "Failed to create TCP provider"); return; } printf("DEBUG: TCP provider created successfully.\n"); @@ -62,12 +66,15 @@ void startHeartbeat() { HeartbeatClientHandle *client = NULL; err = heartbeat_connect_tcp(provider, &client); if (err != IdeviceSuccess) { + completion(err, "Failed to connect to Heartbeat"); fprintf(stderr, "DEBUG: Failed to connect to installation proxy: %d\n", err); return; } tcp_provider_free(provider); printf("DEBUG: Connected to installation proxy successfully.\n"); + completion(0, "Heartbeat Completed"); + u_int64_t current_interval = 15; while (1) { u_int64_t new_interval = 0; diff --git a/StikJIT/idevice/heartbeat.h b/StikJIT/idevice/heartbeat.h index 78188592..5d31d58b 100644 --- a/StikJIT/idevice/heartbeat.h +++ b/StikJIT/idevice/heartbeat.h @@ -9,6 +9,8 @@ #ifndef HEARTBEAT_H #define HEARTBEAT_H -void startHeartbeat(); +typedef void (^HeartbeatCompletionHandler)(int result, const char *message); + +void startHeartbeat(HeartbeatCompletionHandler completion); #endif /* HEARTBEAT_H */