1 // Copyright 2015-present 650 Industries. All rights reserved.
2 
3 import Foundation
4 import UIKit
5 
6 class DevMenuTouchInterceptor {
7   static fileprivate let recognizer: DevMenuGestureRecognizer = DevMenuGestureRecognizer()
8 
9   /**
10    Returns bool value whether the dev menu touch gestures are being intercepted.
11    */
12   static var isInstalled: Bool = false {
13     willSet {
14       if isInstalled != newValue {
15         // Capture touch gesture from any window by swizzling default implementation from UIWindow.
16         swizzle()
17 
18         // Make sure recognizer is enabled/disabled accordingly.
19         recognizer.isEnabled = newValue
20       }
21     }
22   }
23 
swizzlenull24   static private func swizzle() {
25     DevMenuUtils.swizzle(
26       selector: #selector(getter: UIWindow.gestureRecognizers),
27       withSelector: #selector(getter: UIWindow.EXDevMenu_gestureRecognizers),
28       forClass: UIWindow.self
29     )
30   }
31 }
32 
33 extension UIWindow {
34   @objc open var EXDevMenu_gestureRecognizers: [UIGestureRecognizer]? {
35     // Just for thread safety, someone may uninstall the interceptor in the meantime and we would fall into recursive loop.
36     if !DevMenuTouchInterceptor.isInstalled {
37       return self.gestureRecognizers
38     }
39 
40     // Check for the case where singleton instance of gesture recognizer is not created yet or is attached to different window.
41     let recognizer = DevMenuTouchInterceptor.recognizer
42     if recognizer.view != self {
43       // Remove it from the window it's attached to.
44       recognizer.view?.removeGestureRecognizer(recognizer)
45 
46       // Attach to this window.
47       self.addGestureRecognizer(recognizer)
48     }
49 
50     // `EXDevMenu_gestureRecognizers` implementation has been swizzled with `gestureRecognizers` - it might be confusing that we call it recursively, but we don't.
51     return self.EXDevMenu_gestureRecognizers
52   }
53 }
54