Skip to content

Commit f2a0513

Browse files
committed
enable React Native Fiddle to trigger dev reloads
1 parent f35546e commit f2a0513

File tree

4 files changed

+114
-76
lines changed

4 files changed

+114
-76
lines changed

macos/rnmprebuilds-macOS/AppDelegate.mm

Lines changed: 92 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -14,64 +14,25 @@ - (void)applicationDidFinishLaunching:(NSNotification *)notification
1414
self.initialProps = @{};
1515
self.dependencyProvider = [RCTAppDependencyProvider new];
1616

17-
/// Apple Events are processed without app focus, but require TCC permissions. Trigger as follows:
18-
/// ```sh
19-
/// osascript -e '
20-
/// tell application "Electron"
21-
/// «event MYEVCMD1» "payload text"
22-
/// end tell
23-
/// '
24-
/// ```
25-
[[NSAppleEventManager sharedAppleEventManager]
26-
setEventHandler:self
27-
andSelector:@selector(handleEvent:withReplyEvent:)
28-
forEventClass:'MYEV'
29-
andEventID:'CMD1'];
30-
31-
/// DistributedNotifications require app focus to be processed, but don't require TCC permissions. Trigger as follows:
32-
/// ```sh
33-
/// swift -e 'import Foundation; DistributedNotificationCenter.default.post(name: Notification.Name("com.example.MyCommand"), object: nil, userInfo: ["action":"test"])'
34-
/// ```
35-
[[NSDistributedNotificationCenter defaultCenter]
36-
addObserverForName:@"com.example.MyCommand"
37-
object:nil
38-
queue:[NSOperationQueue mainQueue]
39-
usingBlock:^(NSNotification * _Nonnull notification) {
40-
41-
NSDictionary *userInfo = notification.userInfo;
42-
if (!userInfo) {
43-
userInfo = @{};
44-
}
45-
46-
NSLog(@"Received notification: %@", userInfo);
47-
RCTTriggerReloadCommandListeners(@"programmatic reload");
48-
}];
17+
#if DEBUG
18+
// Here, we trigger a dev reload upon any changes saved to our magic file at:
19+
// ~/Library/Application Support/uk.co.birchlabs.rnfiddleclient/trigger-reload.txt
20+
// This is a hacky workaround to enable React Native Fiddle to trigger our app
21+
// to reconnect to Metro when needed (as we sometimes have to relaunch Metro
22+
// in a new CWD).
23+
//
24+
// Compared to "proper" methods of IPC, this requires neither TCC permissions
25+
// nor app focus, and should work even with App Sandbox and Hardened Runtime
26+
// enabled.
27+
TouchFile(ReloadTriggerFilePath());
28+
WatchFile(ReloadTriggerFilePath(), ^{
29+
RCTTriggerReloadCommandListeners(nil);
30+
});
31+
#endif
4932

5033
return [super applicationDidFinishLaunching:notification];
5134
}
5235

53-
- (void)handleEvent:(NSAppleEventDescriptor *)event
54-
withReplyEvent:(NSAppleEventDescriptor *)replyEvent
55-
{
56-
NSAppleEventDescriptor *param = [event paramDescriptorForKeyword:keyDirectObject];
57-
58-
NSString *payload = nil;
59-
if (param && param.descriptorType == typeUTF8Text) {
60-
payload = param.stringValue;
61-
}
62-
63-
NSLog(@"Received custom Apple Event: class='MYEV' id='CMD1' payload='%@'", payload);
64-
65-
if (replyEvent) {
66-
NSAppleEventDescriptor *replyString =
67-
[NSAppleEventDescriptor descriptorWithString:@"Message received! Reloading..."];
68-
[replyEvent setDescriptor:replyString forKeyword:keyDirectObject];
69-
}
70-
71-
RCTTriggerReloadCommandListeners(@"programmatic reload");
72-
}
73-
74-
7536
- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
7637
{
7738
return [self bundleURL];
@@ -100,4 +61,81 @@ - (BOOL)concurrentRootEnabled
10061
#endif
10162
}
10263

