Refactor hot loading implementation on iOS

Reviewed By: milend

Differential Revision: D2795580

fb-gh-sync-id: ad33ba152e40b622b10bfa0122afd6edc28a11bf
This commit is contained in:
Nick Lockwood 2016-01-04 10:39:07 -08:00 committed by facebook-github-bot-9
parent 54f2586735
commit ed4478a4ff
15 changed files with 193 additions and 145 deletions

View File

@ -26,6 +26,7 @@
13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB11A68108700A75B9A /* LaunchScreen.xib */; };
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
13B6C1A31C34225900D3FAF5 /* RCTURLUtilsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B6C1A21C34225900D3FAF5 /* RCTURLUtilsTests.m */; };
13DB03481B5D2ED500C27245 /* RCTJSONTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 13DB03471B5D2ED500C27245 /* RCTJSONTests.m */; };
13DF61B61B67A45000EDB188 /* RCTMethodArgumentTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 13DF61B51B67A45000EDB188 /* RCTMethodArgumentTests.m */; };
143BC5A11B21E45C00462512 /* UIExplorerSnapshotTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 143BC5A01B21E45C00462512 /* UIExplorerSnapshotTests.m */; };
@ -198,6 +199,7 @@
13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = UIExplorer/Images.xcassets; sourceTree = "<group>"; };
13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = UIExplorer/Info.plist; sourceTree = "<group>"; };
13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = UIExplorer/main.m; sourceTree = "<group>"; };
13B6C1A21C34225900D3FAF5 /* RCTURLUtilsTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTURLUtilsTests.m; sourceTree = "<group>"; };
13CC9D481AEED2B90020D1C2 /* RCTSettings.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTSettings.xcodeproj; path = ../../Libraries/Settings/RCTSettings.xcodeproj; sourceTree = "<group>"; };
13DB03471B5D2ED500C27245 /* RCTJSONTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTJSONTests.m; sourceTree = "<group>"; };
13DF61B51B67A45000EDB188 /* RCTMethodArgumentTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTMethodArgumentTests.m; sourceTree = "<group>"; };
@ -406,6 +408,7 @@
143BC57C1B21E18100462512 /* UIExplorerUnitTests */ = {
isa = PBXGroup;
children = (
13B6C1A21C34225900D3FAF5 /* RCTURLUtilsTests.m */,
1497CFA41B21F5E400C1F8F2 /* RCTAllocationTests.m */,
1497CFA51B21F5E400C1F8F2 /* RCTBridgeTests.m */,
1497CFA61B21F5E400C1F8F2 /* RCTJSCExecutorTests.m */,
@ -886,6 +889,7 @@
1497CFAC1B21F5E400C1F8F2 /* RCTAllocationTests.m in Sources */,
13DF61B61B67A45000EDB188 /* RCTMethodArgumentTests.m in Sources */,
138D6A181B53CD440074A87E /* RCTShadowViewTests.m in Sources */,
13B6C1A31C34225900D3FAF5 /* RCTURLUtilsTests.m in Sources */,
8385CF041B87479200C6273E /* RCTImageLoaderHelpers.m in Sources */,
8385CEF51B873B5C00C6273E /* RCTImageLoaderTests.m in Sources */,
);

View File

@ -0,0 +1,75 @@
/**
* The examples provided by Facebook are for non-commercial testing and
* evaluation purposes only.
*
* Facebook reserves all rights not expressly granted.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#import <XCTest/XCTest.h>
#import "RCTUtils.h"
@interface RCTURLUtilsTests : XCTestCase
@end
@implementation RCTURLUtilsTests
- (void)testGetQueryParam
{
NSURL *URL = [NSURL URLWithString:@"http://example.com?foo=bar&bar=foo"];
NSString *foo = RCTGetURLQueryParam(URL, @"foo");
NSString *bar = RCTGetURLQueryParam(URL, @"bar");
XCTAssertEqualObjects(foo, @"bar");
XCTAssertEqualObjects(bar, @"foo");
}
- (void)testQueryParamNotFound
{
NSURL *URL = [NSURL URLWithString:@"http://example.com?foo=bar"];
NSString *bar = RCTGetURLQueryParam(URL, @"bar");
XCTAssertNil(bar);
}
- (void)testDuplicateParamTakesLatter
{
NSURL *URL = [NSURL URLWithString:@"http://example.com?foo=bar&foo=foo"];
NSString *foo = RCTGetURLQueryParam(URL, @"foo");
XCTAssertEqualObjects(foo, @"foo");
}
- (void)testNilURLGetQueryParam
{
NSURL *URL = nil;
NSString *foo = RCTGetURLQueryParam(URL, @"foo");
XCTAssertNil(foo);
}
- (void)testReplaceParam
{
NSURL *URL = [NSURL URLWithString:@"http://example.com?foo=bar&bar=foo"];
NSURL *result = RCTURLByReplacingQueryParam(URL, @"foo", @"foo");
XCTAssertEqualObjects(result.absoluteString, @"http://example.com?foo=foo&bar=foo");
}
- (void)testAppendParam
{
NSURL *URL = [NSURL URLWithString:@"http://example.com?bar=foo"];
NSURL *result = RCTURLByReplacingQueryParam(URL, @"foo", @"bar");
XCTAssertEqualObjects(result.absoluteString, @"http://example.com?bar=foo&foo=bar");
}
- (void)testNilURLAppendQueryParam
{
NSURL *URL = nil;
NSURL *result = RCTURLByReplacingQueryParam(URL, @"foo", @"bar");
XCTAssertNil(result);
}
@end

View File

@ -440,6 +440,16 @@ RCT_EXTERN NSArray<Class> *RCTGetModuleClasses(void);
object:_parentBridge userInfo:@{@"bridge": self}];
});
}];
#if RCT_DEV
if (RCTGetURLQueryParam(self.bundleURL, @"hot")) {
NSString *path = [self.bundleURL.path substringFromIndex:1]; // strip initial slash
[self enqueueJSCall:@"HMRClient.enable" args:@[@"ios", path]];
}
#endif
}
- (void)didFinishLoading

View File

@ -41,6 +41,11 @@
*/
@property (nonatomic, copy, readonly) RCTBridgeModuleProviderBlock moduleProvider;
/**
* Used by RCTDevMenu to override the `hot` param of the current bundleURL.
*/
@property (nonatomic, strong, readwrite) NSURL *bundleURL;
@end
@interface RCTBridge (RCTBatchedBridge)

View File

@ -123,11 +123,11 @@ RCT_EXTERN BOOL RCTBridgeModuleClassIsRegistered(Class);
/**
* URL of the script that was loaded into the bridge.
*/
@property (nonatomic, strong) NSURL *bundleURL;
@property (nonatomic, strong, readonly) NSURL *bundleURL;
/**
* The class of the executor currently being used *or* to be used after the next
* reload.
* The class of the executor currently being used. Changes to this value will
* take effect after the bridge is reloaded.
*/
@property (nonatomic, strong) Class executorClass;

View File

@ -17,7 +17,6 @@
#import "RCTLog.h"
#import "RCTPerformanceLogger.h"
#import "RCTUtils.h"
#import "RCTBundleURLProcessor.h"
NSString *const RCTReloadNotification = @"RCTReloadNotification";
NSString *const RCTJavaScriptWillStartLoadingNotification = @"RCTJavaScriptWillStartLoadingNotification";
@ -88,6 +87,9 @@ BOOL RCTBridgeModuleClassIsRegistered(Class cls)
}
@implementation RCTBridge
{
NSURL *_delegateBundleURL;
}
dispatch_queue_t RCTJSThread;
@ -257,8 +259,12 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
{
RCTAssertMainThread();
_bundleURL = [self.delegate sourceURLForBridge:self] ?: _bundleURL;
_bundleURL = [[RCTBundleURLProcessor sharedProcessor] process: _bundleURL];
// Only update bundleURL from delegate if delegate bundleURL has changed
NSURL *previousDelegateURL = _delegateBundleURL;
_delegateBundleURL = [self.delegate sourceURLForBridge:self];
if (_delegateBundleURL && ![_delegateBundleURL isEqual:previousDelegateURL]) {
_bundleURL = _delegateBundleURL;
}
// Sanitize the bundle URL
_bundleURL = [RCTConvert NSURL:_bundleURL.absoluteString];

View File

@ -48,9 +48,9 @@ typedef void (^RCTSourceLoadBlock)(NSError *error, NSData *source);
withBlock:(RCTSourceLoadBlock)loadCallback;
/**
* Indicates wheather Hot Loading is supported or not.
* Note this method will get removed soon, once we support Hot Loading on OSS.
* Indicates whether Hot Loading is supported or not.
* Note: this method will be removed soon, once Hot Loading is supported on OSS.
*/
- (BOOL)isHotLoadingEnabled;
- (BOOL)bridgeSupportsHotLoading:(RCTBridge *)bridge;
@end

View File

@ -1,18 +0,0 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
@interface RCTBundleURLProcessor : NSObject
+ (id)sharedProcessor;
- (NSString *)getQueryStringValue:(NSString *)attribute;
- (void)setQueryStringValue:(NSString *)value forAttribute:(NSString *)attribute;
- (NSURL *)process:(NSURL *)url;
@end

View File

@ -1,73 +0,0 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import <Foundation/Foundation.h>
#import "RCTBundleURLProcessor.h"
@implementation RCTBundleURLProcessor
NSDictionary *_qsAttributes;
+ (id)sharedProcessor
{
static RCTBundleURLProcessor *sharedProcessor = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedProcessor = [self new];
});
return sharedProcessor;
}
- (instancetype)init
{
// dictionary with additional query string attributes that will get appended
// to the bundle URL
_qsAttributes = [NSMutableDictionary new];
return self;
}
- (NSString *)getQueryStringValue:(NSString *)attribute
{
return [_qsAttributes valueForKey:attribute];
}
- (void)setQueryStringValue:(NSString *)value forAttribute:(NSString *)attribute
{
[_qsAttributes setValue:value forKey:attribute];
}
- (NSURL *)process:(NSURL *)url
{
if (url.isFileURL || [_qsAttributes count] == 0) {
return url;
}
// append either `?` or `&` depending on whether there are query string
// attibutes or not.
NSString *urlString = url.absoluteString;
if ([urlString rangeOfString:@"?"].location == NSNotFound) {
urlString = [urlString stringByAppendingString:@"?"];
} else {
urlString = [urlString stringByAppendingString:@"&"];
}
// array with new query string attributes
NSMutableArray *parts = [NSMutableArray new];
for (id attribute in _qsAttributes) {
if ([urlString rangeOfString:[NSString stringWithFormat:@"%@=", attribute]].location != NSNotFound) {
[NSException raise:@"Cannot override attribute" format:@"Attribute %@ is already present in url: %@", attribute, url.absoluteString];
}
[parts addObject:[NSString stringWithFormat:@"%@=%@", attribute, _qsAttributes[attribute]]];
}
return [NSURL URLWithString:[NSString stringWithFormat:@"%@%@", urlString, [parts componentsJoinedByString:@"&"]]];
}
@end

