1// Copyright 2015-present 650 Industries. All rights reserved.
2
3#import <React/RCTUtils.h>
4
5#import "EXDevMenuGestureInterceptor.h"
6#import "EXDevMenuGestureRecognizer.h"
7
8@import UIKit;
9
10static BOOL isInstalled = NO;
11static EXDevMenuGestureRecognizer *gestureRecognizerInstance;
12
13@implementation UIWindow (EXDevMenuGestureInterceptor)
14
15- (NSArray<__kindof UIGestureRecognizer *> *)EX_gestureRecognizers
16{
17  // Just for thread safety, someone may uninstall the interceptor in the meantime and we would fall into recursive loop.
18  if (!isInstalled) {
19    return [self gestureRecognizers];
20  }
21
22  // Check for the case where singleton instance of gesture recognizer is not created yet or is attached to different window.
23  if (!gestureRecognizerInstance || gestureRecognizerInstance.view != self) {
24    // Create gesture recognizer if not created yet.
25    static dispatch_once_t onceToken;
26    dispatch_once(&onceToken, ^{
27      gestureRecognizerInstance = [EXDevMenuGestureRecognizer new];
28    });
29
30    // Remove it from the window it's attached to.
31    [gestureRecognizerInstance.view removeGestureRecognizer:gestureRecognizerInstance];
32
33    // Attach to this window.
34    [self addGestureRecognizer:gestureRecognizerInstance];
35  }
36
37  // `EX_gestureRecognizers` implementation has been swapped with `gestureRecognizers` - it might be confusing that we call it recursively, but we don't.
38  return [self EX_gestureRecognizers];
39}
40
41@end
42
43@implementation EXDevMenuGestureInterceptor
44
45+ (void)install
46{
47  if (!isInstalled) {
48    // Capture touch gesture from any window by swapping default implementation from UIWindow.
49    RCTSwapInstanceMethods([UIWindow class], @selector(gestureRecognizers), @selector(EX_gestureRecognizers));
50    isInstalled = YES;
51
52    // Make sure gesture recognizer is enabled.
53    [gestureRecognizerInstance setEnabled:YES];
54  }
55}
56
57+ (void)uninstall
58{
59  if (isInstalled) {
60    // Bring back the original method.
61    RCTSwapInstanceMethods([UIWindow class], @selector(gestureRecognizers), @selector(EX_gestureRecognizers));
62    isInstalled = NO;
63
64    // Cancel recognized gestures and disable it entirely.
65    [gestureRecognizerInstance setEnabled:NO];
66  }
67}
68
69+ (BOOL)isInstalled
70{
71  return isInstalled;
72}
73
74@end
75