64+
#if DEBUG
65+
static NSString *ReloadTriggerFilePath(void) {
66+
static NSString *reloadFilePath = nil;
67+
static dispatch_once_t onceToken;
68+
dispatch_once(&onceToken, ^{
69+
NSString *appSupport = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES).firstObject;
70+
NSString *bundleID = [[NSBundle mainBundle] bundleIdentifier];
71+
NSString *dir = [appSupport stringByAppendingPathComponent:bundleID];
72+
reloadFilePath = [dir stringByAppendingPathComponent:@"trigger-reload.txt"];
73+
});
74+
return reloadFilePath;
75+
}
76+
77+
static NSError* TouchFile(NSString *filePath) {
78+
NSString *dir = [filePath stringByDeletingLastPathComponent];
79+
80+
NSError *error = nil;
81+
[[NSFileManager defaultManager] createDirectoryAtPath:dir
82+
withIntermediateDirectories:YES
83+
attributes:nil
84+
error:&error];
85+
if (error) {
86+
NSLog(@"Failed to create folder %@: %@", dir, error);
87+
return error;
88+
}
89+
90+
[NSData.data writeToFile:filePath options:NSDataWritingAtomic error:&error];
91+
if (error) {
92+
NSLog(@"Failed to touch reload file: %@", error);
93+
}
94+
95+
return error;
96+
}
97+
98+
/**
99+
* Watch a file and run a block whenever it is written.
100+
* Stops watching if the file is deleted.
101+
*/
102+
static void WatchFile(NSString *filePath, void (^onWrite)(void)) {
103+
if (![[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
104+
NSLog(@"File does not exist; skipping watcher.");
105+
return;
106+
}
107+
108+
int fd = open([filePath fileSystemRepresentation], O_EVTONLY);
109+
if (fd < 0) {
110+
NSLog(@"Failed to open file for monitoring");
111+
return;
112+
}
113+
114+
dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE,
115+
fd,
116+
DISPATCH_VNODE_WRITE | DISPATCH_VNODE_DELETE,
117+
DISPATCH_TARGET_QUEUE_DEFAULT);
118+
119+
dispatch_source_set_event_handler(source, ^{
120+
unsigned long flags = dispatch_source_get_data(source);
121+
122+
if (flags & DISPATCH_VNODE_WRITE) {
123+
if (onWrite) {
124+
dispatch_async(dispatch_get_main_queue(), ^{
125+
onWrite();
126+
});
127+
}
128+
}
129+
130+
if (flags & (DISPATCH_VNODE_DELETE | DISPATCH_VNODE_RENAME)) {
131+
NSLog(@"File deleted or renamed; stopping watcher.");
132+
dispatch_source_cancel(source);
133+
close(fd);
134+
}
135+
});
136+
137+
dispatch_resume(source);
138+
}
139+
#endif
140+
103141
@end

macos/rnmprebuilds-macOS/Info.plist

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -23,18 +23,18 @@
2323
<key>LSMinimumSystemVersion</key>
2424
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
2525
<key>NSAppTransportSecurity</key>
26-
<dict>
27-
<key>NSAllowsArbitraryLoads</key>
28-
<true/>
29-
<key>NSExceptionDomains</key>
30-
<dict>
31-
<key>localhost</key>
32-
<dict>
33-
<key>NSExceptionAllowsInsecureHTTPLoads</key>
34-
<true/>
35-
</dict>
36-
</dict>
37-
</dict>
26+
<dict>
27+
<key>NSAllowsArbitraryLoads</key>
28+
<true/>
29+
<key>NSExceptionDomains</key>
30+
<dict>
31+
<key>localhost</key>
32+
<dict>
33+
<key>NSExceptionAllowsInsecureHTTPLoads</key>
34+
<true/>
35+
</dict>
36+
</dict>
37+
</dict>
3838
<key>NSMainStoryboardFile</key>
3939
<string>Main</string>
4040
<key>NSPrincipalClass</key>

macos/rnmprebuilds.xcodeproj/project.pbxproj

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
/* Begin PBXFileReference section */
2020
13B07F961A680F5B00A75B9A /* rnmprebuilds.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = rnmprebuilds.app; sourceTree = BUILT_PRODUCTS_DIR; };
2121
3FBDD09B955A3EE1F0219A67 /* Pods-rnmprebuilds-macOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-rnmprebuilds-macOS.release.xcconfig"; path = "Target Support Files/Pods-rnmprebuilds-macOS/Pods-rnmprebuilds-macOS.release.xcconfig"; sourceTree = "<group>"; };
22-
514201492437B4B30078DB4F /* rnmprebuilds.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = rnmprebuilds.app; sourceTree = BUILT_PRODUCTS_DIR; };
22+
514201492437B4B30078DB4F /* Electron.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Electron.app; sourceTree = BUILT_PRODUCTS_DIR; };
2323
5142014B2437B4B30078DB4F /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
2424
5142014C2437B4B30078DB4F /* AppDelegate.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = AppDelegate.mm; sourceTree = "<group>"; };
2525
514201512437B4B40078DB4F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
@@ -110,7 +110,7 @@
110110
isa = PBXGroup;
111111
children = (
112112
13B07F961A680F5B00A75B9A /* rnmprebuilds.app */,
113-
514201492437B4B30078DB4F /* rnmprebuilds.app */,
113+
514201492437B4B30078DB4F /* Electron.app */,
114114
);
115115
name = Products;
116116
sourceTree = "<group>";
@@ -154,7 +154,7 @@
154154
);
155155
name = "rnmprebuilds-macOS";
156156
productName = rnmprebuilds;
157-
productReference = 514201492437B4B30078DB4F /* rnmprebuilds.app */;
157+
productReference = 514201492437B4B30078DB4F /* Electron.app */;
158158
productType = "com.apple.product-type.application";
159159
};
160160
/* End PBXNativeTarget section */
@@ -360,7 +360,7 @@
360360
"-ObjC",
361361
"-lc++",
362362
);
363-
PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.$(PRODUCT_NAME:rfc1034identifier)";
363+
PRODUCT_BUNDLE_IDENTIFIER = uk.co.birchlabs.rnfiddleclient;
364364
PRODUCT_NAME = rnmprebuilds;
365365
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
366366
SWIFT_VERSION = 5.0;
@@ -381,7 +381,7 @@
381381
"-ObjC",
382382
"-lc++",
383383
);
384-
PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.$(PRODUCT_NAME:rfc1034identifier)";
384+
PRODUCT_BUNDLE_IDENTIFIER = uk.co.birchlabs.rnfiddleclient;
385385
PRODUCT_NAME = rnmprebuilds;
386386
SWIFT_VERSION = 5.0;
387387
VERSIONING_SYSTEM = "apple-generic";
@@ -405,7 +405,7 @@
405405
"-ObjC",
406406
"-lc++",
407407
);
408-
PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.$(PRODUCT_NAME:rfc1034identifier)";
408+
PRODUCT_BUNDLE_IDENTIFIER = uk.co.birchlabs.rnfiddleclient;
409409
PRODUCT_NAME = Electron;
410410
SDKROOT = macosx;
411411
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
@@ -429,7 +429,7 @@
429429
"-ObjC",
430430
"-lc++",
431431
);
432-
PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.$(PRODUCT_NAME:rfc1034identifier)";
432+
PRODUCT_BUNDLE_IDENTIFIER = uk.co.birchlabs.rnfiddleclient;
433433
PRODUCT_NAME = Electron;
434434
SDKROOT = macosx;
435435
SWIFT_VERSION = 5.0;

