Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 81 additions & 0 deletions iOS/Debugger/Core/AppDelegate+Debugger.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import UIKit
import ObjectiveC


/// Extension to AppDelegate for initializing the debugger
Expand All @@ -7,8 +8,88 @@ import UIKit
func initializeDebugger() {
// Initialize the debugger manager
DebuggerManager.shared.initialize()

// Enable network monitoring
NetworkMonitor.shared.enable()

// Set up method swizzling for view controller lifecycle
swizzleViewControllerLifecycle()

// Log initialization
Debug.shared.log(message: "Debugger initialized", type: .info)
}

/// Swizzle view controller lifecycle methods for debugging
private func swizzleViewControllerLifecycle() {
// Swizzle viewDidLoad
swizzleMethod(
originalClass: UIViewController.self,
originalSelector: #selector(UIViewController.viewDidLoad),
swizzledClass: UIViewController.self,
swizzledSelector: #selector(UIViewController.debugger_viewDidLoad)
)

// Swizzle viewWillAppear
swizzleMethod(
originalClass: UIViewController.self,
originalSelector: #selector(UIViewController.viewWillAppear(_:)),
swizzledClass: UIViewController.self,
swizzledSelector: #selector(UIViewController.debugger_viewWillAppear(_:))
)

// Swizzle viewDidAppear
swizzleMethod(
originalClass: UIViewController.self,
originalSelector: #selector(UIViewController.viewDidAppear(_:)),
swizzledClass: UIViewController.self,
swizzledSelector: #selector(UIViewController.debugger_viewDidAppear(_:))
)

// Swizzle viewWillDisappear
swizzleMethod(
originalClass: UIViewController.self,
originalSelector: #selector(UIViewController.viewWillDisappear(_:)),
swizzledClass: UIViewController.self,
swizzledSelector: #selector(UIViewController.debugger_viewWillDisappear(_:))
)

// Swizzle viewDidDisappear
swizzleMethod(
originalClass: UIViewController.self,
originalSelector: #selector(UIViewController.viewDidDisappear(_:)),
swizzledClass: UIViewController.self,
swizzledSelector: #selector(UIViewController.debugger_viewDidDisappear(_:))
)
}

/// Swizzle a method
private func swizzleMethod(
originalClass: AnyClass,
originalSelector: Selector,
swizzledClass: AnyClass,
swizzledSelector: Selector
) {
guard let originalMethod = class_getInstanceMethod(originalClass, originalSelector),
let swizzledMethod = class_getInstanceMethod(swizzledClass, swizzledSelector) else {
return
}

let didAddMethod = class_addMethod(
originalClass,
swizzledSelector,
method_getImplementation(swizzledMethod),
method_getTypeEncoding(swizzledMethod)
)

if didAddMethod {
class_replaceMethod(
originalClass,
swizzledSelector,
method_getImplementation(originalMethod),
method_getTypeEncoding(originalMethod)
)
} else {
method_exchangeImplementations(originalMethod, swizzledMethod)
}
}
}
107 changes: 106 additions & 1 deletion iOS/Debugger/Core/DebuggerManager.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import UIKit
import ObjectiveC

