react-native-debug-toolkit 3.3.2 → 3.3.4
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 +40 -1
- package/README.zh-CN.md +40 -1
- package/app.plugin.js +51 -0
- package/bin/debug-toolkit.js +18 -2
- package/dev-client.js +3 -0
- package/ios/DebugToolkitDevConnect.h +17 -0
- package/ios/DebugToolkitDevConnect.mm +260 -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 +6 -2
- package/scripts/bundle/android.js +101 -0
- package/scripts/bundle/cli.js +57 -0
- package/scripts/bundle/doctor.js +38 -0
- package/scripts/bundle/ios.js +179 -0
- package/scripts/bundle/setup.js +39 -0
- package/scripts/debug-bundle.gradle +147 -0
- package/src/features/devConnect/DevConnectTab.tsx +92 -92
- package/src/features/devConnect/nativeDevConnect.ts +8 -16
|
@@ -1,87 +1,187 @@
|
|
|
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 only hook
|
|
19
|
+
// jsBundleURLForBundleRoot:fallbackURLProvider: so no DevConnect host starts from main.jsbundle.
|
|
20
|
+
// Persisted DevConnect hosts still flow through RCTBundleURLProvider, preserving RN's reachability
|
|
21
|
+
// and fallback behavior when a saved Metro host is stale.
|
|
22
|
+
//
|
|
23
|
+
// Optional: host apps may call DebugToolkitMetroBundleURL() from bundleURL() for explicit control.
|
|
24
|
+
|
|
25
|
+
static NSString *const kBundleRoot = @"index";
|
|
26
|
+
// Expo prebuild templates pass this to jsBundleURLForBundleRoot: in Debug (see expo/expo#21643).
|
|
27
|
+
static NSString *const kExpoVirtualMetroEntry = @".expo/.virtual-metro-entry";
|
|
28
|
+
static NSString *const kMetroHostKey = @"_devconnect_metro_host";
|
|
29
|
+
|
|
30
|
+
static BOOL gBundleRootHookInstalled = NO;
|
|
31
|
+
|
|
32
|
+
static NSURL *(*gOrigJsBundleURLForBundleRootWithFallback)(id, SEL, NSString *, NSURL * _Nonnull (^)(void));
|
|
16
33
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
34
|
+
static BOOL DevConnectEmbeddedFirstHooksActive(void)
|
|
35
|
+
{
|
|
36
|
+
return gBundleRootHookInstalled;
|
|
37
|
+
}
|
|
20
38
|
|
|
21
|
-
static
|
|
39
|
+
static NSURL *DebugToolkitEmbeddedBundleURL(void)
|
|
22
40
|
{
|
|
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);
|
|
41
|
+
return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
|
|
34
42
|
}
|
|
35
43
|
|
|
36
|
-
|
|
44
|
+
static NSString *DevConnectPersistedMetroHost(void)
|
|
45
|
+
{
|
|
46
|
+
return [[NSUserDefaults standardUserDefaults] stringForKey:kMetroHostKey];
|
|
47
|
+
}
|
|
37
48
|
|
|
38
|
-
static
|
|
49
|
+
static void DevConnectSetPersistedMetroHost(NSString *_Nullable hostPort)
|
|
39
50
|
{
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
NSURL *url = [NSURL URLWithString:urlStr];
|
|
46
|
-
appendDiag(@"swizzle-called", [NSString stringWithFormat:@"host=%@ url=%@", metroHost, url]);
|
|
47
|
-
return url;
|
|
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
|
+
/// Primary hook: return embedded main.jsbundle before RN/Expo tries Metro / virtual-metro-entry.
|
|
101
|
+
static NSURL *replacement_jsBundleURLForBundleRoot_fallback(
|
|
102
|
+
id self, SEL _cmd, NSString *bundleRoot, NSURL * _Nonnull (^fallbackURLProvider)(void))
|
|
103
|
+
{
|
|
104
|
+
if (DevConnectPersistedMetroHost().length == 0) {
|
|
105
|
+
NSURL *embedded = DebugToolkitEmbeddedBundleURL();
|
|
106
|
+
if (embedded) {
|
|
107
|
+
NSLog(@"[DevConnect] cold start → embedded bundle (root=%@)", bundleRoot);
|
|
108
|
+
return embedded;
|
|
109
|
+
}
|
|
110
|
+
NSLog(@"[DevConnect] no embedded main.jsbundle — falling back to Metro (root=%@)", bundleRoot);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (gOrigJsBundleURLForBundleRootWithFallback) {
|
|
114
|
+
return gOrigJsBundleURLForBundleRootWithFallback(self, _cmd, bundleRoot, fallbackURLProvider);
|
|
115
|
+
}
|
|
116
|
+
return fallbackURLProvider ? fallbackURLProvider() : nil;
|
|
53
117
|
}
|
|
54
118
|
|
|
55
|
-
static void
|
|
119
|
+
static void DebugToolkitInstallBundleRootHook(Class cls)
|
|
56
120
|
{
|
|
57
|
-
if (
|
|
58
|
-
appendDiag(stage, @"class=nil");
|
|
121
|
+
if (gBundleRootHookInstalled) {
|
|
59
122
|
return;
|
|
60
123
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
Method method = class_getInstanceMethod(targetClass, selector);
|
|
124
|
+
SEL selector = @selector(jsBundleURLForBundleRoot:fallbackURLProvider:);
|
|
125
|
+
Method method = class_getInstanceMethod(cls, selector);
|
|
64
126
|
if (!method) {
|
|
65
|
-
|
|
127
|
+
NSLog(@"[DevConnect] jsBundleURLForBundleRoot:fallbackURLProvider: not found");
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
IMP replacement = (IMP)replacement_jsBundleURLForBundleRoot_fallback;
|
|
131
|
+
gOrigJsBundleURLForBundleRootWithFallback =
|
|
132
|
+
(NSURL * (*)(id, SEL, NSString *, NSURL * _Nonnull (^)(void)))method_getImplementation(method);
|
|
133
|
+
if ((IMP)gOrigJsBundleURLForBundleRootWithFallback == replacement) {
|
|
134
|
+
gBundleRootHookInstalled = YES;
|
|
66
135
|
return;
|
|
67
136
|
}
|
|
68
|
-
|
|
69
|
-
|
|
137
|
+
method_setImplementation(method, replacement);
|
|
138
|
+
gBundleRootHookInstalled = YES;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
static void DebugToolkitInstallAllHooks(void)
|
|
142
|
+
{
|
|
143
|
+
if (gBundleRootHookInstalled) {
|
|
70
144
|
return;
|
|
71
145
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
if (!added) {
|
|
78
|
-
// Method already exists directly on targetClass; replace in place.
|
|
79
|
-
method_setImplementation(method, (IMP)devconnect_sourceURLForBridge);
|
|
146
|
+
|
|
147
|
+
Class cls = NSClassFromString(@"RCTBundleURLProvider");
|
|
148
|
+
if (!cls) {
|
|
149
|
+
NSLog(@"[DevConnect] RCTBundleURLProvider not loaded — hooks will retry");
|
|
150
|
+
return;
|
|
80
151
|
}
|
|
81
|
-
|
|
82
|
-
|
|
152
|
+
|
|
153
|
+
DebugToolkitInstallBundleRootHook(cls);
|
|
154
|
+
|
|
155
|
+
static BOOL didLogOutcome = NO;
|
|
156
|
+
if (!didLogOutcome) {
|
|
157
|
+
didLogOutcome = YES;
|
|
158
|
+
if (DevConnectEmbeddedFirstHooksActive()) {
|
|
159
|
+
NSLog(@"[DevConnect] embedded-first hook active");
|
|
160
|
+
} else {
|
|
161
|
+
NSLog(@"[DevConnect] embedded-first hooks FAILED — rebuild / check React linkage");
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
NSURL *DebugToolkitMetroBundleURL(void)
|
|
167
|
+
{
|
|
168
|
+
return DevConnectMetroURLForPersistedHost();
|
|
83
169
|
}
|
|
84
170
|
|
|
171
|
+
// RCT_EXPORT_MODULE defines +load for module registration — use a separate class for hooks.
|
|
172
|
+
@interface DebugToolkitDevConnectBootstrap : NSObject
|
|
173
|
+
@end
|
|
174
|
+
|
|
175
|
+
@implementation DebugToolkitDevConnectBootstrap
|
|
176
|
+
|
|
177
|
+
+ (void)load
|
|
178
|
+
{
|
|
179
|
+
DebugToolkitPrepareBundleSourceIfNeeded();
|
|
180
|
+
DebugToolkitInstallAllHooks();
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
@end
|
|
184
|
+
|
|
85
185
|
#pragma mark - Module
|
|
86
186
|
|
|
87
187
|
@interface DebugToolkitDevConnect : NSObject <RCTBridgeModule>
|
|
@@ -94,63 +194,64 @@ RCT_EXPORT_MODULE(DebugToolkitDevConnect)
|
|
|
94
194
|
@synthesize bridge = _bridge;
|
|
95
195
|
@synthesize bundleManager = _bundleManager;
|
|
96
196
|
|
|
97
|
-
__attribute__((constructor))
|
|
98
|
-
static void devconnect_swizzle_init(void)
|
|
99
|
-
{
|
|
100
|
-
swizzleSourceURLForBridge(NSClassFromString(@"AppDelegate"), @"constructor");
|
|
101
|
-
}
|
|
102
|
-
|
|
103
197
|
- (instancetype)init
|
|
104
198
|
{
|
|
105
199
|
if ((self = [super init])) {
|
|
106
|
-
|
|
107
|
-
Class delegateClass = object_getClass([UIApplication sharedApplication].delegate);
|
|
108
|
-
swizzleSourceURLForBridge(delegateClass, @"init");
|
|
109
|
-
}
|
|
200
|
+
DebugToolkitInstallAllHooks();
|
|
110
201
|
}
|
|
111
202
|
return self;
|
|
112
203
|
}
|
|
113
204
|
|
|
114
|
-
+ (BOOL)requiresMainQueueSetup
|
|
115
|
-
{
|
|
116
|
-
return NO;
|
|
117
|
-
}
|
|
205
|
+
+ (BOOL)requiresMainQueueSetup { return NO; }
|
|
206
|
+
- (dispatch_queue_t)methodQueue { return dispatch_get_main_queue(); }
|
|
118
207
|
|
|
119
|
-
-
|
|
208
|
+
#pragma mark - Bundle Manager Resolution
|
|
209
|
+
|
|
210
|
+
- (RCTBundleManager *)resolveBundleManager
|
|
120
211
|
{
|
|
121
|
-
|
|
212
|
+
if (_bundleManager) return _bundleManager;
|
|
213
|
+
if (_bridge) return [_bridge moduleForClass:[RCTBundleManager class]];
|
|
214
|
+
return nil;
|
|
122
215
|
}
|
|
123
216
|
|
|
124
|
-
#pragma mark -
|
|
217
|
+
#pragma mark - Host Parsing
|
|
125
218
|
|
|
126
|
-
- (
|
|
219
|
+
- (NSString *)normalizeHostPort:(NSString *)hostPort
|
|
127
220
|
{
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
221
|
+
NSRange sep = [hostPort rangeOfString:@":" options:NSBackwardsSearch];
|
|
222
|
+
NSString *host = sep.location == NSNotFound ? hostPort : [hostPort substringToIndex:sep.location];
|
|
223
|
+
NSString *portStr = sep.location == NSNotFound ? @"" : [hostPort substringFromIndex:sep.location + 1];
|
|
224
|
+
|
|
225
|
+
NSNumberFormatter *formatter = [NSNumberFormatter new];
|
|
226
|
+
formatter.numberStyle = NSNumberFormatterDecimalStyle;
|
|
227
|
+
NSNumber *parsed = [formatter numberFromString:portStr];
|
|
228
|
+
int port;
|
|
229
|
+
if (parsed && parsed.intValue > 0 && parsed.intValue <= 65535) {
|
|
230
|
+
port = parsed.intValue;
|
|
231
|
+
} else {
|
|
232
|
+
#ifdef RCT_METRO_PORT
|
|
233
|
+
port = RCT_METRO_PORT;
|
|
234
|
+
#else
|
|
235
|
+
port = 8081;
|
|
236
|
+
#endif
|
|
133
237
|
}
|
|
134
|
-
return
|
|
238
|
+
return [NSString stringWithFormat:@"%@:%d", host, port];
|
|
135
239
|
}
|
|
136
240
|
|
|
137
|
-
#pragma mark -
|
|
241
|
+
#pragma mark - Reload helper
|
|
138
242
|
|
|
139
|
-
|
|
140
|
-
rejecter:(__unused RCTPromiseRejectBlock)reject)
|
|
243
|
+
- (void)reloadWithBundleURL:(NSURL *)bundleURL reason:(NSString *)reason
|
|
141
244
|
{
|
|
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);
|
|
245
|
+
if (bundleURL) {
|
|
246
|
+
RCTBundleManager *bm = [self resolveBundleManager];
|
|
247
|
+
if (bm) bm.bundleURL = bundleURL;
|
|
248
|
+
RCTReloadCommandSetBundleURL(bundleURL);
|
|
151
249
|
}
|
|
250
|
+
RCTTriggerReloadCommandListeners(reason);
|
|
152
251
|
}
|
|
153
252
|
|
|
253
|
+
#pragma mark - Exported Methods
|
|
254
|
+
|
|
154
255
|
RCT_EXPORT_METHOD(applyMetroHost:(NSString *)hostPort
|
|
155
256
|
resolver:(RCTPromiseResolveBlock)resolve
|
|
156
257
|
rejecter:(RCTPromiseRejectBlock)reject)
|
|
@@ -160,71 +261,24 @@ RCT_EXPORT_METHOD(applyMetroHost:(NSString *)hostPort
|
|
|
160
261
|
reject(@"invalid_host", @"Metro host cannot be empty.", nil);
|
|
161
262
|
return;
|
|
162
263
|
}
|
|
264
|
+
NSString *normalized = [self normalizeHostPort:hostPort];
|
|
163
265
|
|
|
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];
|
|
196
|
-
|
|
197
|
-
// Also set jsLocation (Debug mode + hot reload)
|
|
198
|
-
settings.jsLocation = normalizedHostPort;
|
|
266
|
+
DevConnectSetPersistedMetroHost(normalized);
|
|
199
267
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
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);
|
|
268
|
+
RCTBundleURLProvider *settings = [RCTBundleURLProvider sharedSettings];
|
|
269
|
+
settings.jsLocation = normalized;
|
|
270
|
+
NSURL *bundleURL = [settings jsBundleURLForBundleRoot:DevConnectMetroBundleRoot()];
|
|
271
|
+
|
|
272
|
+
[self reloadWithBundleURL:bundleURL reason:@"Dev menu - apply changes"];
|
|
273
|
+
|
|
274
|
+
NSLog(@"[DevConnect] applyMetroHost host=%@ url=%@", normalized, bundleURL);
|
|
275
|
+
resolve(@{
|
|
276
|
+
@"hostPort": normalized,
|
|
277
|
+
@"bundleURL": bundleURL.absoluteString ?: [NSNull null],
|
|
278
|
+
});
|
|
279
|
+
} @catch (NSException *e) {
|
|
280
|
+
NSLog(@"[DevConnect] applyMetroHost EXCEPTION: %@", e.reason);
|
|
281
|
+
reject(@"native_error", e.reason ?: @"unknown", nil);
|
|
228
282
|
}
|
|
229
283
|
}
|
|
230
284
|
|
|
@@ -232,48 +286,55 @@ RCT_EXPORT_METHOD(resetMetroHost:(RCTPromiseResolveBlock)resolve
|
|
|
232
286
|
rejecter:(__unused RCTPromiseRejectBlock)reject)
|
|
233
287
|
{
|
|
234
288
|
@try {
|
|
235
|
-
|
|
236
|
-
[[NSUserDefaults standardUserDefaults] removeObjectForKey:kDevConnectMetroHost];
|
|
237
|
-
[[NSUserDefaults standardUserDefaults] synchronize];
|
|
289
|
+
DevConnectSetPersistedMetroHost(nil);
|
|
238
290
|
|
|
239
|
-
// Reset RN settings
|
|
240
291
|
RCTBundleURLProvider *settings = [RCTBundleURLProvider sharedSettings];
|
|
241
292
|
[settings resetToDefaults];
|
|
242
|
-
NSURL *bundleURL = [settings jsBundleURLForFallbackExtension:nil];
|
|
243
293
|
|
|
244
|
-
|
|
245
|
-
if (
|
|
246
|
-
|
|
294
|
+
NSURL *embedded = DebugToolkitEmbeddedBundleURL();
|
|
295
|
+
if (!embedded) {
|
|
296
|
+
embedded = [settings jsBundleURLForFallbackExtension:nil];
|
|
247
297
|
}
|
|
248
298
|
|
|
249
|
-
|
|
250
|
-
|
|
299
|
+
[self reloadWithBundleURL:embedded reason:@"Dev menu - reset to default"];
|
|
300
|
+
|
|
301
|
+
NSLog(@"[DevConnect] resetMetroHost url=%@", embedded);
|
|
251
302
|
resolve([NSNull null]);
|
|
252
|
-
} @catch (NSException *
|
|
253
|
-
NSLog(@"[DevConnect] resetMetroHost EXCEPTION: %@",
|
|
254
|
-
reject(@"native_error",
|
|
303
|
+
} @catch (NSException *e) {
|
|
304
|
+
NSLog(@"[DevConnect] resetMetroHost EXCEPTION: %@", e.reason);
|
|
305
|
+
reject(@"native_error", e.reason ?: @"unknown", nil);
|
|
255
306
|
}
|
|
256
307
|
}
|
|
257
308
|
|
|
258
|
-
RCT_EXPORT_METHOD(
|
|
309
|
+
RCT_EXPORT_METHOD(getMetroHost:(RCTPromiseResolveBlock)resolve
|
|
259
310
|
rejecter:(__unused RCTPromiseRejectBlock)reject)
|
|
260
311
|
{
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
resolve(@{
|
|
264
|
-
@"log": log,
|
|
265
|
-
@"swizzleInstalled": @(original_sourceURLForBridge != NULL),
|
|
266
|
-
@"swizzleInvoked": @(swizzleInvoked),
|
|
267
|
-
@"persistedMetroHost": metroHost ?: [NSNull null],
|
|
268
|
-
});
|
|
312
|
+
NSString *host = DevConnectPersistedMetroHost();
|
|
313
|
+
resolve(host.length > 0 ? host : [NSNull null]);
|
|
269
314
|
}
|
|
270
315
|
|
|
271
|
-
RCT_EXPORT_METHOD(
|
|
316
|
+
RCT_EXPORT_METHOD(getDiagnostics:(RCTPromiseResolveBlock)resolve
|
|
272
317
|
rejecter:(__unused RCTPromiseRejectBlock)reject)
|
|
273
318
|
{
|
|
274
|
-
[
|
|
275
|
-
|
|
276
|
-
|
|
319
|
+
Class realDelegate = object_getClass([UIApplication sharedApplication].delegate);
|
|
320
|
+
NSString *delegateName = realDelegate ? NSStringFromClass(realDelegate) : @"unknown";
|
|
321
|
+
NSString *persisted = DevConnectPersistedMetroHost();
|
|
322
|
+
|
|
323
|
+
#if DEBUG
|
|
324
|
+
BOOL isDebug = YES;
|
|
325
|
+
#else
|
|
326
|
+
BOOL isDebug = NO;
|
|
327
|
+
#endif
|
|
328
|
+
|
|
329
|
+
resolve(@{
|
|
330
|
+
@"persistedMetroHost": persisted.length > 0 ? persisted : [NSNull null],
|
|
331
|
+
@"appDelegateClass": delegateName,
|
|
332
|
+
@"isDebugBuild": @(isDebug),
|
|
333
|
+
@"hasEmbeddedBundle": @(DebugToolkitEmbeddedBundleURL() != nil),
|
|
334
|
+
@"embeddedFirstHookInstalled": @(DevConnectEmbeddedFirstHooksActive()),
|
|
335
|
+
@"packagerHookInstalled": @NO,
|
|
336
|
+
@"bundleRootHookInstalled": @(gBundleRootHookInstalled),
|
|
337
|
+
});
|
|
277
338
|
}
|
|
278
339
|
|
|
279
340
|
RCT_EXPORT_METHOD(getPreference:(NSString *)key
|
|
@@ -283,8 +344,8 @@ RCT_EXPORT_METHOD(getPreference:(NSString *)key
|
|
|
283
344
|
@try {
|
|
284
345
|
NSString *value = [[NSUserDefaults standardUserDefaults] stringForKey:key];
|
|
285
346
|
resolve(value ?: [NSNull null]);
|
|
286
|
-
} @catch (NSException *
|
|
287
|
-
reject(@"native_error",
|
|
347
|
+
} @catch (NSException *e) {
|
|
348
|
+
reject(@"native_error", e.reason ?: @"unknown", nil);
|
|
288
349
|
}
|
|
289
350
|
}
|
|
290
351
|
|
|
@@ -297,8 +358,8 @@ RCT_EXPORT_METHOD(setPreference:(NSString *)key
|
|
|
297
358
|
[[NSUserDefaults standardUserDefaults] setObject:value forKey:key];
|
|
298
359
|
[[NSUserDefaults standardUserDefaults] synchronize];
|
|
299
360
|
resolve([NSNull null]);
|
|
300
|
-
} @catch (NSException *
|
|
301
|
-
reject(@"native_error",
|
|
361
|
+
} @catch (NSException *e) {
|
|
362
|
+
reject(@"native_error", e.reason ?: @"unknown", nil);
|
|
302
363
|
}
|
|
303
364
|
}
|
|
304
365
|
|
|
@@ -317,40 +378,27 @@ RCT_EXPORT_METHOD(getLocalIp:(RCTPromiseResolveBlock)resolve
|
|
|
317
378
|
{
|
|
318
379
|
@try {
|
|
319
380
|
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
|
-
}
|
|
381
|
+
if (getifaddrs(&interfaces) != 0) {
|
|
382
|
+
resolve([NSNull null]);
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
NSString *preferred = nil;
|
|
387
|
+
NSString *fallback = nil;
|
|
388
|
+
for (struct ifaddrs *iface = interfaces; iface != NULL; iface = iface->ifa_next) {
|
|
389
|
+
if (!iface->ifa_addr || iface->ifa_addr->sa_family != AF_INET) continue;
|
|
390
|
+
if (iface->ifa_flags & IFF_LOOPBACK) continue;
|
|
391
|
+
char addrStr[INET_ADDRSTRLEN];
|
|
392
|
+
struct sockaddr_in *sin = (struct sockaddr_in *)iface->ifa_addr;
|
|
393
|
+
inet_ntop(AF_INET, &sin->sin_addr, addrStr, sizeof(addrStr));
|
|
394
|
+
NSString *ip = [NSString stringWithUTF8String:addrStr];
|
|
395
|
+
if (strcmp(iface->ifa_name, "en0") == 0) { preferred = ip; break; }
|
|
396
|
+
if (!fallback) fallback = ip;
|
|
349
397
|
}
|
|
350
398
|
freeifaddrs(interfaces);
|
|
351
|
-
resolve([NSNull null]);
|
|
352
|
-
} @catch (NSException *
|
|
353
|
-
reject(@"native_error",
|
|
399
|
+
resolve(preferred ?: fallback ?: [NSNull null]);
|
|
400
|
+
} @catch (NSException *e) {
|
|
401
|
+
reject(@"native_error", e.reason ?: @"unknown", nil);
|
|
354
402
|
}
|
|
355
403
|
}
|
|
356
404
|
|