View File

@ -105,3 +105,7 @@ RCT_EXTERN NSString *RCTColorToHexString(CGColorRef color);
// Get standard localized string (if it exists)
RCT_EXTERN NSString *RCTUIKitLocalizedString(NSString *string);
// URL manipulation
RCT_EXTERN NSString *RCTGetURLQueryParam(NSURL *URL, NSString *param);
RCT_EXTERN NSURL *RCTURLByReplacingQueryParam(NSURL *URL, NSString *param, NSString *value);

View File

@ -576,10 +576,53 @@ NSString *RCTColorToHexString(CGColorRef color)
}
}
// (https://github.com/0xced/XCDFormInputAccessoryView/blob/master/XCDFormInputAccessoryView/XCDFormInputAccessoryView.m#L10-L14)
RCT_EXTERN NSString *RCTUIKitLocalizedString(NSString *string)
NSString *RCTUIKitLocalizedString(NSString *string)
{
NSBundle *UIKitBundle = [NSBundle bundleForClass:[UIApplication class]];
return UIKitBundle ? [UIKitBundle localizedStringForKey:string value:string table:nil] : string;
}
NSString *RCTGetURLQueryParam(NSURL *URL, NSString *param)
{
RCTAssertParam(param);
if (!URL) {
return nil;
}
NSURLComponents *components = [NSURLComponents componentsWithURL:URL
resolvingAgainstBaseURL:YES];
for (NSURLQueryItem *item in components.queryItems.reverseObjectEnumerator) {
if ([item.name isEqualToString:param]) {
return item.value;
}
}
return nil;
}
NSURL *RCTURLByReplacingQueryParam(NSURL *URL, NSString *param, NSString *value)
{
RCTAssertParam(param);
if (!URL) {
return nil;
}
NSURLComponents *components = [NSURLComponents componentsWithURL:URL
resolvingAgainstBaseURL:YES];
__block NSInteger paramIndex = NSNotFound;
NSMutableArray *queryItems = [components.queryItems mutableCopy];
[queryItems enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:
^(NSURLQueryItem *item, NSUInteger i, BOOL *stop) {
if ([item.name isEqualToString:param]) {
paramIndex = i;
*stop = YES;
}
}];
NSURLQueryItem *newItem = [NSURLQueryItem queryItemWithName:param value:value];
if (paramIndex == NSNotFound) {
[queryItems addObject:newItem];
} else {
[queryItems replaceObjectAtIndex:paramIndex withObject:newItem];
}
components.queryItems = queryItems;
return components.URL;
}

