@granite-js/micro-frontend 2.0.0
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/GraniteMicroFrontendRuntime.podspec +40 -0
- package/LICENSE +202 -0
- package/LICENSE.react-native-teleport +21 -0
- package/NOTICE +17 -0
- package/README.md +508 -0
- package/android/CMakeLists.txt +34 -0
- package/android/build.gradle +74 -0
- package/android/gradle.properties +4 -0
- package/android/src/main/AndroidManifest.xml +1 -0
- package/android/src/main/cpp/BundleEvaluator.cpp +64 -0
- package/android/src/main/cpp/BundleEvaluator.h +20 -0
- package/android/src/main/cpp/FileReader.cpp +92 -0
- package/android/src/main/cpp/FileReader.h +27 -0
- package/android/src/main/cpp/onLoad.cpp +9 -0
- package/android/src/main/java/com/teleport/extensions/Float.kt +12 -0
- package/android/src/main/java/com/teleport/extensions/View.kt +10 -0
- package/android/src/main/java/com/teleport/global/PortalRegistry.kt +117 -0
- package/android/src/main/java/com/teleport/host/PortalHostView.kt +97 -0
- package/android/src/main/java/com/teleport/host/PortalHostViewManager.kt +41 -0
- package/android/src/main/java/com/teleport/host/PortalReactRootView.kt +116 -0
- package/android/src/main/java/com/teleport/managers/TeleportViewManager.kt +27 -0
- package/android/src/main/java/com/teleport/portal/PortalLayoutStateController.kt +107 -0
- package/android/src/main/java/com/teleport/portal/PortalView.kt +309 -0
- package/android/src/main/java/com/teleport/portal/PortalViewManager.kt +51 -0
- package/android/src/main/java/run/granite/microfrontend/ActivitySessionBinding.kt +87 -0
- package/android/src/main/java/run/granite/microfrontend/BundleEvaluator.kt +46 -0
- package/android/src/main/java/run/granite/microfrontend/GraniteMicroFrontendEvent.kt +62 -0
- package/android/src/main/java/run/granite/microfrontend/GraniteMicroFrontendRuntimeEventRouter.kt +186 -0
- package/android/src/main/java/run/granite/microfrontend/GraniteMicroFrontendRuntimeHost.kt +42 -0
- package/android/src/main/java/run/granite/microfrontend/GraniteMicroFrontendRuntimeModule.kt +56 -0
- package/android/src/main/java/run/granite/microfrontend/GraniteMicroFrontendRuntimePackage.kt +35 -0
- package/android/src/main/java/run/granite/microfrontend/GraniteMicroFrontendSessionStore.kt +116 -0
- package/android/src/main/jni/CMakeLists.txt +64 -0
- package/android/src/test/cpp/CMakeLists.txt +35 -0
- package/android/src/test/cpp/FileReaderTest.cpp +44 -0
- package/android/src/test/java/com/teleport/host/PortalReactRootViewContractTest.kt +76 -0
- package/android/src/test/java/run/granite/microfrontend/GraniteMicroFrontendRuntimeEventRouterTest.kt +216 -0
- package/android/src/test/java/run/granite/microfrontend/GraniteMicroFrontendSessionStoreTest.kt +190 -0
- package/android/test-cpp.sh +12 -0
- package/common/cpp/react/renderer/components/GraniteMicroFrontendRuntimeSpec/ComponentDescriptors.h +24 -0
- package/common/cpp/react/renderer/components/GraniteMicroFrontendRuntimeSpec/PortalShadowRegistry.cpp +31 -0
- package/common/cpp/react/renderer/components/GraniteMicroFrontendRuntimeSpec/PortalShadowRegistry.h +29 -0
- package/common/cpp/react/renderer/components/GraniteMicroFrontendRuntimeSpec/RNTPortalHostViewComponentDescriptor.h +22 -0
- package/common/cpp/react/renderer/components/GraniteMicroFrontendRuntimeSpec/RNTPortalHostViewShadowNode.cpp +7 -0
- package/common/cpp/react/renderer/components/GraniteMicroFrontendRuntimeSpec/RNTPortalHostViewShadowNode.h +43 -0
- package/common/cpp/react/renderer/components/GraniteMicroFrontendRuntimeSpec/RNTPortalHostViewState.h +21 -0
- package/common/cpp/react/renderer/components/GraniteMicroFrontendRuntimeSpec/RNTPortalViewComponentDescriptor.h +54 -0
- package/common/cpp/react/renderer/components/GraniteMicroFrontendRuntimeSpec/RNTPortalViewShadowNode.cpp +7 -0
- package/common/cpp/react/renderer/components/GraniteMicroFrontendRuntimeSpec/RNTPortalViewShadowNode.h +51 -0
- package/common/cpp/react/renderer/components/GraniteMicroFrontendRuntimeSpec/RNTPortalViewState.h +43 -0
- package/dist/module/createMicroFrontendRuntime.js +18 -0
- package/dist/module/createMicroFrontendRuntime.js.map +1 -0
- package/dist/module/createRoute.js +43 -0
- package/dist/module/createRoute.js.map +1 -0
- package/dist/module/host/PendingHostComponent.js +70 -0
- package/dist/module/host/PendingHostComponent.js.map +1 -0
- package/dist/module/host/index.js +6 -0
- package/dist/module/host/index.js.map +1 -0
- package/dist/module/host/pendingHostComponentStore.js +164 -0
- package/dist/module/host/pendingHostComponentStore.js.map +1 -0
- package/dist/module/host/resolveParams.js +60 -0
- package/dist/module/host/resolveParams.js.map +1 -0
- package/dist/module/host/routeMatcher.js +92 -0
- package/dist/module/host/routeMatcher.js.map +1 -0
- package/dist/module/host/types.js +4 -0
- package/dist/module/host/types.js.map +1 -0
- package/dist/module/index.js +12 -0
- package/dist/module/index.js.map +1 -0
- package/dist/module/package.json +1 -0
- package/dist/module/plugin/index.js +4 -0
- package/dist/module/plugin/index.js.map +1 -0
- package/dist/module/plugin/intoShared.js +32 -0
- package/dist/module/plugin/intoShared.js.map +1 -0
- package/dist/module/plugin/microFrontendPlugin.js +58 -0
- package/dist/module/plugin/microFrontendPlugin.js.map +1 -0
- package/dist/module/plugin/prelude.js +23 -0
- package/dist/module/plugin/prelude.js.map +1 -0
- package/dist/module/plugin/resolver.js +25 -0
- package/dist/module/plugin/resolver.js.map +1 -0
- package/dist/module/plugin/types.js +4 -0
- package/dist/module/plugin/types.js.map +1 -0
- package/dist/module/runtime/createMicroFrontendRuntime.js +79 -0
- package/dist/module/runtime/createMicroFrontendRuntime.js.map +1 -0
- package/dist/module/runtime/errors.js +63 -0
- package/dist/module/runtime/errors.js.map +1 -0
- package/dist/module/runtime/parseAppRequest.js +16 -0
- package/dist/module/runtime/parseAppRequest.js.map +1 -0
- package/dist/module/runtime/parseNativeRuntimeEvent.js +54 -0
- package/dist/module/runtime/parseNativeRuntimeEvent.js.map +1 -0
- package/dist/module/runtime/registry.js +87 -0
- package/dist/module/runtime/registry.js.map +1 -0
- package/dist/module/session/MicroFrontendSessionContext.js +32 -0
- package/dist/module/session/MicroFrontendSessionContext.js.map +1 -0
- package/dist/module/session/useMicroFrontendSessions.js +47 -0
- package/dist/module/session/useMicroFrontendSessions.js.map +1 -0
- package/dist/module/specs/NativeGraniteMicroFrontendRuntime.js +21 -0
- package/dist/module/specs/NativeGraniteMicroFrontendRuntime.js.map +1 -0
- package/dist/module/specs/PortalHostViewNativeComponent.ts +9 -0
- package/dist/module/specs/PortalViewNativeComponent.ts +9 -0
- package/dist/module/types.js +2 -0
- package/dist/module/types.js.map +1 -0
- package/dist/typescript/createMicroFrontendRuntime.d.ts +6 -0
- package/dist/typescript/createRoute.d.ts +23 -0
- package/dist/typescript/host/PendingHostComponent.d.ts +14 -0
- package/dist/typescript/host/index.d.ts +5 -0
- package/dist/typescript/host/pendingHostComponentStore.d.ts +11 -0
- package/dist/typescript/host/resolveParams.d.ts +10 -0
- package/dist/typescript/host/routeMatcher.d.ts +7 -0
- package/dist/typescript/host/types.d.ts +32 -0
- package/dist/typescript/index.d.ts +16 -0
- package/dist/typescript/plugin/index.d.ts +2 -0
- package/dist/typescript/plugin/intoShared.d.ts +3 -0
- package/dist/typescript/plugin/microFrontendPlugin.d.ts +3 -0
- package/dist/typescript/plugin/prelude.d.ts +6 -0
- package/dist/typescript/plugin/resolver.d.ts +3 -0
- package/dist/typescript/plugin/types.d.ts +5 -0
- package/dist/typescript/runtime/createMicroFrontendRuntime.d.ts +31 -0
- package/dist/typescript/runtime/errors.d.ts +35 -0
- package/dist/typescript/runtime/parseAppRequest.d.ts +6 -0
- package/dist/typescript/runtime/parseNativeRuntimeEvent.d.ts +3 -0
- package/dist/typescript/runtime/registry.d.ts +35 -0
- package/dist/typescript/session/MicroFrontendSessionContext.d.ts +13 -0
- package/dist/typescript/session/useMicroFrontendSessions.d.ts +8 -0
- package/dist/typescript/specs/NativeGraniteMicroFrontendRuntime.d.ts +21 -0
- package/dist/typescript/specs/PortalHostViewNativeComponent.d.ts +6 -0
- package/dist/typescript/specs/PortalViewNativeComponent.d.ts +6 -0
- package/dist/typescript/types.d.ts +49 -0
- package/ios/GraniteMicroFrontendRuntimeHost+Internal.h +18 -0
- package/ios/GraniteMicroFrontendRuntimeHost.h +34 -0
- package/ios/GraniteMicroFrontendRuntimeHost.mm +485 -0
- package/ios/GraniteMicroFrontendRuntimeModule.h +6 -0
- package/ios/GraniteMicroFrontendRuntimeModule.mm +138 -0
- package/ios/PortalHostContainerView.h +56 -0
- package/ios/PortalHostContainerView.mm +133 -0
- package/ios/PortalHostView.h +39 -0
- package/ios/PortalHostView.mm +153 -0
- package/ios/PortalRegistry.h +38 -0
- package/ios/PortalRegistry.mm +228 -0
- package/ios/PortalView.h +32 -0
- package/ios/PortalView.mm +426 -0
- package/package.json +113 -0
- package/react-native.config.js +13 -0
- package/src/createMicroFrontendRuntime.ts +22 -0
- package/src/createRoute.ts +84 -0
- package/src/host/PendingHostComponent.tsx +92 -0
- package/src/host/index.ts +26 -0
- package/src/host/pendingHostComponentStore.ts +241 -0
- package/src/host/resolveParams.ts +70 -0
- package/src/host/routeMatcher.ts +117 -0
- package/src/host/types.ts +45 -0
- package/src/index.ts +57 -0
- package/src/plugin/index.ts +2 -0
- package/src/plugin/intoShared.ts +50 -0
- package/src/plugin/microFrontendPlugin.ts +57 -0
- package/src/plugin/prelude.ts +73 -0
- package/src/plugin/resolver.ts +73 -0
- package/src/plugin/types.ts +6 -0
- package/src/runtime/createMicroFrontendRuntime.ts +115 -0
- package/src/runtime/errors.ts +86 -0
- package/src/runtime/parseAppRequest.ts +22 -0
- package/src/runtime/parseNativeRuntimeEvent.ts +54 -0
- package/src/runtime/registry.ts +131 -0
- package/src/session/MicroFrontendSessionContext.tsx +45 -0
- package/src/session/useMicroFrontendSessions.ts +73 -0
- package/src/specs/NativeGraniteMicroFrontendRuntime.ts +44 -0
- package/src/specs/PortalHostViewNativeComponent.ts +9 -0
- package/src/specs/PortalViewNativeComponent.ts +9 -0
- package/src/types.ts +55 -0
|
@@ -0,0 +1,485 @@
|
|
|
1
|
+
#import "GraniteMicroFrontendRuntimeHost+Internal.h"
|
|
2
|
+
#import <objc/runtime.h>
|
|
3
|
+
#import <TargetConditionals.h>
|
|
4
|
+
|
|
5
|
+
#if TARGET_OS_IOS || TARGET_OS_TV
|
|
6
|
+
#import <UIKit/UIKit.h>
|
|
7
|
+
#define GRANITE_MICRO_FRONTEND_HAS_UIKIT 1
|
|
8
|
+
#else
|
|
9
|
+
#define GRANITE_MICRO_FRONTEND_HAS_UIKIT 0
|
|
10
|
+
#endif
|
|
11
|
+
|
|
12
|
+
@class GraniteMicroFrontendSessionEntry;
|
|
13
|
+
|
|
14
|
+
static NSRecursiveLock *runtimeLock;
|
|
15
|
+
static NSMutableDictionary<NSString *, GraniteMicroFrontendSessionEntry *> *sessions;
|
|
16
|
+
static NSMutableArray<NSDictionary *> *pendingEvents;
|
|
17
|
+
static __weak id<GraniteMicroFrontendRuntimeEventSink> eventSink;
|
|
18
|
+
static BOOL isDeliveringEvents;
|
|
19
|
+
|
|
20
|
+
static void GraniteMicroFrontendRequireMainThread(void) {
|
|
21
|
+
if (!NSThread.isMainThread) {
|
|
22
|
+
@throw [NSException exceptionWithName:NSInternalInconsistencyException
|
|
23
|
+
reason:@"Session bindings must be used on the main thread"
|
|
24
|
+
userInfo:nil];
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
@interface GraniteMicroFrontendSessionEntry : NSObject
|
|
29
|
+
@property(nonatomic, copy) NSString *token;
|
|
30
|
+
@property(nonatomic, assign) BOOL opened;
|
|
31
|
+
@property(nonatomic, assign) BOOL isVisible;
|
|
32
|
+
@property(nonatomic, assign) BOOL closed;
|
|
33
|
+
@end
|
|
34
|
+
|
|
35
|
+
@implementation GraniteMicroFrontendSessionEntry
|
|
36
|
+
@end
|
|
37
|
+
|
|
38
|
+
@interface GraniteMicroFrontendSessionRegistration ()
|
|
39
|
+
@property(nonatomic, copy) NSString *sessionId;
|
|
40
|
+
@property(nonatomic, copy) NSString *token;
|
|
41
|
+
@property(nonatomic, assign) BOOL invalidated;
|
|
42
|
+
@end
|
|
43
|
+
|
|
44
|
+
@class GraniteMicroFrontendSessionLifecycleObserverViewController;
|
|
45
|
+
|
|
46
|
+
#if GRANITE_MICRO_FRONTEND_HAS_UIKIT
|
|
47
|
+
@interface GraniteMicroFrontendViewControllerSessionBinding ()
|
|
48
|
+
@property(nonatomic, weak) UIViewController *viewController;
|
|
49
|
+
@property(nonatomic, strong) GraniteMicroFrontendSessionRegistration *registration;
|
|
50
|
+
@property(nonatomic, strong) GraniteMicroFrontendSessionLifecycleObserverViewController *observer;
|
|
51
|
+
@property(nonatomic, assign) BOOL viewAppeared;
|
|
52
|
+
@property(nonatomic, assign) BOOL applicationForeground;
|
|
53
|
+
@property(nonatomic, assign) BOOL invalidated;
|
|
54
|
+
- (void)updateVisibility;
|
|
55
|
+
@end
|
|
56
|
+
#endif
|
|
57
|
+
|
|
58
|
+
@interface GraniteMicroFrontendRuntimeHost ()
|
|
59
|
+
+ (void)drainEventsToSink:(id<GraniteMicroFrontendRuntimeEventSink>)sink;
|
|
60
|
+
@end
|
|
61
|
+
|
|
62
|
+
@implementation GraniteMicroFrontendSessionRegistration
|
|
63
|
+
|
|
64
|
+
- (void)openAppWithAppName:(NSString *)appName scheme:(NSString *)scheme {
|
|
65
|
+
NSParameterAssert(appName.length > 0);
|
|
66
|
+
NSParameterAssert(scheme.length > 0);
|
|
67
|
+
BOOL shouldEmit = NO;
|
|
68
|
+
[runtimeLock lock];
|
|
69
|
+
@try {
|
|
70
|
+
GraniteMicroFrontendSessionEntry *entry = sessions[_sessionId];
|
|
71
|
+
if ([entry.token isEqualToString:_token] && !entry.opened && !entry.closed) {
|
|
72
|
+
entry.opened = YES;
|
|
73
|
+
shouldEmit = YES;
|
|
74
|
+
}
|
|
75
|
+
} @finally {
|
|
76
|
+
[runtimeLock unlock];
|
|
77
|
+
}
|
|
78
|
+
if (shouldEmit) {
|
|
79
|
+
[GraniteMicroFrontendRuntimeHost emitOpenApp:_sessionId appName:appName scheme:scheme];
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
- (BOOL)setVisible:(BOOL)isVisible {
|
|
84
|
+
BOOL shouldEmit = NO;
|
|
85
|
+
[runtimeLock lock];
|
|
86
|
+
@try {
|
|
87
|
+
GraniteMicroFrontendSessionEntry *entry = sessions[_sessionId];
|
|
88
|
+
if ([entry.token isEqualToString:_token] && entry.opened && !entry.closed &&
|
|
89
|
+
entry.isVisible != isVisible) {
|
|
90
|
+
entry.isVisible = isVisible;
|
|
91
|
+
shouldEmit = YES;
|
|
92
|
+
}
|
|
93
|
+
} @finally {
|
|
94
|
+
[runtimeLock unlock];
|
|
95
|
+
}
|
|
96
|
+
if (shouldEmit) {
|
|
97
|
+
[GraniteMicroFrontendRuntimeHost emitSessionVisibilityChanged:_sessionId
|
|
98
|
+
isVisible:isVisible];
|
|
99
|
+
}
|
|
100
|
+
return shouldEmit;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
- (BOOL)closeApp {
|
|
104
|
+
BOOL shouldEmit = NO;
|
|
105
|
+
[runtimeLock lock];
|
|
106
|
+
@try {
|
|
107
|
+
GraniteMicroFrontendSessionEntry *entry = sessions[_sessionId];
|
|
108
|
+
if ([entry.token isEqualToString:_token] && entry.opened && !entry.closed) {
|
|
109
|
+
entry.closed = YES;
|
|
110
|
+
shouldEmit = YES;
|
|
111
|
+
}
|
|
112
|
+
} @finally {
|
|
113
|
+
[runtimeLock unlock];
|
|
114
|
+
}
|
|
115
|
+
if (shouldEmit) {
|
|
116
|
+
[GraniteMicroFrontendRuntimeHost emitCloseApp:_sessionId];
|
|
117
|
+
}
|
|
118
|
+
return shouldEmit;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
- (void)invalidate {
|
|
122
|
+
@synchronized(self) {
|
|
123
|
+
if (_invalidated) {
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
_invalidated = YES;
|
|
127
|
+
}
|
|
128
|
+
[GraniteMicroFrontendRuntimeHost unregisterSession:_sessionId token:_token];
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
- (void)dealloc {
|
|
132
|
+
[self invalidate];
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
@end
|
|
136
|
+
|
|
137
|
+
#if GRANITE_MICRO_FRONTEND_HAS_UIKIT
|
|
138
|
+
|
|
139
|
+
@interface GraniteMicroFrontendSessionLifecycleObserverViewController : UIViewController
|
|
140
|
+
@property(nonatomic, weak) GraniteMicroFrontendViewControllerSessionBinding *binding;
|
|
141
|
+
@end
|
|
142
|
+
|
|
143
|
+
@implementation GraniteMicroFrontendSessionLifecycleObserverViewController
|
|
144
|
+
|
|
145
|
+
- (void)viewWillAppear:(BOOL)animated {
|
|
146
|
+
[super viewWillAppear:animated];
|
|
147
|
+
_binding.viewAppeared = YES;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
- (void)viewWillDisappear:(BOOL)animated {
|
|
151
|
+
_binding.viewAppeared = NO;
|
|
152
|
+
[super viewWillDisappear:animated];
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
- (void)dealloc {
|
|
156
|
+
[_binding invalidate];
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
@end
|
|
160
|
+
|
|
161
|
+
@implementation GraniteMicroFrontendViewControllerSessionBinding
|
|
162
|
+
|
|
163
|
+
static char GraniteMicroFrontendViewControllerSessionBindingKey;
|
|
164
|
+
|
|
165
|
+
+ (nullable GraniteMicroFrontendViewControllerSessionBinding *)bindViewController:(UIViewController *)viewController
|
|
166
|
+
sessionId:(NSString *)sessionId
|
|
167
|
+
appName:(NSString *)appName
|
|
168
|
+
scheme:(NSString *)scheme {
|
|
169
|
+
GraniteMicroFrontendRequireMainThread();
|
|
170
|
+
NSParameterAssert(viewController != nil);
|
|
171
|
+
NSParameterAssert(sessionId.length > 0);
|
|
172
|
+
NSParameterAssert(appName.length > 0);
|
|
173
|
+
NSParameterAssert(scheme.length > 0);
|
|
174
|
+
|
|
175
|
+
GraniteMicroFrontendViewControllerSessionBinding *existingBinding =
|
|
176
|
+
objc_getAssociatedObject(viewController, &GraniteMicroFrontendViewControllerSessionBindingKey);
|
|
177
|
+
if (existingBinding != nil) {
|
|
178
|
+
return existingBinding;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
GraniteMicroFrontendSessionRegistration *registration =
|
|
182
|
+
[GraniteMicroFrontendRuntimeHost registerSession:sessionId];
|
|
183
|
+
if (registration == nil) {
|
|
184
|
+
return nil;
|
|
185
|
+
}
|
|
186
|
+
GraniteMicroFrontendViewControllerSessionBinding *binding =
|
|
187
|
+
[[GraniteMicroFrontendViewControllerSessionBinding alloc] init];
|
|
188
|
+
GraniteMicroFrontendSessionLifecycleObserverViewController *observer =
|
|
189
|
+
[[GraniteMicroFrontendSessionLifecycleObserverViewController alloc] init];
|
|
190
|
+
observer.binding = binding;
|
|
191
|
+
binding.viewController = viewController;
|
|
192
|
+
binding.registration = registration;
|
|
193
|
+
binding.observer = observer;
|
|
194
|
+
binding.applicationForeground =
|
|
195
|
+
UIApplication.sharedApplication.applicationState != UIApplicationStateBackground;
|
|
196
|
+
|
|
197
|
+
[NSNotificationCenter.defaultCenter addObserver:binding
|
|
198
|
+
selector:@selector(applicationWillEnterForeground)
|
|
199
|
+
name:UIApplicationWillEnterForegroundNotification
|
|
200
|
+
object:nil];
|
|
201
|
+
[NSNotificationCenter.defaultCenter addObserver:binding
|
|
202
|
+
selector:@selector(applicationDidEnterBackground)
|
|
203
|
+
name:UIApplicationDidEnterBackgroundNotification
|
|
204
|
+
object:nil];
|
|
205
|
+
|
|
206
|
+
[viewController addChildViewController:observer];
|
|
207
|
+
observer.view.hidden = YES;
|
|
208
|
+
observer.view.frame = CGRectZero;
|
|
209
|
+
observer.view.userInteractionEnabled = NO;
|
|
210
|
+
[viewController.view addSubview:observer.view];
|
|
211
|
+
[observer didMoveToParentViewController:viewController];
|
|
212
|
+
|
|
213
|
+
objc_setAssociatedObject(
|
|
214
|
+
viewController,
|
|
215
|
+
&GraniteMicroFrontendViewControllerSessionBindingKey,
|
|
216
|
+
binding,
|
|
217
|
+
OBJC_ASSOCIATION_RETAIN_NONATOMIC);
|
|
218
|
+
[registration openAppWithAppName:appName scheme:scheme];
|
|
219
|
+
[binding updateVisibility];
|
|
220
|
+
return binding;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
- (void)setViewAppeared:(BOOL)viewAppeared {
|
|
224
|
+
if (_viewAppeared == viewAppeared) {
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
_viewAppeared = viewAppeared;
|
|
228
|
+
[self updateVisibility];
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
- (void)setApplicationForeground:(BOOL)applicationForeground {
|
|
232
|
+
if (_applicationForeground == applicationForeground) {
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
_applicationForeground = applicationForeground;
|
|
236
|
+
[self updateVisibility];
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
- (void)applicationWillEnterForeground {
|
|
240
|
+
self.applicationForeground = YES;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
- (void)applicationDidEnterBackground {
|
|
244
|
+
self.applicationForeground = NO;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
- (void)updateVisibility {
|
|
248
|
+
GraniteMicroFrontendRequireMainThread();
|
|
249
|
+
BOOL invalidated = NO;
|
|
250
|
+
@synchronized(self) {
|
|
251
|
+
invalidated = _invalidated;
|
|
252
|
+
}
|
|
253
|
+
if (invalidated) {
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
[_registration setVisible:(_viewAppeared && _applicationForeground)];
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
- (void)invalidate {
|
|
260
|
+
GraniteMicroFrontendRequireMainThread();
|
|
261
|
+
@synchronized(self) {
|
|
262
|
+
if (_invalidated) {
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
_invalidated = YES;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
[NSNotificationCenter.defaultCenter removeObserver:self
|
|
269
|
+
name:UIApplicationWillEnterForegroundNotification
|
|
270
|
+
object:nil];
|
|
271
|
+
[NSNotificationCenter.defaultCenter removeObserver:self
|
|
272
|
+
name:UIApplicationDidEnterBackgroundNotification
|
|
273
|
+
object:nil];
|
|
274
|
+
|
|
275
|
+
UIViewController *viewController = _viewController;
|
|
276
|
+
_observer.binding = nil;
|
|
277
|
+
if (_observer.parentViewController != nil) {
|
|
278
|
+
[_observer willMoveToParentViewController:nil];
|
|
279
|
+
[_observer.view removeFromSuperview];
|
|
280
|
+
[_observer removeFromParentViewController];
|
|
281
|
+
}
|
|
282
|
+
[_registration closeApp];
|
|
283
|
+
[_registration invalidate];
|
|
284
|
+
if (viewController != nil &&
|
|
285
|
+
objc_getAssociatedObject(viewController, &GraniteMicroFrontendViewControllerSessionBindingKey) == self) {
|
|
286
|
+
objc_setAssociatedObject(
|
|
287
|
+
viewController,
|
|
288
|
+
&GraniteMicroFrontendViewControllerSessionBindingKey,
|
|
289
|
+
nil,
|
|
290
|
+
OBJC_ASSOCIATION_ASSIGN);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
- (void)dealloc {
|
|
295
|
+
[self invalidate];
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
@end
|
|
299
|
+
|
|
300
|
+
#else
|
|
301
|
+
|
|
302
|
+
@implementation GraniteMicroFrontendViewControllerSessionBinding
|
|
303
|
+
|
|
304
|
+
+ (nullable GraniteMicroFrontendViewControllerSessionBinding *)bindViewController:(UIViewController *)viewController
|
|
305
|
+
sessionId:(NSString *)sessionId
|
|
306
|
+
appName:(NSString *)appName
|
|
307
|
+
scheme:(NSString *)scheme {
|
|
308
|
+
NSParameterAssert(NO);
|
|
309
|
+
return nil;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
- (void)invalidate {
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
@end
|
|
316
|
+
|
|
317
|
+
#endif
|
|
318
|
+
|
|
319
|
+
@implementation GraniteMicroFrontendRuntimeHost
|
|
320
|
+
|
|
321
|
+
+ (void)initialize {
|
|
322
|
+
if (self != GraniteMicroFrontendRuntimeHost.class) {
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
runtimeLock = [[NSRecursiveLock alloc] init];
|
|
326
|
+
sessions = [[NSMutableDictionary alloc] init];
|
|
327
|
+
pendingEvents = [[NSMutableArray alloc] init];
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
+ (nullable GraniteMicroFrontendSessionRegistration *)registerSession:(NSString *)sessionId {
|
|
331
|
+
NSParameterAssert(sessionId.length > 0);
|
|
332
|
+
|
|
333
|
+
NSString *token = NSUUID.UUID.UUIDString;
|
|
334
|
+
GraniteMicroFrontendSessionEntry *entry = [[GraniteMicroFrontendSessionEntry alloc] init];
|
|
335
|
+
entry.token = token;
|
|
336
|
+
|
|
337
|
+
[runtimeLock lock];
|
|
338
|
+
if (sessions[sessionId] != nil) {
|
|
339
|
+
[runtimeLock unlock];
|
|
340
|
+
// Do not throw: Swift cannot catch NSException and a reuse race during
|
|
341
|
+
// VC teardown would crash the host. Callers must use unique sessionIds and
|
|
342
|
+
// invalidate the previous registration before reusing an id.
|
|
343
|
+
return nil;
|
|
344
|
+
}
|
|
345
|
+
sessions[sessionId] = entry;
|
|
346
|
+
[runtimeLock unlock];
|
|
347
|
+
|
|
348
|
+
GraniteMicroFrontendSessionRegistration *registration =
|
|
349
|
+
[[GraniteMicroFrontendSessionRegistration alloc] init];
|
|
350
|
+
registration.sessionId = sessionId;
|
|
351
|
+
registration.token = token;
|
|
352
|
+
return registration;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
+ (void)emitPreloadApp:(NSString *)appName {
|
|
356
|
+
[self emitEvent:@{ @"name" : @"preloadApp", @"params" : @{ @"appName" : appName } }];
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
+ (void)emitOpenApp:(NSString *)sessionId
|
|
360
|
+
appName:(NSString *)appName
|
|
361
|
+
scheme:(NSString *)scheme {
|
|
362
|
+
[self emitEvent:@{
|
|
363
|
+
@"name" : @"openApp",
|
|
364
|
+
@"params" : @{
|
|
365
|
+
@"sessionId" : sessionId,
|
|
366
|
+
@"appName" : appName,
|
|
367
|
+
@"scheme" : scheme,
|
|
368
|
+
},
|
|
369
|
+
}];
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
+ (void)emitCloseApp:(NSString *)sessionId {
|
|
373
|
+
[self emitEvent:@{ @"name" : @"closeApp", @"params" : @{ @"sessionId" : sessionId } }];
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
+ (void)emitSessionVisibilityChanged:(NSString *)sessionId
|
|
377
|
+
isVisible:(BOOL)isVisible {
|
|
378
|
+
[self emitEvent:@{
|
|
379
|
+
@"name" : @"sessionVisibilityChanged",
|
|
380
|
+
@"params" : @{
|
|
381
|
+
@"sessionId" : sessionId,
|
|
382
|
+
@"isVisible" : @(isVisible),
|
|
383
|
+
},
|
|
384
|
+
}];
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
+ (void)emitEvent:(NSDictionary *)event {
|
|
388
|
+
[runtimeLock lock];
|
|
389
|
+
[pendingEvents addObject:event];
|
|
390
|
+
id<GraniteMicroFrontendRuntimeEventSink> sink = eventSink;
|
|
391
|
+
BOOL shouldDrain = sink != nil && !isDeliveringEvents;
|
|
392
|
+
if (shouldDrain) {
|
|
393
|
+
isDeliveringEvents = YES;
|
|
394
|
+
}
|
|
395
|
+
[runtimeLock unlock];
|
|
396
|
+
if (shouldDrain) {
|
|
397
|
+
[self drainEventsToSink:sink];
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
+ (void)startEventDeliveryToEventSink:(id<GraniteMicroFrontendRuntimeEventSink>)sink {
|
|
402
|
+
[runtimeLock lock];
|
|
403
|
+
if (eventSink != nil && eventSink != sink) {
|
|
404
|
+
[runtimeLock unlock];
|
|
405
|
+
return;
|
|
406
|
+
}
|
|
407
|
+
eventSink = sink;
|
|
408
|
+
BOOL shouldDrain = !isDeliveringEvents && pendingEvents.count > 0;
|
|
409
|
+
if (shouldDrain) {
|
|
410
|
+
isDeliveringEvents = YES;
|
|
411
|
+
}
|
|
412
|
+
[runtimeLock unlock];
|
|
413
|
+
if (shouldDrain) {
|
|
414
|
+
[self drainEventsToSink:sink];
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
+ (void)drainEventsToSink:(id<GraniteMicroFrontendRuntimeEventSink>)sink {
|
|
419
|
+
id<GraniteMicroFrontendRuntimeEventSink> currentSink = sink;
|
|
420
|
+
while (YES) {
|
|
421
|
+
[runtimeLock lock];
|
|
422
|
+
id<GraniteMicroFrontendRuntimeEventSink> activeSink = eventSink;
|
|
423
|
+
if (activeSink == nil || pendingEvents.count == 0) {
|
|
424
|
+
isDeliveringEvents = NO;
|
|
425
|
+
[runtimeLock unlock];
|
|
426
|
+
return;
|
|
427
|
+
}
|
|
428
|
+
currentSink = activeSink;
|
|
429
|
+
NSDictionary *event = pendingEvents.firstObject;
|
|
430
|
+
[runtimeLock unlock];
|
|
431
|
+
|
|
432
|
+
@try {
|
|
433
|
+
if (![currentSink enqueueRuntimeEvent:event]) {
|
|
434
|
+
[runtimeLock lock];
|
|
435
|
+
isDeliveringEvents = NO;
|
|
436
|
+
id<GraniteMicroFrontendRuntimeEventSink> replacementSink = eventSink;
|
|
437
|
+
BOOL shouldResumeWithReplacement =
|
|
438
|
+
replacementSink != nil && replacementSink != currentSink && pendingEvents.count > 0;
|
|
439
|
+
if (shouldResumeWithReplacement) {
|
|
440
|
+
isDeliveringEvents = YES;
|
|
441
|
+
}
|
|
442
|
+
[runtimeLock unlock];
|
|
443
|
+
if (shouldResumeWithReplacement) {
|
|
444
|
+
[self drainEventsToSink:replacementSink];
|
|
445
|
+
}
|
|
446
|
+
return;
|
|
447
|
+
}
|
|
448
|
+
[runtimeLock lock];
|
|
449
|
+
[pendingEvents removeObjectAtIndex:0];
|
|
450
|
+
[runtimeLock unlock];
|
|
451
|
+
} @catch (NSException *exception) {
|
|
452
|
+
[runtimeLock lock];
|
|
453
|
+
isDeliveringEvents = NO;
|
|
454
|
+
id<GraniteMicroFrontendRuntimeEventSink> replacementSink = eventSink;
|
|
455
|
+
BOOL shouldResumeWithReplacement =
|
|
456
|
+
replacementSink != nil && replacementSink != currentSink && pendingEvents.count > 0;
|
|
457
|
+
if (shouldResumeWithReplacement) {
|
|
458
|
+
isDeliveringEvents = YES;
|
|
459
|
+
}
|
|
460
|
+
[runtimeLock unlock];
|
|
461
|
+
if (shouldResumeWithReplacement) {
|
|
462
|
+
[self drainEventsToSink:replacementSink];
|
|
463
|
+
}
|
|
464
|
+
@throw exception;
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
+ (void)detachEventSink:(id<GraniteMicroFrontendRuntimeEventSink>)sink {
|
|
470
|
+
[runtimeLock lock];
|
|
471
|
+
if (eventSink == sink) {
|
|
472
|
+
eventSink = nil;
|
|
473
|
+
}
|
|
474
|
+
[runtimeLock unlock];
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
+ (void)unregisterSession:(NSString *)sessionId token:(NSString *)token {
|
|
478
|
+
[runtimeLock lock];
|
|
479
|
+
if ([sessions[sessionId].token isEqualToString:token]) {
|
|
480
|
+
[sessions removeObjectForKey:sessionId];
|
|
481
|
+
}
|
|
482
|
+
[runtimeLock unlock];
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
@end
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
#import "GraniteMicroFrontendRuntimeModule.h"
|
|
2
|
+
|
|
3
|
+
#import "GraniteMicroFrontendRuntimeHost+Internal.h"
|
|
4
|
+
#import <React/RCTBridge.h>
|
|
5
|
+
#import <React/RCTBridgeProxy+Cxx.h>
|
|
6
|
+
#import <React-callinvoker/ReactCommon/CallInvoker.h>
|
|
7
|
+
#import <jsi/jsi.h>
|
|
8
|
+
|
|
9
|
+
static NSString *const GraniteMicroFrontendRuntimeErrorDomain =
|
|
10
|
+
@"GraniteMicroFrontendRuntime";
|
|
11
|
+
|
|
12
|
+
@interface RCTBridge (GraniteMicroFrontendRuntime)
|
|
13
|
+
- (void *)runtime;
|
|
14
|
+
- (std::shared_ptr<facebook::react::CallInvoker>)jsCallInvoker;
|
|
15
|
+
@end
|
|
16
|
+
|
|
17
|
+
@interface GraniteMicroFrontendRuntimeModule () <GraniteMicroFrontendRuntimeEventSink>
|
|
18
|
+
@property(nonatomic, weak) RCTBridge *bridge;
|
|
19
|
+
- (BOOL)scheduleRuntimeEvent:(NSDictionary *)event;
|
|
20
|
+
@end
|
|
21
|
+
|
|
22
|
+
@implementation GraniteMicroFrontendRuntimeModule
|
|
23
|
+
|
|
24
|
+
RCT_EXPORT_MODULE(GraniteMicroFrontendRuntime)
|
|
25
|
+
|
|
26
|
+
+ (BOOL)requiresMainQueueSetup {
|
|
27
|
+
return NO;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
- (void)dealloc {
|
|
31
|
+
[GraniteMicroFrontendRuntimeHost detachEventSink:self];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
- (std::shared_ptr<facebook::react::CallInvoker>)jsCallInvokerIfAvailable {
|
|
35
|
+
RCTBridge *bridge = self.bridge;
|
|
36
|
+
if (bridge == nil) {
|
|
37
|
+
return nullptr;
|
|
38
|
+
}
|
|
39
|
+
// Prefer the public CallInvoker on RCTBridge / bridge proxy without casting
|
|
40
|
+
// to a concrete bridge subclass.
|
|
41
|
+
return bridge.jsCallInvoker;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
- (facebook::jsi::Runtime *)jsRuntimeIfAvailable {
|
|
45
|
+
RCTBridge *bridge = self.bridge;
|
|
46
|
+
if (bridge == nil || ![bridge respondsToSelector:@selector(runtime)]) {
|
|
47
|
+
return nullptr;
|
|
48
|
+
}
|
|
49
|
+
return reinterpret_cast<facebook::jsi::Runtime *>(bridge.runtime);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
- (void)evaluateScript:(JS::NativeGraniteMicroFrontendRuntime::EvaluateScriptRequest &)request
|
|
53
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
54
|
+
reject:(RCTPromiseRejectBlock)reject {
|
|
55
|
+
NSString *filePath = request.filePath();
|
|
56
|
+
if (![filePath isAbsolutePath]) {
|
|
57
|
+
reject(@"INVALID_BUNDLE_PATH", @"Bundle path must be absolute", nil);
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
__weak GraniteMicroFrontendRuntimeModule *weakSelf = self;
|
|
62
|
+
dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), ^{
|
|
63
|
+
NSError *readError = nil;
|
|
64
|
+
NSData *scriptData = [NSData dataWithContentsOfFile:filePath
|
|
65
|
+
options:NSDataReadingMappedIfSafe
|
|
66
|
+
error:&readError];
|
|
67
|
+
if (scriptData == nil) {
|
|
68
|
+
reject(@"BUNDLE_READ_FAILED", readError.localizedDescription, readError);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
std::string source{static_cast<const char *>(scriptData.bytes), scriptData.length};
|
|
73
|
+
std::string sourceUrl{[NSURL fileURLWithPath:filePath].absoluteString.UTF8String};
|
|
74
|
+
GraniteMicroFrontendRuntimeModule *strongSelf = weakSelf;
|
|
75
|
+
if (strongSelf == nil) {
|
|
76
|
+
reject(@"RUNTIME_UNAVAILABLE", @"JavaScript runtime is unavailable", nil);
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
std::shared_ptr<facebook::react::CallInvoker> callInvoker =
|
|
81
|
+
[strongSelf jsCallInvokerIfAvailable];
|
|
82
|
+
if (callInvoker == nullptr) {
|
|
83
|
+
reject(@"RUNTIME_UNAVAILABLE", @"JavaScript runtime is unavailable", nil);
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Read the JSI runtime only on the JS queue owned by callInvoker.
|
|
88
|
+
callInvoker->invokeAsync([strongSelf,
|
|
89
|
+
source = std::move(source),
|
|
90
|
+
sourceUrl = std::move(sourceUrl),
|
|
91
|
+
resolve,
|
|
92
|
+
reject]() mutable {
|
|
93
|
+
facebook::jsi::Runtime *runtime = [strongSelf jsRuntimeIfAvailable];
|
|
94
|
+
if (runtime == nullptr) {
|
|
95
|
+
reject(@"RUNTIME_UNAVAILABLE", @"JavaScript runtime is unavailable", nil);
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
try {
|
|
99
|
+
runtime->evaluateJavaScript(
|
|
100
|
+
std::make_unique<facebook::jsi::StringBuffer>(std::move(source)),
|
|
101
|
+
sourceUrl);
|
|
102
|
+
resolve(nil);
|
|
103
|
+
} catch (const std::exception &exception) {
|
|
104
|
+
NSString *message = [NSString stringWithUTF8String:exception.what()];
|
|
105
|
+
NSError *error = [NSError errorWithDomain:GraniteMicroFrontendRuntimeErrorDomain
|
|
106
|
+
code:500
|
|
107
|
+
userInfo:@{ NSLocalizedDescriptionKey : message }];
|
|
108
|
+
reject(@"EVALUATE_SCRIPT_FAILED", message, error);
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
- (void)startEventDelivery {
|
|
115
|
+
[GraniteMicroFrontendRuntimeHost startEventDeliveryToEventSink:self];
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
- (BOOL)enqueueRuntimeEvent:(NSDictionary *)event {
|
|
119
|
+
return [self scheduleRuntimeEvent:event];
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
- (BOOL)scheduleRuntimeEvent:(NSDictionary *)event {
|
|
123
|
+
std::shared_ptr<facebook::react::CallInvoker> callInvoker = [self jsCallInvokerIfAvailable];
|
|
124
|
+
if (callInvoker == nullptr) {
|
|
125
|
+
return NO;
|
|
126
|
+
}
|
|
127
|
+
callInvoker->invokeAsync([self, event] {
|
|
128
|
+
[self emitOnEvent:event];
|
|
129
|
+
});
|
|
130
|
+
return YES;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
|
|
134
|
+
(const facebook::react::ObjCTurboModule::InitParams &)params {
|
|
135
|
+
return std::make_shared<facebook::react::NativeGraniteMicroFrontendRuntimeSpecJSI>(params);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
@end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
#import <UIKit/UIKit.h>
|
|
2
|
+
|
|
3
|
+
NS_ASSUME_NONNULL_BEGIN
|
|
4
|
+
|
|
5
|
+
/// UIKit-owned container for hosting teleported portal content outside the
|
|
6
|
+
/// React tree. This header intentionally imports UIKit only, so native
|
|
7
|
+
/// integrations can include it in build configurations where the React
|
|
8
|
+
/// renderer headers are unavailable (e.g. prebuilt React distributions).
|
|
9
|
+
@interface PortalHostContainerView : UIView
|
|
10
|
+
|
|
11
|
+
/// Creates a container that activates immediately (see `-activateIfNeeded`).
|
|
12
|
+
- (instancetype)initWithFrame:(CGRect)frame;
|
|
13
|
+
|
|
14
|
+
/// Creates a container that postpones creating the underlying Fabric host
|
|
15
|
+
/// view until `-activateIfNeeded` is called.
|
|
16
|
+
///
|
|
17
|
+
/// Deferred activation exists because constructing the host view runs
|
|
18
|
+
/// `RCTViewComponentView` initialization, which reads ReactNativeFeatureFlags.
|
|
19
|
+
/// React only allows overriding those flags before their first read, so a
|
|
20
|
+
/// container created before the app's React factory has booted (a parked
|
|
21
|
+
/// native destination, for example) would otherwise make the boot-time
|
|
22
|
+
/// override throw. Defer activation and call `-activateIfNeeded` once the
|
|
23
|
+
/// runtime is up — typically right before asking React to mount content.
|
|
24
|
+
- (instancetype)initWithFrame:(CGRect)frame deferredActivation:(BOOL)deferredActivation;
|
|
25
|
+
|
|
26
|
+
/// Registers the container under `name` so `<Portal hostName={name}>` content
|
|
27
|
+
/// teleports into it. Safe to call before activation; the name is applied
|
|
28
|
+
/// when the host view is created. Pass nil to unregister.
|
|
29
|
+
- (void)setName:(nullable NSString *)name;
|
|
30
|
+
|
|
31
|
+
/// Creates the underlying Fabric host view, attaches the touch handler, and
|
|
32
|
+
/// applies any pending name. No-op when already activated. Must be called on
|
|
33
|
+
/// the main thread after the React runtime has booted.
|
|
34
|
+
- (void)activateIfNeeded;
|
|
35
|
+
|
|
36
|
+
/// Whether the underlying Fabric host view has been created.
|
|
37
|
+
@property (nonatomic, readonly) BOOL isActivated;
|
|
38
|
+
|
|
39
|
+
/// Whether any teleported content view is currently attached.
|
|
40
|
+
@property (nonatomic, readonly) BOOL hasAttachedContent;
|
|
41
|
+
|
|
42
|
+
/// Called on the main thread when the first content view attaches.
|
|
43
|
+
@property (nonatomic, copy, nullable) void (^onContentDidAttach)(void);
|
|
44
|
+
|
|
45
|
+
/// Called on the main thread when the last content view detaches. Transient
|
|
46
|
+
/// remove-and-reinsert churn within one mount commit is re-checked on the
|
|
47
|
+
/// next run loop before this fires.
|
|
48
|
+
@property (nonatomic, copy, nullable) void (^onContentDidDetach)(void);
|
|
49
|
+
|
|
50
|
+
/// Unregisters the host and clears lifecycle callbacks. Call when the owning
|
|
51
|
+
/// screen is torn down; the container stops receiving teleported content.
|
|
52
|
+
- (void)invalidate;
|
|
53
|
+
|
|
54
|
+
@end
|
|
55
|
+
|
|
56
|
+
NS_ASSUME_NONNULL_END
|