Merge pull request #5 from stossy11/main

Add Heartbeat Completion Handler
This commit is contained in:
Stephen 2025-03-27 17:51:12 -04:00 • committed by GitHub
commit b76c6581ff
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 142 additions and 11 deletions

View file

@ -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<CChar>?) -> 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<CChar>?) -> 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)
}
}
}

View file

@ -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):

View file

@ -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):

View file

@ -10,7 +10,9 @@
#include <CoreFoundation/CoreFoundation.h>
#include <limits.h>
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;

View file

@ -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 */