[ReactNative] Better profiling API + Fix overlaping events

This commit is contained in:
Tadeu Zagallo 2015-04-20 04:55:05 -07:00
parent fde476f4e5
commit fb1fa12e89
9 changed files with 328 additions and 98 deletions

View File

@ -22,6 +22,7 @@
#import "RCTJavaScriptLoader.h"
#import "RCTKeyCommands.h"
#import "RCTLog.h"
#import "RCTProfile.h"
#import "RCTRedBox.h"
#import "RCTRootView.h"
#import "RCTSparseArray.h"
@ -48,39 +49,6 @@ typedef NS_ENUM(NSUInteger, RCTBridgeFields) {
*/
#define BATCHED_BRIDGE 1
#ifdef DEBUG
#define RCT_PROFILE_START() \
_Pragma("clang diagnostic push") \
_Pragma("clang diagnostic ignored \"-Wshadow\"") \
NSTimeInterval __rct_profile_start = CACurrentMediaTime() \
_Pragma("clang diagnostic pop")
#define RCT_PROFILE_END(cat, args, profileName...) \
do { \
if (_profile) { \
[_profileLock lock]; \
[_profile addObject:@{ \
@"name": [@[profileName] componentsJoinedByString: @"_"], \
@"cat": @ #cat, \
@"ts": @((NSUInteger)((__rct_profile_start - _startingTime) * 1e6)), \
@"dur": @((NSUInteger)((CACurrentMediaTime() - __rct_profile_start) * 1e6)), \
@"ph": @"X", \
@"pid": @([[NSProcessInfo processInfo] processIdentifier]), \
@"tid": [[NSThread currentThread] description], \
@"args": args ?: [NSNull null], \
}]; \
[_profileLock unlock]; \
} \
} while(0)
#else
#define RCT_PROFILE_START(...)
#define RCT_PROFILE_END(...)
#endif
#ifdef __LP64__
typedef uint64_t RCTHeaderValue;
typedef struct section_64 RCTHeaderSection;
@ -230,8 +198,6 @@ static NSArray *RCTBridgeModuleClassesByModuleID(void)
@interface RCTBridge ()
@property (nonatomic, copy, readonly) NSArray *profile;
- (void)_invokeAndProcessModule:(NSString *)module
method:(NSString *)method
arguments:(NSArray *)args
@ -250,6 +216,7 @@ static NSArray *RCTBridgeModuleClassesByModuleID(void)
@property (nonatomic, copy, readonly) NSString *moduleClassName;
@property (nonatomic, copy, readonly) NSString *JSMethodName;
@property (nonatomic, assign, readonly) SEL selector;
@end
@ -805,10 +772,6 @@ static NSDictionary *RCTLocalModulesConfig()
NSMutableArray *_scheduledCalls;
RCTSparseArray *_scheduledCallbacks;
BOOL _loading;
NSUInteger _startingTime;
NSMutableArray *_profile;
NSLock *_profileLock;
}
static id<RCTJavaScriptExecutor> _latestJSExecutor;
@ -1111,25 +1074,28 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
- (void)enqueueApplicationScript:(NSString *)script url:(NSURL *)url onComplete:(RCTJavaScriptCompleteBlock)onComplete
{
RCTAssert(onComplete != nil, @"onComplete block passed in should be non-nil");
RCT_PROFILE_START();
NSNumber *context = RCTGetExecutorID(_javaScriptExecutor);
RCTProfileBeginEvent();
[_javaScriptExecutor executeApplicationScript:script sourceURL:url onComplete:^(NSError *scriptLoadError) {
RCT_PROFILE_END(js_call, scriptLoadError, @"initial_script");
RCTProfileEndEvent(@"ApplicationScript", @"js_call,init", scriptLoadError);
if (scriptLoadError) {
onComplete(scriptLoadError);
return;
}
RCT_PROFILE_START();
RCTProfileBeginEvent();
NSNumber *context = RCTGetExecutorID(_javaScriptExecutor);
[_javaScriptExecutor executeJSCall:@"BatchedBridge"
method:@"flushedQueue"
arguments:@[]
context:context
callback:^(id json, NSError *error) {
RCT_PROFILE_END(js_call, error, @"initial_call", @"BatchedBridge.flushedQueue");
RCT_PROFILE_START();
RCTProfileEndEvent(@"FetchApplicationScriptCallbacks", @"js_call,init", @{
@"json": json ?: [NSNull null],
@"error": error ?: [NSNull null],
});
[self _handleBuffer:json context:context];
RCT_PROFILE_END(objc_call, json, @"batched_js_calls");
onComplete(error);
}];
}];
@ -1140,7 +1106,7 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
- (void)_invokeAndProcessModule:(NSString *)module method:(NSString *)method arguments:(NSArray *)args context:(NSNumber *)context
{
#if BATCHED_BRIDGE
RCT_PROFILE_START();
RCTProfileBeginEvent();
if ([module isEqualToString:@"RCTEventEmitter"]) {
for (NSDictionary *call in _scheduledCalls) {
@ -1163,7 +1129,7 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
[_scheduledCalls addObject:call];
}
RCT_PROFILE_END(js_call, args, @"schedule", module, method);
RCTProfileEndEvent(@"enqueue_call", @"objc_call", call);
}
- (void)_actuallyInvokeAndProcessModule:(NSString *)module method:(NSString *)method arguments:(NSArray *)args context:(NSNumber *)context
@ -1171,15 +1137,9 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
#endif
[[NSNotificationCenter defaultCenter] postNotificationName:RCTEnqueueNotification object:nil userInfo:nil];
NSString *moduleDotMethod = [NSString stringWithFormat:@"%@.%@", module, method];
RCT_PROFILE_START();
RCTJavaScriptCallback processResponse = ^(id json, NSError *error) {
[[NSNotificationCenter defaultCenter] postNotificationName:RCTDequeueNotification object:nil userInfo:nil];
RCT_PROFILE_END(js_call, args, moduleDotMethod);
RCT_PROFILE_START();
[self _handleBuffer:json context:context];
RCT_PROFILE_END(objc_call, json, @"batched_js_calls");
};
[_javaScriptExecutor executeJSCall:module
@ -1271,9 +1231,17 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
}
RCTModuleMethod *method = methods[methodID];
// Look up module
id module = self->_modulesByID[moduleID];
if (!module) {
RCTLogError(@"No module found for name '%@'", RCTModuleNamesByID[moduleID]);
return NO;
}
__weak RCTBridge *weakSelf = self;
dispatch_queue_t queue = _queuesByID[moduleID];
dispatch_async(queue ?: dispatch_get_main_queue(), ^{
RCTProfileBeginEvent();
__strong RCTBridge *strongSelf = weakSelf;
if (!strongSelf.isValid) {
@ -1282,13 +1250,6 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
return;
}
// Look up module
id module = strongSelf->_modulesByID[moduleID];
if (!module) {
RCTLogError(@"No module found for name '%@'", RCTModuleNamesByID[moduleID]);
return;
}
@try {
[method invokeWithBridge:strongSelf module:module arguments:params context:context];
}
@ -1298,6 +1259,12 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
@throw;
}
}
RCTProfileEndEvent(@"Invoke callback", @"objc_call", @{
@"module": method.moduleClassName,
@"method": method.JSMethodName,
@"selector": NSStringFromSelector(method.selector),
});
});
return YES;
@ -1305,7 +1272,8 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
- (void)_update:(CADisplayLink *)displayLink
{
RCT_PROFILE_START();
RCTProfileImmediateEvent(@"VSYNC", displayLink.timestamp, @"g");
RCTProfileBeginEvent();
RCTFrameUpdate *frameUpdate = [[RCTFrameUpdate alloc] initWithDisplayLink:displayLink];
for (id<RCTFrameUpdateObserver> observer in _frameUpdateObservers) {
@ -1316,7 +1284,7 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
[self _runScheduledCalls];
RCT_PROFILE_END(display_link, nil, @"main_thread");
RCTProfileEndEvent(@"DispatchFrameUpdate", @"objc_call", nil);
}
- (void)_runScheduledCalls
@ -1382,23 +1350,12 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
RCTLogError(@"To run the profiler you must be running from the dev server");
return;
}
_profileLock = [[NSLock alloc] init];
_startingTime = CACurrentMediaTime();
[_profileLock lock];
_profile = [[NSMutableArray alloc] init];
[_profileLock unlock];
RCTProfileInit();
}
- (void)stopProfiling
{
[_profileLock lock];
NSArray *profile = _profile;
_profile = nil;
[_profileLock unlock];
_profileLock = nil;
NSString *log = RCTJSONStringify(profile, NULL);
NSString *log = RCTProfileEnd();
NSString *URLString = [NSString stringWithFormat:@"%@://%@:%@/profile", _bundleURL.scheme, _bundleURL.host, _bundleURL.port];
NSURL *URL = [NSURL URLWithString:URLString];
NSMutableURLRequest *URLRequest = [NSMutableURLRequest requestWithURL:URL];

View File

@ -11,14 +11,13 @@
#import "RCTBridge.h"
#import "RCTLog.h"
#import "RCTProfile.h"
#import "RCTRootView.h"
#import "RCTSourceCode.h"
#import "RCTUtils.h"
@interface RCTBridge (Profiling)
@property (nonatomic, copy, readonly) NSArray *profile;
- (void)startProfiling;
- (void)stopProfiling;
@ -94,7 +93,7 @@ RCT_EXPORT_MODULE()
NSString *debugTitleChrome = _bridge.executorClass && _bridge.executorClass == NSClassFromString(@"RCTWebSocketExecutor") ? @"Disable Chrome Debugging" : @"Enable Chrome Debugging";
NSString *debugTitleSafari = _bridge.executorClass && _bridge.executorClass == NSClassFromString(@"RCTWebViewExecutor") ? @"Disable Safari Debugging" : @"Enable Safari Debugging";
NSString *liveReloadTitle = _liveReloadEnabled ? @"Disable Live Reload" : @"Enable Live Reload";
NSString *profilingTitle = _bridge.profile ? @"Stop Profiling" : @"Start Profiling";
NSString *profilingTitle = RCTProfileIsProfiling() ? @"Stop Profiling" : @"Start Profiling";
UIActionSheet *actionSheet =
[[UIActionSheet alloc] initWithTitle:@"React Native: Development"
@ -148,7 +147,7 @@ RCT_EXPORT_MODULE()
}
_profilingEnabled = enabled;
if (_bridge.profile) {
if (RCTProfileIsProfiling()) {
[_bridge stopProfiling];
} else {
[_bridge startProfiling];

View File

@ -45,6 +45,11 @@ typedef void (^RCTLogFunction)(
NSString *message
);
/**
* Get a given thread's name (or the current queue, iff in debug mode)
*/
NSString *RCTThreadName(NSThread *);
/**
* A method to generate a string from a collection of log data. To omit any
* particular data from the log, just pass nil or zero for the argument.

View File

@ -98,6 +98,22 @@ void RCTPerformBlockWithLogPrefix(void (^block)(void), NSString *prefix)
[prefixStack removeLastObject];
}
NSString *RCTThreadName(NSThread *thread)
{
NSString *threadName = [thread isMainThread] ? @"main" : thread.name;
if (threadName.length == 0) {
#if DEBUG
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
threadName = @(dispatch_queue_get_label(dispatch_get_current_queue()));
#pragma clang diagnostic pop
#else
threadName = [NSString stringWithFormat:@"%p", thread];
#endif
}
return threadName;
}
NSString *RCTFormatLog(
NSDate *timestamp,
NSThread *thread,
@ -121,18 +137,7 @@ NSString *RCTFormatLog(
[log appendFormat:@"[%s]", RCTLogLevels[level - 1]];
}
if (thread) {
NSString *threadName = [thread isMainThread] ? @"main" : thread.name;
if (threadName.length == 0) {
#if DEBUG
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
threadName = @(dispatch_queue_get_label(dispatch_get_current_queue()));
#pragma clang diagnostic pop
#else
threadName = [NSString stringWithFormat:@"%p", thread];
#endif
}
[log appendFormat:@"[tid:%@]", threadName];
[log appendFormat:@"[tid:%@]", RCTThreadName(thread)];
}
if (fileName) {
fileName = [fileName lastPathComponent];

103
React/Base/RCTProfile.h Normal file
View File

@ -0,0 +1,103 @@
/**
* 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>
/**
* RCTProfile
*
* This file provides a set of functions and macros for performance profiling
*
* NOTE: This API is a work in a work in progress, please consider it before
* before using.
*/
#if DEBUG
/**
* Returns YES if the profiling information is currently being collected
*/
BOOL RCTProfileIsProfiling(void);
/**
* Start collecting profiling information
*/
void RCTProfileInit(void);
/**
* Stop profiling and return a JSON string of the collected data - The data
* returned is compliant with google's trace event format - the format used
* as input to trace-viewer
*/
NSString *RCTProfileEnd(void);
/**
* Collects the initial event information for the event and returns a reference ID
*/
NSNumber *_RCTProfileBeginEvent(void);
/**
* The ID returned by BeginEvent should then be passed into EndEvent, with the
* rest of the event information. Just at this point the event will actually be
* registered
*/
void _RCTProfileEndEvent(NSNumber *, NSString *, NSString *, id);
/**
* This pair of macros implicitly handle the event ID when beginning and ending
* an event, for both simplicity and performance reasons, this method is preferred
*
* NOTE: The EndEvent call has to be either, in the same scope of BeginEvent,
* or in a sub-scope, otherwise the ID stored by BeginEvent won't be accessible
* for EndEvent, in this case you may want to use the actual C functions.
*/
#define RCTProfileBeginEvent() \
_Pragma("clang diagnostic push") \
_Pragma("clang diagnostic ignored \"-Wshadow\"") \
NSNumber *__rct_profile_id = _RCTProfileBeginEvent(); \
_Pragma("clang diagnostic pop")
#define RCTProfileEndEvent(name, category, args...) \
_RCTProfileEndEvent(__rct_profile_id, name, category, args)
/**
* An event that doesn't have a duration (i.e. Notification, VSync, etc)
*/
void RCTProfileImmediateEvent(NSString *, NSTimeInterval , NSString *);
/**
* Helper to profile the duration of the execution of a block. This method uses
* self and _cmd to name this event for simplicity sake.
*
* NOTE: The block can't expect any argument
*/
#define RCTProfileBlock(block, category, arguments) \
^{ \
RCTProfileBeginEvent(); \
block(); \
RCTProfileEndEvent([NSString stringWithFormat:@"[%@ %@]", NSStringFromClass([self class]), NSStringFromSelector(_cmd)], category, arguments); \
}
#else
#define RCTProfileIsProfiling(...) NO
#define RCTProfileInit(...)
#define RCTProfileEnd(...) @""
#define _RCTProfileBeginEvent(...) @0
#define RCTProfileBeginEvent(...)
#define _RCTProfileEndEvent(...)
#define RCTProfileEndEvent(...)
#define RCTProfileImmediateEvent(...)
#define RCTProfileBlock(block, ...) block
#endif

149
React/Base/RCTProfile.m Normal file
View File

@ -0,0 +1,149 @@
/**
* 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 "RCTProfile.h"
#import <mach/mach.h>
#import <UIKit/UIKit.h>
#import "RCTLog.h"
#import "RCTUtils.h"
#pragma mark - Prototypes
NSNumber *RCTProfileTimestamp(NSTimeInterval);
NSString *RCTProfileMemory(vm_size_t);
NSDictionary *RCTProfileGetMemoryUsage(void);
#pragma mark - Constants
NSString const *RCTProfileTraceEvents = @"traceEvents";
NSString const *RCTProfileSamples = @"samples";
#pragma mark - Variables
NSDictionary *RCTProfileInfo;
NSUInteger RCTProfileEventID = 0;
NSMutableDictionary *RCTProfileOngoingEvents;
NSTimeInterval RCTProfileStartTime;
#pragma mark - Macros
#define RCTProfileAddEvent(type, props...) \
[RCTProfileInfo[type] addObject:@{ \
@"pid": @([[NSProcessInfo processInfo] processIdentifier]), \
@"tid": RCTThreadName([NSThread currentThread]), \
props \
}];
#define CHECK(...) \
if (!RCTProfileIsProfiling()) { \
return __VA_ARGS__; \
}
#pragma mark - Private Helpers
NSNumber *RCTProfileTimestamp(NSTimeInterval timestamp)
{
return @((timestamp - RCTProfileStartTime) * 1e6);
}
NSString *RCTProfileMemory(vm_size_t memory)
{
double mem = ((double)memory) / 1024 / 1024;
return [NSString stringWithFormat:@"%.2lfmb", mem];
}
NSDictionary *RCTProfileGetMemoryUsage(void)
{
CHECK(@{});
struct task_basic_info info;
mach_msg_type_number_t size = sizeof(info);
kern_return_t kerr = task_info(mach_task_self(),
TASK_BASIC_INFO,
(task_info_t)&info,
&size);
if( kerr == KERN_SUCCESS ) {
return @{
@"suspend_count": @(info.suspend_count),
@"virtual_size": RCTProfileMemory(info.virtual_size),
@"resident_size": RCTProfileMemory(info.resident_size),
};
} else {
return @{};
}
}
#pragma mark - Public Functions
BOOL RCTProfileIsProfiling(void)
{
return RCTProfileInfo != nil;
}
void RCTProfileInit(void)
{
RCTProfileStartTime = CACurrentMediaTime();
RCTProfileOngoingEvents = [[NSMutableDictionary alloc] init];
RCTProfileInfo = @{
RCTProfileTraceEvents: [[NSMutableArray alloc] init],
RCTProfileSamples: [[NSMutableArray alloc] init],
};
}
NSString *RCTProfileEnd(void)
{
NSString *log = RCTJSONStringify(RCTProfileInfo, NULL);
RCTProfileEventID = 0;
RCTProfileInfo = nil;
RCTProfileOngoingEvents = nil;
return log;
}
NSNumber *_RCTProfileBeginEvent(void)
{
CHECK(@0);
NSNumber *eventID = @(++RCTProfileEventID);
RCTProfileOngoingEvents[eventID] = RCTProfileTimestamp(CACurrentMediaTime());
return eventID;
}
void _RCTProfileEndEvent(NSNumber *eventID, NSString *name, NSString *categories, id args)
{
CHECK();
NSNumber *startTimestamp = RCTProfileOngoingEvents[eventID];
if (!startTimestamp) {
return;
}
NSNumber *endTimestamp = RCTProfileTimestamp(CACurrentMediaTime());
RCTProfileAddEvent(RCTProfileTraceEvents,
@"name": name,
@"cat": categories,
@"ph": @"X",
@"ts": startTimestamp,
@"dur": @(endTimestamp.doubleValue - startTimestamp.doubleValue),
@"args": args ?: @[],
);
[RCTProfileOngoingEvents removeObjectForKey:eventID];
}
void RCTProfileImmediateEvent(NSString *name, NSTimeInterval timestamp, NSString *scope)
{
CHECK();
RCTProfileAddEvent(RCTProfileTraceEvents,
@"name": name,
@"ts": RCTProfileTimestamp(timestamp),
@"scope": scope,
@"ph": @"i",
@"args": RCTProfileGetMemoryUsage(),
);
}

View File

@ -15,6 +15,7 @@
#import "RCTAssert.h"
#import "RCTLog.h"
#import "RCTProfile.h"
#import "RCTUtils.h"
@interface RCTJavaScriptContext : NSObject <RCTInvalidating>
@ -234,7 +235,7 @@ static NSError *RCTNSErrorFromJSError(JSContextRef context, JSValueRef jsError)
{
RCTAssert(onComplete != nil, @"onComplete block should not be nil");
__weak RCTContextExecutor *weakSelf = self;
[self executeBlockOnJavaScriptQueue:^{
[self executeBlockOnJavaScriptQueue:RCTProfileBlock((^{
RCTContextExecutor *strongSelf = weakSelf;
if (!strongSelf || !strongSelf.isValid || ![RCTGetExecutorID(strongSelf) isEqualToNumber:executorID]) {
return;
@ -275,7 +276,7 @@ static NSError *RCTNSErrorFromJSError(JSContextRef context, JSValueRef jsError)
}
onComplete(objcValue, nil);
}];
}), @"js_call", (@{@"module":name, @"method": method, @"args": arguments}))];
}
- (void)executeApplicationScript:(NSString *)script
@ -285,7 +286,7 @@ static NSError *RCTNSErrorFromJSError(JSContextRef context, JSValueRef jsError)
RCTAssert(sourceURL != nil, @"url should not be nil");
__weak RCTContextExecutor *weakSelf = self;
[self executeBlockOnJavaScriptQueue:^{
[self executeBlockOnJavaScriptQueue:RCTProfileBlock((^{
RCTContextExecutor *strongSelf = weakSelf;
if (!strongSelf || !strongSelf.isValid) {
return;
@ -304,7 +305,7 @@ static NSError *RCTNSErrorFromJSError(JSContextRef context, JSValueRef jsError)
}
onComplete(error);
}
}];
}), @"js_call", (@{ @"url": sourceURL }))];
}
- (void)executeBlockOnJavaScriptQueue:(dispatch_block_t)block
@ -327,7 +328,7 @@ static NSError *RCTNSErrorFromJSError(JSContextRef context, JSValueRef jsError)
#endif
__weak RCTContextExecutor *weakSelf = self;
[self executeBlockOnJavaScriptQueue:^{
[self executeBlockOnJavaScriptQueue:RCTProfileBlock((^{
RCTContextExecutor *strongSelf = weakSelf;
if (!strongSelf || !strongSelf.isValid) {
return;
@ -354,7 +355,7 @@ static NSError *RCTNSErrorFromJSError(JSContextRef context, JSValueRef jsError)
if (onComplete) {
onComplete(nil);
}
}];
}), @"js_call,json_call", (@{@"objectName": objectName}))];
}
@end

View File

@ -19,6 +19,7 @@
#import "RCTBridge.h"
#import "RCTConvert.h"
#import "RCTLog.h"
#import "RCTProfile.h"
#import "RCTRootView.h"
#import "RCTScrollableProtocol.h"
#import "RCTShadowView.h"
@ -888,9 +889,13 @@ RCT_EXPORT_METHOD(blur:(NSNumber *)reactTag)
// Execute the previously queued UI blocks
dispatch_async(dispatch_get_main_queue(), ^{
RCTProfileBeginEvent();
for (dispatch_block_t block in previousPendingUIBlocks) {
block();
}
RCTProfileEndEvent(@"UIManager flushUIBlocks", @"objc_call", @{
@"count": @(previousPendingUIBlocks.count),
});
});
}

View File

@ -47,6 +47,7 @@
14F3620D1AABD06A001CE568 /* RCTSwitch.m in Sources */ = {isa = PBXBuildFile; fileRef = 14F362081AABD06A001CE568 /* RCTSwitch.m */; };
14F3620E1AABD06A001CE568 /* RCTSwitchManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 14F3620A1AABD06A001CE568 /* RCTSwitchManager.m */; };
14F484561AABFCE100FDF6B9 /* RCTSliderManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 14F484551AABFCE100FDF6B9 /* RCTSliderManager.m */; };
14F4D38B1AE1B7E40049C042 /* RCTProfile.m in Sources */ = {isa = PBXBuildFile; fileRef = 14F4D38A1AE1B7E40049C042 /* RCTProfile.m */; };
58114A161AAE854800E7D092 /* RCTPicker.m in Sources */ = {isa = PBXBuildFile; fileRef = 58114A131AAE854800E7D092 /* RCTPicker.m */; };
58114A171AAE854800E7D092 /* RCTPickerManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 58114A151AAE854800E7D092 /* RCTPickerManager.m */; };
58114A501AAE93D500E7D092 /* RCTAsyncLocalStorage.m in Sources */ = {isa = PBXBuildFile; fileRef = 58114A4E1AAE93D500E7D092 /* RCTAsyncLocalStorage.m */; };
@ -165,6 +166,8 @@
14F3620A1AABD06A001CE568 /* RCTSwitchManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTSwitchManager.m; sourceTree = "<group>"; };
14F484541AABFCE100FDF6B9 /* RCTSliderManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTSliderManager.h; sourceTree = "<group>"; };
14F484551AABFCE100FDF6B9 /* RCTSliderManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTSliderManager.m; sourceTree = "<group>"; };
14F4D3891AE1B7E40049C042 /* RCTProfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTProfile.h; sourceTree = "<group>"; };
14F4D38A1AE1B7E40049C042 /* RCTProfile.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTProfile.m; sourceTree = "<group>"; };
58114A121AAE854800E7D092 /* RCTPicker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTPicker.h; sourceTree = "<group>"; };
58114A131AAE854800E7D092 /* RCTPicker.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTPicker.m; sourceTree = "<group>"; };
58114A141AAE854800E7D092 /* RCTPickerManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTPickerManager.h; sourceTree = "<group>"; };
@ -393,6 +396,8 @@
83CBBA4F1A601E3B00E9B192 /* RCTUtils.h */,
83CBBA501A601E3B00E9B192 /* RCTUtils.m */,
1436DD071ADE7AA000A5ED7D /* RCTFrameUpdate.h */,
14F4D3891AE1B7E40049C042 /* RCTProfile.h */,
14F4D38A1AE1B7E40049C042 /* RCTProfile.m */,
);
path = Base;
sourceTree = "<group>";
@ -483,6 +488,7 @@
83CBBA511A601E3B00E9B192 /* RCTAssert.m in Sources */,
58114A501AAE93D500E7D092 /* RCTAsyncLocalStorage.m in Sources */,
832348161A77A5AA00B55238 /* Layout.c in Sources */,
14F4D38B1AE1B7E40049C042 /* RCTProfile.m in Sources */,
14F3620D1AABD06A001CE568 /* RCTSwitch.m in Sources */,
14F3620E1AABD06A001CE568 /* RCTSwitchManager.m in Sources */,
13B080201A69489C00A75B9A /* RCTUIActivityIndicatorViewManager.m in Sources */,