View File

@ -23,10 +23,8 @@
#import "RCTPerformanceLogger.h"
#import "RCTUtils.h"
#import "RCTJSCProfiler.h"
#import "RCTBundleURLProcessor.h"
static NSString *const RCTJSCProfilerEnabledDefaultsKey = @"RCTJSCProfilerEnabled";
static NSString *const RCTHotLoadingEnabledDefaultsKey = @"RCTHotLoadingEnabled";
@interface RCTJavaScriptContext : NSObject <RCTInvalidating>
@ -146,26 +144,6 @@ static void RCTInstallJSCProfiler(RCTBridge *bridge, JSContextRef context)
}
}
static BOOL isHotLoadingEnabled()
{
NSString *enabledQS = [[RCTBundleURLProcessor sharedProcessor] getQueryStringValue:@"hot"];
return (enabledQS != nil && [enabledQS isEqualToString:@"true"]) ? YES : NO;
}
static void RCTInstallHotLoading(RCTBridge *bridge, RCTJSCExecutor *executor)
{
[bridge.devMenu addItem:[RCTDevMenuItem toggleItemWithKey:RCTHotLoadingEnabledDefaultsKey title:@"Enable Hot Loading" selectedTitle:@"Disable Hot Loading" handler:^(BOOL enabledOnCurrentBundle) {
[executor executeBlockOnJavaScriptQueue:^{
BOOL enabledOnConfig = isHotLoadingEnabled();
// reload bundle when user change Hot Loading setting
if (enabledOnConfig != enabledOnCurrentBundle) {
[[RCTBundleURLProcessor sharedProcessor] setQueryStringValue:enabledOnCurrentBundle ? @"true" : @"false" forAttribute:@"hot"];
[bridge reload];
}
}];
}]];
}
#endif
+ (void)runRunLoopThread
@ -320,10 +298,6 @@ static void RCTInstallHotLoading(RCTBridge *bridge, RCTJSCExecutor *executor)
RCTInstallJSCProfiler(_bridge, strongSelf->_context.ctx);
if ([self.bridge.delegate respondsToSelector:@selector(isHotLoadingEnabled)] && [self.bridge.delegate isHotLoadingEnabled]) {
RCTInstallHotLoading(_bridge, strongSelf);
}
for (NSString *event in @[RCTProfileDidStartProfiling, RCTProfileDidEndProfiling]) {
[[NSNotificationCenter defaultCenter] addObserver:strongSelf
selector:@selector(toggleProfilingFlag:)
@ -535,13 +509,6 @@ static void RCTInstallHotLoading(RCTBridge *bridge, RCTJSCExecutor *executor)
onComplete(error);
}
}), 0, @"js_call", (@{ @"url": sourceURL.absoluteString }))];
#if RCT_DEV
if (isHotLoadingEnabled()) {
// strip initial slash
[_bridge enqueueJSCall:@"HMRClient.enable" args:@[@"ios", [sourceURL.path substringFromIndex: 1]]];
}
#endif
}
- (void)executeBlockOnJavaScriptQueue:(dispatch_block_t)block

