diff --git a/React/Base/RCTBridge.m b/React/Base/RCTBridge.m index a2ff0146b..46dcc1dfe 100644 --- a/React/Base/RCTBridge.m +++ b/React/Base/RCTBridge.m @@ -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 _latestJSExecutor; @@ -1111,25 +1074,28 @@ static id _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 _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 _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 _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 _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 _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 _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 _latestJSExecutor; - (void)_update:(CADisplayLink *)displayLink { - RCT_PROFILE_START(); + RCTProfileImmediateEvent(@"VSYNC", displayLink.timestamp, @"g"); + RCTProfileBeginEvent(); RCTFrameUpdate *frameUpdate = [[RCTFrameUpdate alloc] initWithDisplayLink:displayLink]; for (id observer in _frameUpdateObservers) { @@ -1316,7 +1284,7 @@ static id _latestJSExecutor; [self _runScheduledCalls]; - RCT_PROFILE_END(display_link, nil, @"main_thread"); + RCTProfileEndEvent(@"DispatchFrameUpdate", @"objc_call", nil); } - (void)_runScheduledCalls @@ -1382,23 +1350,12 @@ static id _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]; diff --git a/React/Base/RCTDevMenu.m b/React/Base/RCTDevMenu.m index f29201f9a..4af9d4e62 100644 --- a/React/Base/RCTDevMenu.m +++ b/React/Base/RCTDevMenu.m @@ -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]; diff --git a/React/Base/RCTLog.h b/React/Base/RCTLog.h index 7ffd86006..3a8a70d5b 100644 --- a/React/Base/RCTLog.h +++ b/React/Base/RCTLog.h @@ -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. diff --git a/React/Base/RCTLog.m b/React/Base/RCTLog.m index 4b9653650..449980fc3 100644 --- a/React/Base/RCTLog.m +++ b/React/Base/RCTLog.m @@ -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]; diff --git a/React/Base/RCTProfile.h b/React/Base/RCTProfile.h new file mode 100644 index 000000000..b3a1683d1 --- /dev/null +++ b/React/Base/RCTProfile.h @@ -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 + +/** + * 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 diff --git a/React/Base/RCTProfile.m b/React/Base/RCTProfile.m new file mode 100644 index 000000000..4ba99725b --- /dev/null +++ b/React/Base/RCTProfile.m @@ -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 + +#import + +#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(), + ); +} diff --git a/React/Executors/RCTContextExecutor.m b/React/Executors/RCTContextExecutor.m index 71e4f45c3..3de6182d1 100644 --- a/React/Executors/RCTContextExecutor.m +++ b/React/Executors/RCTContextExecutor.m @@ -15,6 +15,7 @@ #import "RCTAssert.h" #import "RCTLog.h" +#import "RCTProfile.h" #import "RCTUtils.h" @interface RCTJavaScriptContext : NSObject @@ -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 diff --git a/React/Modules/RCTUIManager.m b/React/Modules/RCTUIManager.m index 2830ce6b0..e2dc8d560 100644 --- a/React/Modules/RCTUIManager.m +++ b/React/Modules/RCTUIManager.m @@ -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), + }); }); } diff --git a/React/React.xcodeproj/project.pbxproj b/React/React.xcodeproj/project.pbxproj index 294bf4145..48d7aee04 100644 --- a/React/React.xcodeproj/project.pbxproj +++ b/React/React.xcodeproj/project.pbxproj @@ -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 = ""; }; 14F484541AABFCE100FDF6B9 /* RCTSliderManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTSliderManager.h; sourceTree = ""; }; 14F484551AABFCE100FDF6B9 /* RCTSliderManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTSliderManager.m; sourceTree = ""; }; + 14F4D3891AE1B7E40049C042 /* RCTProfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTProfile.h; sourceTree = ""; }; + 14F4D38A1AE1B7E40049C042 /* RCTProfile.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTProfile.m; sourceTree = ""; }; 58114A121AAE854800E7D092 /* RCTPicker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTPicker.h; sourceTree = ""; }; 58114A131AAE854800E7D092 /* RCTPicker.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTPicker.m; sourceTree = ""; }; 58114A141AAE854800E7D092 /* RCTPickerManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTPickerManager.h; sourceTree = ""; }; @@ -393,6 +396,8 @@ 83CBBA4F1A601E3B00E9B192 /* RCTUtils.h */, 83CBBA501A601E3B00E9B192 /* RCTUtils.m */, 1436DD071ADE7AA000A5ED7D /* RCTFrameUpdate.h */, + 14F4D3891AE1B7E40049C042 /* RCTProfile.h */, + 14F4D38A1AE1B7E40049C042 /* RCTProfile.m */, ); path = Base; sourceTree = ""; @@ -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 */,