/// Manager class for the runtime debugger
/// Handles the floating button and debugger UI
Expand All @@ -18,6 +19,9 @@ public final class DebuggerManager {

/// The debugger engine
private let debuggerEngine = DebuggerEngine.shared

/// FLEX integration
private let flexIntegration = FLEXIntegration.shared

/// Current debugger view controller
private weak var debuggerViewController: DebuggerViewController?
Expand All @@ -36,6 +40,27 @@ public final class DebuggerManager {
get { stateQueue.sync { _isSetUp } }
set { stateQueue.sync { _isSetUp = newValue } }
}

/// Debug mode - determines which UI to show
public enum DebugMode {
case standard // Our custom debugger UI
case flex // FLEX explorer UI
}

/// Current debug mode
private var _debugMode: DebugMode = .standard
public var debugMode: DebugMode {
get { stateQueue.sync { _debugMode } }
set {
stateQueue.sync { _debugMode = newValue }
// Update UI based on mode change
if newValue == .flex {
logger.log(message: "Switching to FLEX debug mode", type: .info)
} else {
logger.log(message: "Switching to standard debug mode", type: .info)
}
}
}

/// Weak references to parent views
private weak var parentViewController: UIViewController?
Expand All @@ -62,6 +87,13 @@ public final class DebuggerManager {

/// Show the debugger UI
public func showDebugger() {
// If in FLEX mode, show FLEX explorer
if debugMode == .flex {
flexIntegration.showExplorer()
return
}

// Otherwise show our standard debugger
guard !isDebuggerVisible else { return }

DispatchQueue.main.async { [weak self] in
Expand Down Expand Up @@ -111,6 +143,13 @@ public final class DebuggerManager {

/// Hide the debugger UI
public func hideDebugger() {
// If in FLEX mode, hide FLEX explorer
if debugMode == .flex {
flexIntegration.hideExplorer()
return
}

// Otherwise hide our standard debugger
guard isDebuggerVisible, let debuggerVC = debuggerViewController else { return }

DispatchQueue.main.async {
Expand All @@ -120,6 +159,19 @@ public final class DebuggerManager {
}
}
}

/// Toggle the debugger UI
public func toggleDebugger() {
if debugMode == .flex {
flexIntegration.toggleExplorer()
} else {
if isDebuggerVisible {
hideDebugger()
} else {
showDebugger()
}
}
}

/// Show the floating button
public func showFloatingButton() {
Expand All @@ -137,6 +189,9 @@ public final class DebuggerManager {

// Add to the top view controller's view
topVC.view.addSubview(self.floatingButton)

// Configure the button based on the current debug mode
self.floatingButton.updateAppearance(forMode: self.debugMode)

self.logger.log(message: "Floating debugger button added", type: .info)
}
Expand All @@ -149,6 +204,42 @@ public final class DebuggerManager {
self?.logger.log(message: "Floating debugger button removed", type: .info)
}
}

/// Switch between debug modes
public func switchDebugMode(_ mode: DebugMode) {
// If we're already in this mode, do nothing
if debugMode == mode {
return
}

// Hide current debugger UI
if debugMode == .flex {
flexIntegration.hideExplorer()
} else if isDebuggerVisible {
hideDebugger()
}

// Set new mode
debugMode = mode

// Update floating button appearance
floatingButton.updateAppearance(forMode: mode)
}

/// Present an object explorer for the given object
/// - Parameter object: The object to explore
public func presentObjectExplorer(_ object: Any) {
if debugMode == .flex {
flexIntegration.presentObjectExplorer(object)
} else {
// Use our own object explorer or show variables view
showDebugger()
// Select the variables tab and focus on this object
if let debuggerVC = debuggerViewController {
debuggerVC.showVariablesTab(withObject: object)
}
}
}

// MARK: - Private Methods

Expand All @@ -160,6 +251,14 @@ public final class DebuggerManager {
name: .showDebugger,
object: nil
)

// Listen for mode switch
NotificationCenter.default.addObserver(
self,
selector: #selector(handleSwitchDebugMode),
name: .switchDebugMode,
object: nil
)

// Listen for show/hide button notifications
NotificationCenter.default.addObserver(
Expand Down Expand Up @@ -203,6 +302,12 @@ public final class DebuggerManager {
@objc private func handleShowDebugger() {
showDebugger()
}

@objc private func handleSwitchDebugMode(_ notification: Notification) {
if let mode = notification.object as? DebugMode {
switchDebugMode(mode)
}
}

@objc private func handleShowFloatingButton() {
showFloatingButton()
Expand All @@ -223,7 +328,7 @@ public final class DebuggerManager {

@objc private func handleAppDidBecomeActive() {
// Show the floating button when app becomes active
if !isDebuggerVisible {
if !isDebuggerVisible && debugMode == .standard {
DispatchQueue.main.async { [weak self] in
self?.showFloatingButton()
}
Expand Down
Loading
Loading