View File

@ -36,6 +36,11 @@
*/
@property (nonatomic, assign) BOOL liveReloadEnabled;
/**
* Enables hot loading. Currently not supported in open source.
*/
@property (nonatomic, assign) BOOL hotLoadingEnabled;
/**
* Shows the FPS monitor for the JS and Main threads.
*/

View File

@ -267,6 +267,7 @@ RCT_EXPORT_MODULE()
self.shakeToShow = [_settings[@"shakeToShow"] ?: @YES boolValue];
self.profilingEnabled = [_settings[@"profilingEnabled"] ?: @NO boolValue];
self.liveReloadEnabled = [_settings[@"liveReloadEnabled"] ?: @NO boolValue];
self.hotLoadingEnabled = [_settings[@"hotLoadingEnabled"] ?: @YES boolValue];
self.showFPS = [_settings[@"showFPS"] ?: @NO boolValue];
self.executorClass = NSClassFromString(_executorOverride ?: _settings[@"executorClass"]);
}
@ -321,7 +322,7 @@ RCT_EXPORT_MODULE()
} else {
RCTLogWarn(@"RCTSourceCode module scriptURL has not been set");
}
} else if (!(sourceCodeModule.scriptURL).fileURL) {
} else if (!sourceCodeModule.scriptURL.fileURL) {
// Live reloading is disabled when running from bundled JS file
_liveReloadURL = [[NSURL alloc] initWithString:@"/onchange" relativeToURL:sourceCodeModule.scriptURL];
}
@ -420,6 +421,13 @@ RCT_EXPORT_MODULE()
}]];
}
if ([self hotLoadingAvailable]) {
NSString *hotLoadingTitle = _hotLoadingEnabled ? @"Disable Hot Loading" : @"Enable Hot Loading";
[items addObject:[RCTDevMenuItem buttonItemWithTitle:hotLoadingTitle handler:^{
weakSelf.hotLoadingEnabled = !_hotLoadingEnabled;
}]];
}
[items addObjectsFromArray:_extraMenuItems];
return items;
@ -483,8 +491,6 @@ RCT_EXPORT_METHOD(show)
RCT_EXPORT_METHOD(reload)
{
_jsLoaded = NO;
_liveReloadURL = nil;
[_bridge reload];
}
@ -523,6 +529,26 @@ RCT_EXPORT_METHOD(reload)
}
}
- (BOOL)hotLoadingAvailable
{
return !_bridge.bundleURL.fileURL // Only works when running from server
&& [_bridge.delegate respondsToSelector:@selector(bridgeSupportsHotLoading:)]
&& [_bridge.delegate bridgeSupportsHotLoading:_bridge];
}
- (void)setHotLoadingEnabled:(BOOL)enabled
{
_hotLoadingEnabled = enabled;
[self updateSetting:@"hotLoadingEnabled" value:@(_hotLoadingEnabled)];
BOOL actuallyEnabled = [self hotLoadingAvailable] && _hotLoadingEnabled;
if (RCTGetURLQueryParam(_bridge.bundleURL, @"hot").boolValue != actuallyEnabled) {
_bridge.bundleURL = RCTURLByReplacingQueryParam(_bridge.bundleURL, @"hot",
actuallyEnabled ? @"true" : @"false");
[_bridge reload];
}
}
- (void)setExecutorClass:(Class)executorClass
{
if (_executorClass != executorClass) {
@ -543,7 +569,7 @@ RCT_EXPORT_METHOD(reload)
}
_bridge.executorClass = executorClass;
[self reload];
[_bridge reload];
}
}

