react-native-debug-toolkit 3.3.2 → 3.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -1
- package/README.zh-CN.md +7 -1
- package/bin/debug-toolkit.js +16 -1
- package/ios/DebugToolkitDevConnect.h +17 -0
- package/ios/DebugToolkitDevConnect.mm +293 -212
- package/lib/commonjs/features/devConnect/DevConnectTab.js +127 -126
- package/lib/commonjs/features/devConnect/DevConnectTab.js.map +1 -1
- package/lib/commonjs/features/devConnect/nativeDevConnect.js +0 -12
- package/lib/commonjs/features/devConnect/nativeDevConnect.js.map +1 -1
- package/lib/module/features/devConnect/DevConnectTab.js +129 -128
- package/lib/module/features/devConnect/DevConnectTab.js.map +1 -1
- package/lib/module/features/devConnect/nativeDevConnect.js +0 -11
- package/lib/module/features/devConnect/nativeDevConnect.js.map +1 -1
- package/lib/typescript/src/features/devConnect/DevConnectTab.d.ts.map +1 -1
- package/lib/typescript/src/features/devConnect/nativeDevConnect.d.ts +6 -4
- package/lib/typescript/src/features/devConnect/nativeDevConnect.d.ts.map +1 -1
- package/package.json +4 -2
- package/scripts/android-debug-bundle.gradle +23 -0
- package/scripts/eas-postinstall.sh +6 -0
- package/scripts/embed-android.js +116 -0
- package/scripts/embed-expo.js +109 -0
- package/scripts/embed-ios.js +119 -0
- package/scripts/embed.js +224 -0
- package/src/features/devConnect/DevConnectTab.tsx +92 -92
- package/src/features/devConnect/nativeDevConnect.ts +8 -16
|
@@ -1,87 +1,220 @@
|
|
|
1
|
+
#import "DebugToolkitDevConnect.h"
|
|
2
|
+
|
|
1
3
|
#import <Foundation/Foundation.h>
|
|
4
|
+
#import <UIKit/UIKit.h>
|
|
2
5
|
#import <React/RCTBridge.h>
|
|
3
6
|
#import <React/RCTBundleManager.h>
|
|
4
7
|
#import <React/RCTBridgeModule.h>
|
|
5
8
|
#import <React/RCTBundleURLProvider.h>
|
|
6
|
-
#import <React/RCTDefines.h>
|
|
7
9
|
#import <React/RCTReloadCommand.h>
|
|
8
10
|
#import <objc/runtime.h>
|
|
9
11
|
#include <ifaddrs.h>
|
|
10
12
|
#include <arpa/inet.h>
|
|
11
13
|
#include <net/if.h>
|
|
12
14
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
15
|
+
// Debug-only Metro host switching — zero host-app native changes required.
|
|
16
|
+
//
|
|
17
|
+
// RN/Expo Debug templates call jsBundleURLForBundleRoot:fallbackURLProvider:, which consults
|
|
18
|
+
// -packagerServerHostPort (guessPackagerHost on simulator when Metro is running). We hook:
|
|
19
|
+
// 1. jsBundleURLForBundleRoot:fallbackURLProvider: — no DevConnect host → main.jsbundle first.
|
|
20
|
+
// 2. packagerServerHostPort — no DevConnect host → nil (block auto-discovered localhost:8081).
|
|
21
|
+
//
|
|
22
|
+
// Optional: host apps may call DebugToolkitMetroBundleURL() from bundleURL() for explicit control.
|
|
23
|
+
|
|
24
|
+
static NSString *const kBundleRoot = @"index";
|
|
25
|
+
// Expo prebuild templates pass this to jsBundleURLForBundleRoot: in Debug (see expo/expo#21643).
|
|
26
|
+
static NSString *const kExpoVirtualMetroEntry = @".expo/.virtual-metro-entry";
|
|
27
|
+
static NSString *const kMetroHostKey = @"_devconnect_metro_host";
|
|
28
|
+
|
|
29
|
+
static BOOL gPackagerHookInstalled = NO;
|
|
30
|
+
static BOOL gBundleRootHookInstalled = NO;
|
|
16
31
|
|
|
17
|
-
|
|
18
|
-
static IMP original_sourceURLForBridge;
|
|
19
|
-
static BOOL swizzleInvoked;
|
|
32
|
+
static NSURL *(*gOrigJsBundleURLForBundleRootWithFallback)(id, SEL, NSString *, NSURL * _Nonnull (^)(void));
|
|
20
33
|
|
|
21
|
-
static
|
|
34
|
+
static BOOL DevConnectEmbeddedFirstHooksActive(void)
|
|
22
35
|
{
|
|
23
|
-
|
|
24
|
-
stage, detail,
|
|
25
|
-
original_sourceURLForBridge ? @"YES" : @"NO",
|
|
26
|
-
swizzleInvoked ? @"YES" : @"NO"];
|
|
27
|
-
NSMutableArray *log = [[[NSUserDefaults standardUserDefaults] arrayForKey:kDevConnectDiagLog] mutableCopy]
|
|
28
|
-
?: [NSMutableArray new];
|
|
29
|
-
[log addObject:msg];
|
|
30
|
-
if (log.count > 30) [log removeObjectAtIndex:0];
|
|
31
|
-
[[NSUserDefaults standardUserDefaults] setObject:[log copy] forKey:kDevConnectDiagLog];
|
|
32
|
-
[[NSUserDefaults standardUserDefaults] synchronize];
|
|
33
|
-
NSLog(@"[DevConnect] diag: %@", msg);
|
|
36
|
+
return gBundleRootHookInstalled;
|
|
34
37
|
}
|
|
35
38
|
|
|
36
|
-
|
|
39
|
+
static NSURL *DebugToolkitEmbeddedBundleURL(void)
|
|
40
|
+
{
|
|
41
|
+
return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
|
|
42
|
+
}
|
|
37
43
|
|
|
38
|
-
static
|
|
44
|
+
static NSString *DevConnectPersistedMetroHost(void)
|
|
39
45
|
{
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
46
|
+
return [[NSUserDefaults standardUserDefaults] stringForKey:kMetroHostKey];
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
static void DevConnectSetPersistedMetroHost(NSString *_Nullable hostPort)
|
|
50
|
+
{
|
|
51
|
+
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
|
52
|
+
if (hostPort.length > 0) {
|
|
53
|
+
[defaults setObject:hostPort forKey:kMetroHostKey];
|
|
54
|
+
} else {
|
|
55
|
+
[defaults removeObjectForKey:kMetroHostKey];
|
|
48
56
|
}
|
|
49
|
-
|
|
50
|
-
|
|
57
|
+
[defaults synchronize];
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
static void DebugToolkitPrepareBundleSourceIfNeeded(void)
|
|
61
|
+
{
|
|
62
|
+
// Touch sharedSettings so RCTBundleURLProvider is linked before hook install retries.
|
|
63
|
+
RCTBundleURLProvider *settings = [RCTBundleURLProvider sharedSettings];
|
|
64
|
+
if (DevConnectPersistedMetroHost().length > 0) {
|
|
65
|
+
return;
|
|
51
66
|
}
|
|
52
|
-
|
|
67
|
+
[settings resetToDefaults];
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
static BOOL DevConnectIsExpoProject(void)
|
|
71
|
+
{
|
|
72
|
+
static dispatch_once_t onceToken;
|
|
73
|
+
static BOOL isExpo = NO;
|
|
74
|
+
dispatch_once(&onceToken, ^{
|
|
75
|
+
// Heuristic: matches common Expo prebuild / dev-client binaries.
|
|
76
|
+
isExpo = NSClassFromString(@"EXAppDelegateWrapper") != nil
|
|
77
|
+
|| [[NSBundle mainBundle] objectForInfoDictionaryKey:@"EXUpdatesURL"] != nil
|
|
78
|
+
|| [[NSBundle mainBundle] objectForInfoDictionaryKey:@"EXPO_RUNTIME_VERSION"] != nil;
|
|
79
|
+
});
|
|
80
|
+
return isExpo;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/// Bundle root Metro expects when DevConnect steers the packager (index for plain RN, virtual entry for Expo).
|
|
84
|
+
static NSString *DevConnectMetroBundleRoot(void)
|
|
85
|
+
{
|
|
86
|
+
return DevConnectIsExpoProject() ? kExpoVirtualMetroEntry : kBundleRoot;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
static NSURL *DevConnectMetroURLForPersistedHost(void)
|
|
90
|
+
{
|
|
91
|
+
NSString *host = DevConnectPersistedMetroHost();
|
|
92
|
+
if (host.length == 0) {
|
|
93
|
+
return nil;
|
|
94
|
+
}
|
|
95
|
+
RCTBundleURLProvider *settings = [RCTBundleURLProvider sharedSettings];
|
|
96
|
+
settings.jsLocation = host;
|
|
97
|
+
return [settings jsBundleURLForBundleRoot:DevConnectMetroBundleRoot()];
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/// When no DevConnect host: block guessPackagerHost (simulator often has Metro on :8081).
|
|
101
|
+
static NSString *replacement_packagerServerHostPort(id self, SEL _cmd)
|
|
102
|
+
{
|
|
103
|
+
NSString *host = DevConnectPersistedMetroHost();
|
|
104
|
+
if (host.length == 0) {
|
|
105
|
+
return nil;
|
|
106
|
+
}
|
|
107
|
+
[(RCTBundleURLProvider *)self setJsLocation:host];
|
|
108
|
+
return host;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/// Primary hook: return embedded main.jsbundle before RN/Expo tries Metro / virtual-metro-entry.
|
|
112
|
+
static NSURL *replacement_jsBundleURLForBundleRoot_fallback(
|
|
113
|
+
id self, SEL _cmd, NSString *bundleRoot, NSURL * _Nonnull (^fallbackURLProvider)(void))
|
|
114
|
+
{
|
|
115
|
+
if (DevConnectPersistedMetroHost().length == 0) {
|
|
116
|
+
NSURL *embedded = DebugToolkitEmbeddedBundleURL();
|
|
117
|
+
if (embedded) {
|
|
118
|
+
NSLog(@"[DevConnect] cold start → embedded bundle (root=%@)", bundleRoot);
|
|
119
|
+
return embedded;
|
|
120
|
+
}
|
|
121
|
+
NSLog(@"[DevConnect] no embedded main.jsbundle — falling back to Metro (root=%@)", bundleRoot);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (gOrigJsBundleURLForBundleRootWithFallback) {
|
|
125
|
+
return gOrigJsBundleURLForBundleRootWithFallback(self, _cmd, bundleRoot, fallbackURLProvider);
|
|
126
|
+
}
|
|
127
|
+
return fallbackURLProvider ? fallbackURLProvider() : nil;
|
|
53
128
|
}
|
|
54
129
|
|
|
55
|
-
static void
|
|
130
|
+
static void DebugToolkitInstallPackagerHook(Class cls)
|
|
56
131
|
{
|
|
57
|
-
if (
|
|
58
|
-
appendDiag(stage, @"class=nil");
|
|
132
|
+
if (gPackagerHookInstalled) {
|
|
59
133
|
return;
|
|
60
134
|
}
|
|
61
|
-
|
|
62
|
-
SEL selector = @selector(sourceURLForBridge:);
|
|
63
|
-
Method method = class_getInstanceMethod(targetClass, selector);
|
|
135
|
+
Method method = class_getInstanceMethod(cls, @selector(packagerServerHostPort));
|
|
64
136
|
if (!method) {
|
|
65
|
-
|
|
137
|
+
NSLog(@"[DevConnect] packagerServerHostPort not found");
|
|
66
138
|
return;
|
|
67
139
|
}
|
|
68
|
-
|
|
69
|
-
|
|
140
|
+
IMP replacement = (IMP)replacement_packagerServerHostPort;
|
|
141
|
+
if (method_getImplementation(method) == replacement) {
|
|
142
|
+
gPackagerHookInstalled = YES;
|
|
70
143
|
return;
|
|
71
144
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
145
|
+
method_setImplementation(method, replacement);
|
|
146
|
+
gPackagerHookInstalled = YES;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
static void DebugToolkitInstallBundleRootHook(Class cls)
|
|
150
|
+
{
|
|
151
|
+
if (gBundleRootHookInstalled) {
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
SEL selector = @selector(jsBundleURLForBundleRoot:fallbackURLProvider:);
|
|
155
|
+
Method method = class_getInstanceMethod(cls, selector);
|
|
156
|
+
if (!method) {
|
|
157
|
+
NSLog(@"[DevConnect] jsBundleURLForBundleRoot:fallbackURLProvider: not found");
|
|
158
|
+
return;
|
|
80
159
|
}
|
|
81
|
-
|
|
82
|
-
|
|
160
|
+
IMP replacement = (IMP)replacement_jsBundleURLForBundleRoot_fallback;
|
|
161
|
+
gOrigJsBundleURLForBundleRootWithFallback =
|
|
162
|
+
(NSURL * (*)(id, SEL, NSString *, NSURL * _Nonnull (^)(void)))method_getImplementation(method);
|
|
163
|
+
if ((IMP)gOrigJsBundleURLForBundleRootWithFallback == replacement) {
|
|
164
|
+
gBundleRootHookInstalled = YES;
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
method_setImplementation(method, replacement);
|
|
168
|
+
gBundleRootHookInstalled = YES;
|
|
83
169
|
}
|
|
84
170
|
|
|
171
|
+
static void DebugToolkitInstallAllHooks(void)
|
|
172
|
+
{
|
|
173
|
+
if (gBundleRootHookInstalled && gPackagerHookInstalled) {
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
Class cls = NSClassFromString(@"RCTBundleURLProvider");
|
|
178
|
+
if (!cls) {
|
|
179
|
+
NSLog(@"[DevConnect] RCTBundleURLProvider not loaded — hooks will retry");
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
DebugToolkitInstallBundleRootHook(cls);
|
|
184
|
+
DebugToolkitInstallPackagerHook(cls);
|
|
185
|
+
|
|
186
|
+
static BOOL didLogOutcome = NO;
|
|
187
|
+
if (!didLogOutcome && (gBundleRootHookInstalled || gPackagerHookInstalled)) {
|
|
188
|
+
didLogOutcome = YES;
|
|
189
|
+
if (DevConnectEmbeddedFirstHooksActive()) {
|
|
190
|
+
NSLog(@"[DevConnect] embedded-first hooks active (bundleRoot=%@ packager=%@)",
|
|
191
|
+
gBundleRootHookInstalled ? @"Y" : @"N",
|
|
192
|
+
gPackagerHookInstalled ? @"Y" : @"N");
|
|
193
|
+
} else {
|
|
194
|
+
NSLog(@"[DevConnect] embedded-first hooks FAILED — rebuild / check React linkage");
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
NSURL *DebugToolkitMetroBundleURL(void)
|
|
200
|
+
{
|
|
201
|
+
return DevConnectMetroURLForPersistedHost();
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// RCT_EXPORT_MODULE defines +load for module registration — use a separate class for hooks.
|
|
205
|
+
@interface DebugToolkitDevConnectBootstrap : NSObject
|
|
206
|
+
@end
|
|
207
|
+
|
|
208
|
+
@implementation DebugToolkitDevConnectBootstrap
|
|
209
|
+
|
|
210
|
+
+ (void)load
|
|
211
|
+
{
|
|
212
|
+
DebugToolkitPrepareBundleSourceIfNeeded();
|
|
213
|
+
DebugToolkitInstallAllHooks();
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
@end
|
|
217
|
+
|
|
85
218
|
#pragma mark - Module
|
|
86
219
|
|
|
87
220
|
@interface DebugToolkitDevConnect : NSObject <RCTBridgeModule>
|
|
@@ -94,63 +227,64 @@ RCT_EXPORT_MODULE(DebugToolkitDevConnect)
|
|
|
94
227
|
@synthesize bridge = _bridge;
|
|
95
228
|
@synthesize bundleManager = _bundleManager;
|
|
96
229
|
|
|
97
|
-
__attribute__((constructor))
|
|
98
|
-
static void devconnect_swizzle_init(void)
|
|
99
|
-
{
|
|
100
|
-
swizzleSourceURLForBridge(NSClassFromString(@"AppDelegate"), @"constructor");
|
|
101
|
-
}
|
|
102
|
-
|
|
103
230
|
- (instancetype)init
|
|
104
231
|
{
|
|
105
232
|
if ((self = [super init])) {
|
|
106
|
-
|
|
107
|
-
Class delegateClass = object_getClass([UIApplication sharedApplication].delegate);
|
|
108
|
-
swizzleSourceURLForBridge(delegateClass, @"init");
|
|
109
|
-
}
|
|
233
|
+
DebugToolkitInstallAllHooks();
|
|
110
234
|
}
|
|
111
235
|
return self;
|
|
112
236
|
}
|
|
113
237
|
|
|
114
|
-
+ (BOOL)requiresMainQueueSetup
|
|
115
|
-
{
|
|
116
|
-
|
|
117
|
-
|
|
238
|
+
+ (BOOL)requiresMainQueueSetup { return NO; }
|
|
239
|
+
- (dispatch_queue_t)methodQueue { return dispatch_get_main_queue(); }
|
|
240
|
+
|
|
241
|
+
#pragma mark - Bundle Manager Resolution
|
|
118
242
|
|
|
119
|
-
- (
|
|
243
|
+
- (RCTBundleManager *)resolveBundleManager
|
|
120
244
|
{
|
|
121
|
-
|
|
245
|
+
if (_bundleManager) return _bundleManager;
|
|
246
|
+
if (_bridge) return [_bridge moduleForClass:[RCTBundleManager class]];
|
|
247
|
+
return nil;
|
|
122
248
|
}
|
|
123
249
|
|
|
124
|
-
#pragma mark -
|
|
250
|
+
#pragma mark - Host Parsing
|
|
125
251
|
|
|
126
|
-
- (
|
|
252
|
+
- (NSString *)normalizeHostPort:(NSString *)hostPort
|
|
127
253
|
{
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
254
|
+
NSRange sep = [hostPort rangeOfString:@":" options:NSBackwardsSearch];
|
|
255
|
+
NSString *host = sep.location == NSNotFound ? hostPort : [hostPort substringToIndex:sep.location];
|
|
256
|
+
NSString *portStr = sep.location == NSNotFound ? @"" : [hostPort substringFromIndex:sep.location + 1];
|
|
257
|
+
|
|
258
|
+
NSNumberFormatter *formatter = [NSNumberFormatter new];
|
|
259
|
+
formatter.numberStyle = NSNumberFormatterDecimalStyle;
|
|
260
|
+
NSNumber *parsed = [formatter numberFromString:portStr];
|
|
261
|
+
int port;
|
|
262
|
+
if (parsed && parsed.intValue > 0 && parsed.intValue <= 65535) {
|
|
263
|
+
port = parsed.intValue;
|
|
264
|
+
} else {
|
|
265
|
+
#ifdef RCT_METRO_PORT
|
|
266
|
+
port = RCT_METRO_PORT;
|
|
267
|
+
#else
|
|
268
|
+
port = 8081;
|
|
269
|
+
#endif
|
|
133
270
|
}
|
|
134
|
-
return
|
|
271
|
+
return [NSString stringWithFormat:@"%@:%d", host, port];
|
|
135
272
|
}
|
|
136
273
|
|
|
137
|
-
#pragma mark -
|
|
274
|
+
#pragma mark - Reload helper
|
|
138
275
|
|
|
139
|
-
|
|
140
|
-
rejecter:(__unused RCTPromiseRejectBlock)reject)
|
|
276
|
+
- (void)reloadWithBundleURL:(NSURL *)bundleURL reason:(NSString *)reason
|
|
141
277
|
{
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
NSString *jsLocation = [RCTBundleURLProvider sharedSettings].jsLocation;
|
|
147
|
-
NSString *host = persisted.length > 0 ? persisted : jsLocation;
|
|
148
|
-
resolve(host ?: [NSNull null]);
|
|
149
|
-
} @catch (NSException *exception) {
|
|
150
|
-
reject(@"native_error", exception.reason, nil);
|
|
278
|
+
if (bundleURL) {
|
|
279
|
+
RCTBundleManager *bm = [self resolveBundleManager];
|
|
280
|
+
if (bm) bm.bundleURL = bundleURL;
|
|
281
|
+
RCTReloadCommandSetBundleURL(bundleURL);
|
|
151
282
|
}
|
|
283
|
+
RCTTriggerReloadCommandListeners(reason);
|
|
152
284
|
}
|
|
153
285
|
|
|
286
|
+
#pragma mark - Exported Methods
|
|
287
|
+
|
|
154
288
|
RCT_EXPORT_METHOD(applyMetroHost:(NSString *)hostPort
|
|
155
289
|
resolver:(RCTPromiseResolveBlock)resolve
|
|
156
290
|
rejecter:(RCTPromiseRejectBlock)reject)
|
|
@@ -160,71 +294,24 @@ RCT_EXPORT_METHOD(applyMetroHost:(NSString *)hostPort
|
|
|
160
294
|
reject(@"invalid_host", @"Metro host cannot be empty.", nil);
|
|
161
295
|
return;
|
|
162
296
|
}
|
|
297
|
+
NSString *normalized = [self normalizeHostPort:hostPort];
|
|
163
298
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
// Parse host and port
|
|
167
|
-
NSRange separator = [hostPort rangeOfString:@":" options:NSBackwardsSearch];
|
|
168
|
-
NSString *host = separator.location == NSNotFound
|
|
169
|
-
? hostPort
|
|
170
|
-
: [hostPort substringToIndex:separator.location];
|
|
171
|
-
NSString *port = separator.location == NSNotFound
|
|
172
|
-
? @""
|
|
173
|
-
: [hostPort substringFromIndex:separator.location + 1];
|
|
174
|
-
|
|
175
|
-
if (host.length == 0 && port.length == 0) {
|
|
176
|
-
[self resetMetroHost:resolve rejecter:reject];
|
|
177
|
-
return;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
NSNumberFormatter *formatter = [NSNumberFormatter new];
|
|
181
|
-
formatter.numberStyle = NSNumberFormatterDecimalStyle;
|
|
182
|
-
NSNumber *portNumber = [formatter numberFromString:port];
|
|
183
|
-
if (portNumber == nil) {
|
|
184
|
-
#ifdef RCT_METRO_PORT
|
|
185
|
-
portNumber = [NSNumber numberWithInt:RCT_METRO_PORT];
|
|
186
|
-
#else
|
|
187
|
-
portNumber = [NSNumber numberWithInt:8081];
|
|
188
|
-
#endif
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
NSString *normalizedHostPort = [NSString stringWithFormat:@"%@:%d", host, portNumber.intValue];
|
|
192
|
-
|
|
193
|
-
// Persist for AppDelegate swizzle (Release mode + restart)
|
|
194
|
-
[[NSUserDefaults standardUserDefaults] setObject:normalizedHostPort forKey:kDevConnectMetroHost];
|
|
195
|
-
[[NSUserDefaults standardUserDefaults] synchronize];
|
|
299
|
+
DevConnectSetPersistedMetroHost(normalized);
|
|
196
300
|
|
|
197
|
-
|
|
198
|
-
settings.jsLocation =
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
RCTReloadCommandSetBundleURL(bundleURL);
|
|
212
|
-
}
|
|
213
|
-
#endif
|
|
214
|
-
|
|
215
|
-
NSLog(@"[DevConnect] applyMetroHost: %@ | bm=%@ | bridge=%@ | url=%@",
|
|
216
|
-
normalizedHostPort, bm ? @"YES" : @"nil", _bridge ? @"YES" : @"nil", bundleURL);
|
|
217
|
-
|
|
218
|
-
RCTTriggerReloadCommandListeners(@"Dev menu - apply changes");
|
|
219
|
-
|
|
220
|
-
NSMutableDictionary *result = [@{@"hostPort" : normalizedHostPort} mutableCopy];
|
|
221
|
-
if (bm && bm.bundleURL.absoluteString) {
|
|
222
|
-
result[@"bundleURL"] = bm.bundleURL.absoluteString;
|
|
223
|
-
}
|
|
224
|
-
resolve(result);
|
|
225
|
-
} @catch (NSException *exception) {
|
|
226
|
-
NSLog(@"[DevConnect] applyMetroHost EXCEPTION: %@", exception.reason);
|
|
227
|
-
reject(@"native_error", exception.reason, nil);
|
|
301
|
+
RCTBundleURLProvider *settings = [RCTBundleURLProvider sharedSettings];
|
|
302
|
+
settings.jsLocation = normalized;
|
|
303
|
+
NSURL *bundleURL = [settings jsBundleURLForBundleRoot:DevConnectMetroBundleRoot()];
|
|
304
|
+
|
|
305
|
+
[self reloadWithBundleURL:bundleURL reason:@"Dev menu - apply changes"];
|
|
306
|
+
|
|
307
|
+
NSLog(@"[DevConnect] applyMetroHost host=%@ url=%@", normalized, bundleURL);
|
|
308
|
+
resolve(@{
|
|
309
|
+
@"hostPort": normalized,
|
|
310
|
+
@"bundleURL": bundleURL.absoluteString ?: [NSNull null],
|
|
311
|
+
});
|
|
312
|
+
} @catch (NSException *e) {
|
|
313
|
+
NSLog(@"[DevConnect] applyMetroHost EXCEPTION: %@", e.reason);
|
|
314
|
+
reject(@"native_error", e.reason ?: @"unknown", nil);
|
|
228
315
|
}
|
|
229
316
|
}
|
|
230
317
|
|
|
@@ -232,48 +319,55 @@ RCT_EXPORT_METHOD(resetMetroHost:(RCTPromiseResolveBlock)resolve
|
|
|
232
319
|
rejecter:(__unused RCTPromiseRejectBlock)reject)
|
|
233
320
|
{
|
|
234
321
|
@try {
|
|
235
|
-
|
|
236
|
-
[[NSUserDefaults standardUserDefaults] removeObjectForKey:kDevConnectMetroHost];
|
|
237
|
-
[[NSUserDefaults standardUserDefaults] synchronize];
|
|
322
|
+
DevConnectSetPersistedMetroHost(nil);
|
|
238
323
|
|
|
239
|
-
// Reset RN settings
|
|
240
324
|
RCTBundleURLProvider *settings = [RCTBundleURLProvider sharedSettings];
|
|
241
325
|
[settings resetToDefaults];
|
|
242
|
-
NSURL *bundleURL = [settings jsBundleURLForFallbackExtension:nil];
|
|
243
326
|
|
|
244
|
-
|
|
245
|
-
if (
|
|
246
|
-
|
|
327
|
+
NSURL *embedded = DebugToolkitEmbeddedBundleURL();
|
|
328
|
+
if (!embedded) {
|
|
329
|
+
embedded = [settings jsBundleURLForFallbackExtension:nil];
|
|
247
330
|
}
|
|
248
331
|
|
|
249
|
-
|
|
250
|
-
|
|
332
|
+
[self reloadWithBundleURL:embedded reason:@"Dev menu - reset to default"];
|
|
333
|
+
|
|
334
|
+
NSLog(@"[DevConnect] resetMetroHost url=%@", embedded);
|
|
251
335
|
resolve([NSNull null]);
|
|
252
|
-
} @catch (NSException *
|
|
253
|
-
NSLog(@"[DevConnect] resetMetroHost EXCEPTION: %@",
|
|
254
|
-
reject(@"native_error",
|
|
336
|
+
} @catch (NSException *e) {
|
|
337
|
+
NSLog(@"[DevConnect] resetMetroHost EXCEPTION: %@", e.reason);
|
|
338
|
+
reject(@"native_error", e.reason ?: @"unknown", nil);
|
|
255
339
|
}
|
|
256
340
|
}
|
|
257
341
|
|
|
258
|
-
RCT_EXPORT_METHOD(
|
|
342
|
+
RCT_EXPORT_METHOD(getMetroHost:(RCTPromiseResolveBlock)resolve
|
|
259
343
|
rejecter:(__unused RCTPromiseRejectBlock)reject)
|
|
260
344
|
{
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
resolve(@{
|
|
264
|
-
@"log": log,
|
|
265
|
-
@"swizzleInstalled": @(original_sourceURLForBridge != NULL),
|
|
266
|
-
@"swizzleInvoked": @(swizzleInvoked),
|
|
267
|
-
@"persistedMetroHost": metroHost ?: [NSNull null],
|
|
268
|
-
});
|
|
345
|
+
NSString *host = DevConnectPersistedMetroHost();
|
|
346
|
+
resolve(host.length > 0 ? host : [NSNull null]);
|
|
269
347
|
}
|
|
270
348
|
|
|
271
|
-
RCT_EXPORT_METHOD(
|
|
349
|
+
RCT_EXPORT_METHOD(getDiagnostics:(RCTPromiseResolveBlock)resolve
|
|
272
350
|
rejecter:(__unused RCTPromiseRejectBlock)reject)
|
|
273
351
|
{
|
|
274
|
-
[
|
|
275
|
-
|
|
276
|
-
|
|
352
|
+
Class realDelegate = object_getClass([UIApplication sharedApplication].delegate);
|
|
353
|
+
NSString *delegateName = realDelegate ? NSStringFromClass(realDelegate) : @"unknown";
|
|
354
|
+
NSString *persisted = DevConnectPersistedMetroHost();
|
|
355
|
+
|
|
356
|
+
#if DEBUG
|
|
357
|
+
BOOL isDebug = YES;
|
|
358
|
+
#else
|
|
359
|
+
BOOL isDebug = NO;
|
|
360
|
+
#endif
|
|
361
|
+
|
|
362
|
+
resolve(@{
|
|
363
|
+
@"persistedMetroHost": persisted.length > 0 ? persisted : [NSNull null],
|
|
364
|
+
@"appDelegateClass": delegateName,
|
|
365
|
+
@"isDebugBuild": @(isDebug),
|
|
366
|
+
@"hasEmbeddedBundle": @(DebugToolkitEmbeddedBundleURL() != nil),
|
|
367
|
+
@"embeddedFirstHookInstalled": @(DevConnectEmbeddedFirstHooksActive()),
|
|
368
|
+
@"packagerHookInstalled": @(gPackagerHookInstalled),
|
|
369
|
+
@"bundleRootHookInstalled": @(gBundleRootHookInstalled),
|
|
370
|
+
});
|
|
277
371
|
}
|
|
278
372
|
|
|
279
373
|
RCT_EXPORT_METHOD(getPreference:(NSString *)key
|
|
@@ -283,8 +377,8 @@ RCT_EXPORT_METHOD(getPreference:(NSString *)key
|
|
|
283
377
|
@try {
|
|
284
378
|
NSString *value = [[NSUserDefaults standardUserDefaults] stringForKey:key];
|
|
285
379
|
resolve(value ?: [NSNull null]);
|
|
286
|
-
} @catch (NSException *
|
|
287
|
-
reject(@"native_error",
|
|
380
|
+
} @catch (NSException *e) {
|
|
381
|
+
reject(@"native_error", e.reason ?: @"unknown", nil);
|
|
288
382
|
}
|
|
289
383
|
}
|
|
290
384
|
|
|
@@ -297,8 +391,8 @@ RCT_EXPORT_METHOD(setPreference:(NSString *)key
|
|
|
297
391
|
[[NSUserDefaults standardUserDefaults] setObject:value forKey:key];
|
|
298
392
|
[[NSUserDefaults standardUserDefaults] synchronize];
|
|
299
393
|
resolve([NSNull null]);
|
|
300
|
-
} @catch (NSException *
|
|
301
|
-
reject(@"native_error",
|
|
394
|
+
} @catch (NSException *e) {
|
|
395
|
+
reject(@"native_error", e.reason ?: @"unknown", nil);
|
|
302
396
|
}
|
|
303
397
|
}
|
|
304
398
|
|
|
@@ -317,40 +411,27 @@ RCT_EXPORT_METHOD(getLocalIp:(RCTPromiseResolveBlock)resolve
|
|
|
317
411
|
{
|
|
318
412
|
@try {
|
|
319
413
|
struct ifaddrs *interfaces = NULL;
|
|
320
|
-
if (getifaddrs(&interfaces)
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
iface = interfaces;
|
|
337
|
-
while (iface != NULL) {
|
|
338
|
-
if (iface->ifa_addr != NULL && iface->ifa_addr->sa_family == AF_INET && !(iface->ifa_flags & IFF_LOOPBACK)) {
|
|
339
|
-
char addrStr[INET_ADDRSTRLEN];
|
|
340
|
-
struct sockaddr_in *sin = (struct sockaddr_in *)iface->ifa_addr;
|
|
341
|
-
inet_ntop(AF_INET, &sin->sin_addr, addrStr, sizeof(addrStr));
|
|
342
|
-
NSString *ip = [NSString stringWithUTF8String:addrStr];
|
|
343
|
-
freeifaddrs(interfaces);
|
|
344
|
-
resolve(ip);
|
|
345
|
-
return;
|
|
346
|
-
}
|
|
347
|
-
iface = iface->ifa_next;
|
|
348
|
-
}
|
|
414
|
+
if (getifaddrs(&interfaces) != 0) {
|
|
415
|
+
resolve([NSNull null]);
|
|
416
|
+
return;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
NSString *preferred = nil;
|
|
420
|
+
NSString *fallback = nil;
|
|
421
|
+
for (struct ifaddrs *iface = interfaces; iface != NULL; iface = iface->ifa_next) {
|
|
422
|
+
if (!iface->ifa_addr || iface->ifa_addr->sa_family != AF_INET) continue;
|
|
423
|
+
if (iface->ifa_flags & IFF_LOOPBACK) continue;
|
|
424
|
+
char addrStr[INET_ADDRSTRLEN];
|
|
425
|
+
struct sockaddr_in *sin = (struct sockaddr_in *)iface->ifa_addr;
|
|
426
|
+
inet_ntop(AF_INET, &sin->sin_addr, addrStr, sizeof(addrStr));
|
|
427
|
+
NSString *ip = [NSString stringWithUTF8String:addrStr];
|
|
428
|
+
if (strcmp(iface->ifa_name, "en0") == 0) { preferred = ip; break; }
|
|
429
|
+
if (!fallback) fallback = ip;
|
|
349
430
|
}
|
|
350
431
|
freeifaddrs(interfaces);
|
|
351
|
-
resolve([NSNull null]);
|
|
352
|
-
} @catch (NSException *
|
|
353
|
-
reject(@"native_error",
|
|
432
|
+
resolve(preferred ?: fallback ?: [NSNull null]);
|
|
433
|
+
} @catch (NSException *e) {
|
|
434
|
+
reject(@"native_error", e.reason ?: @"unknown", nil);
|
|
354
435
|
}
|
|
355
436
|
}
|
|
356
437
|
|