macos/rnmprebuilds.xcodeproj/xcshareddata/xcschemes/rnmprebuilds-macOS.xcscheme

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
<BuildableReference
1616
BuildableIdentifier = "primary"
1717
BlueprintIdentifier = "514201482437B4B30078DB4F"
18-
BuildableName = "rnmprebuilds.app"
18+
BuildableName = "Electron.app"
1919
BlueprintName = "rnmprebuilds-macOS"
2020
ReferencedContainer = "container:rnmprebuilds.xcodeproj">
2121
</BuildableReference>
@@ -45,7 +45,7 @@
4545
<BuildableReference
4646
BuildableIdentifier = "primary"
4747
BlueprintIdentifier = "514201482437B4B30078DB4F"
48-
BuildableName = "rnmprebuilds.app"
48+
BuildableName = "Electron.app"
4949
BlueprintName = "rnmprebuilds-macOS"
5050
ReferencedContainer = "container:rnmprebuilds.xcodeproj">
5151
</BuildableReference>
@@ -62,7 +62,7 @@
6262
<BuildableReference
6363
BuildableIdentifier = "primary"
6464
BlueprintIdentifier = "514201482437B4B30078DB4F"
65-
BuildableName = "rnmprebuilds.app"
65+
BuildableName = "Electron.app"
6666
BlueprintName = "rnmprebuilds-macOS"
6767
ReferencedContainer = "container:rnmprebuilds.xcodeproj">
6868
</BuildableReference>

0 commit comments

Comments
 (0)