View File

@ -70,7 +70,6 @@
14F484561AABFCE100FDF6B9 /* RCTSliderManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 14F484551AABFCE100FDF6B9 /* RCTSliderManager.m */; };
14F7A0EC1BDA3B3C003C6C10 /* RCTPerfMonitor.m in Sources */ = {isa = PBXBuildFile; fileRef = 14F7A0EB1BDA3B3C003C6C10 /* RCTPerfMonitor.m */; };
14F7A0F01BDA714B003C6C10 /* RCTFPSGraph.m in Sources */ = {isa = PBXBuildFile; fileRef = 14F7A0EF1BDA714B003C6C10 /* RCTFPSGraph.m */; };
1BCBD4A71C32FA0B006FC476 /* RCTBundleURLProcessor.m in Sources */ = {isa = PBXBuildFile; fileRef = 1BCBD4A61C32FA0B006FC476 /* RCTBundleURLProcessor.m */; };
191E3EBE1C29D9AF00C180A6 /* RCTRefreshControlManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 191E3EBD1C29D9AF00C180A6 /* RCTRefreshControlManager.m */; };
191E3EC11C29DC3800C180A6 /* RCTRefreshControl.m in Sources */ = {isa = PBXBuildFile; fileRef = 191E3EC01C29DC3800C180A6 /* RCTRefreshControl.m */; };
58114A161AAE854800E7D092 /* RCTPicker.m in Sources */ = {isa = PBXBuildFile; fileRef = 58114A131AAE854800E7D092 /* RCTPicker.m */; };
@ -245,8 +244,6 @@
14F7A0EB1BDA3B3C003C6C10 /* RCTPerfMonitor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTPerfMonitor.m; sourceTree = "<group>"; };
14F7A0EE1BDA714B003C6C10 /* RCTFPSGraph.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTFPSGraph.h; sourceTree = "<group>"; };
14F7A0EF1BDA714B003C6C10 /* RCTFPSGraph.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTFPSGraph.m; sourceTree = "<group>"; };
1BCBD4A51C32FA0B006FC476 /* RCTBundleURLProcessor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTBundleURLProcessor.h; sourceTree = "<group>"; };
1BCBD4A61C32FA0B006FC476 /* RCTBundleURLProcessor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTBundleURLProcessor.m; sourceTree = "<group>"; };
191E3EBC1C29D9AF00C180A6 /* RCTRefreshControlManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTRefreshControlManager.h; sourceTree = "<group>"; };
191E3EBD1C29D9AF00C180A6 /* RCTRefreshControlManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTRefreshControlManager.m; sourceTree = "<group>"; };
191E3EBF1C29DC3800C180A6 /* RCTRefreshControl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTRefreshControl.h; sourceTree = "<group>"; };
@ -526,8 +523,6 @@
83CBBA651A601EF300E9B192 /* RCTEventDispatcher.h */,
83CBBA661A601EF300E9B192 /* RCTEventDispatcher.m */,
1436DD071ADE7AA000A5ED7D /* RCTFrameUpdate.h */,
1BCBD4A51C32FA0B006FC476 /* RCTBundleURLProcessor.h */,
1BCBD4A61C32FA0B006FC476 /* RCTBundleURLProcessor.m */,
14C2CA751B3AC64F00E6CBB2 /* RCTFrameUpdate.m */,
83CBBA4C1A601E3B00E9B192 /* RCTInvalidating.h */,
83CBBA631A601ECA00E9B192 /* RCTJavaScriptExecutor.h */,
@ -687,7 +682,6 @@
13E41EEB1C05CA0B00CD8DAC /* RCTProfileTrampoline-i386.S in Sources */,
13B080061A6947C200A75B9A /* RCTScrollViewManager.m in Sources */,
14200DAA1AC179B3008EE6BA /* RCTJavaScriptLoader.m in Sources */,
1BCBD4A71C32FA0B006FC476 /* RCTBundleURLProcessor.m in Sources */,
137327EA1AA5CF210034F82E /* RCTTabBarManager.m in Sources */,
13B080261A694A8400A75B9A /* RCTWrapperViewController.m in Sources */,
13B080051A6947C200A75B9A /* RCTScrollView.m in Sources */,