Merge branch '0.227' into universalBuild

This commit is contained in:
2023-10-18 10:19:18 +02:00
1013 changed files with 58711 additions and 20312 deletions

View File

@@ -463,7 +463,7 @@
);
INFOPLIST_FILE = "$(SRCROOT)/FlipperKit/Info.plist";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
OTHER_CPLUSPLUSFLAGS = (
"$(OTHER_CFLAGS)",
@@ -540,7 +540,7 @@
);
INFOPLIST_FILE = "$(SRCROOT)/FlipperKit/Info.plist";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
OTHER_CPLUSPLUSFLAGS = (
"$(OTHER_CFLAGS)",

View File

@@ -35,6 +35,10 @@
[self sendInternal:method withParams:params];
}
- (void)send:(NSString*)method withRawParams:(NSString*)params {
conn_->sendRaw([method UTF8String], [params UTF8String]);
}
- (void)send:(NSString*)method withArrayParams:(NSArray*)params {
[self sendInternal:method withParams:params];
}

View File

@@ -13,7 +13,7 @@
#import "FlipperStateUpdateListener.h"
/**
Represents a connection between the Sonar desktop och client side. Manages the
Represents a connection between the Flipper desktop client side. Manages the
lifecycle of attached plugin instances.
*/
@interface FlipperClient : NSObject
@@ -41,41 +41,48 @@ this client.
- (NSObject<FlipperPlugin>*)pluginWithIdentifier:(NSString*)identifier;
/**
Establish a connection to the Sonar desktop.
Establish a connection to the Flipper desktop.
*/
- (void)start;
/**
Stop the connection to the Sonar desktop.
Stop the connection to the Flipper desktop.
*/
- (void)stop;
/**
Get the log of state changes from the sonar client
Get the log of state changes from the Flipper client.
*/
- (NSString*)getState;
/**
Get the current summarized state of the sonar client
Get the current summarized state of the Flipper client.
*/
- (NSArray<NSDictionary*>*)getStateElements;
/**
Subscribe a ViewController to state update change notifications
Return true if the app is connected to Flipper desktop. Otherwise, returns
false.
*/
- (BOOL)isConnected;
/**
Subscribe a ViewController to state update change notifications.
*/
- (void)subscribeForUpdates:(id<FlipperStateUpdateListener>)controller;
/**
Sets the certificate provider responsible for obtaining certificates
Sets the certificate provider responsible for obtaining certificates.
*/
- (void)setCertificateProvider:(id<FlipperKitCertificateProvider>)provider;
/**
Get the certificate provider of Flipper Client
Get the certificate provider of Flipper Client.
*/
- (id<FlipperKitCertificateProvider>)getCertificateProvider;
// initializers are disabled. You must use `+[FlipperClient sharedClient]`
// Initializers are disabled. You must use `+[FlipperClient sharedClient]`
// instance.
- (instancetype)init NS_UNAVAILABLE;
+ (instancetype)new NS_UNAVAILABLE;

View File

@@ -182,6 +182,10 @@ using WrapperPlugin = facebook::flipper::FlipperCppWrapperPlugin;
#endif
}
- (BOOL)isConnected {
return _cppClient->isConnected();
}
- (NSString*)getState {
return @(_cppClient->getState().c_str());
}

View File

@@ -23,6 +23,11 @@ Invoke a method on the Sonar desktop plugin with with a matching identifier.
*/
- (void)send:(NSString*)method withParams:(NSDictionary*)params;
/**
Invoke a method on the Sonar desktop plugin with with a matching identifier.
*/
- (void)send:(NSString*)method withRawParams:(NSString*)params;
/**
Invoke a method on the Sonar desktop plugin with with a matching identifier.
*/

View File

@@ -37,6 +37,7 @@ static NSString* const kSKCellIdentifier =
cell.textLabel.font = [UIFont fontWithName:@"Arial" size:10];
cell.textLabel.text = [self.elements[row][@"state"]
stringByAppendingString:self.elements[row][@"name"]];
return cell;
}
@@ -52,12 +53,19 @@ static NSString* const kSKCellIdentifier =
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
self.stateTable = [[UITableView alloc]
initWithFrame:CGRectMake(
0, 0, self.view.bounds.size.width, STATE_VIEW_HEIGHT)];
self.scrollView = [[UIScrollView alloc]
initWithFrame:CGRectMake(
0,
STATE_VIEW_HEIGHT,
self.view.frame.size.width,
self.view.frame.size.height - 100 - STATE_VIEW_HEIGHT)];
self.logLabel =
[[UILabel alloc] initWithFrame:CGRectMake(
0,
@@ -66,11 +74,9 @@ static NSString* const kSKCellIdentifier =
self.scrollView.frame.size.height)];
self.logLabel.numberOfLines = 0;
self.logLabel.font = [UIFont systemFontOfSize:10.0f];
[self.scrollView addSubview:self.logLabel];
self.stateTable = [[UITableView alloc]
initWithFrame:CGRectMake(
0, 0, self.view.bounds.size.width, STATE_VIEW_HEIGHT)];
[self.stateTable registerClass:[UITableViewCell class]
forCellReuseIdentifier:kSKCellIdentifier];
self.stateTable.rowHeight = 14;
@@ -104,10 +110,11 @@ static NSString* const kSKCellIdentifier =
[self.logLabel sizeToFit];
self.scrollView.contentSize = self.logLabel.frame.size;
// Scroll to bottom
CGPoint bottomOffset = CGPointMake(
0,
self.scrollView.contentSize.height - self.scrollView.bounds.size.height);
CGFloat y = 0;
if (self.scrollView.contentSize.height > self.scrollView.bounds.size.height) {
y = self.scrollView.contentSize.height - self.scrollView.bounds.size.height;
}
CGPoint bottomOffset = CGPointMake(0, y);
[self.scrollView setContentOffset:bottomOffset animated:YES];
}

View File

@@ -166,25 +166,26 @@ static constexpr int connectionKeepaliveSeconds = 10;
}
- (void)disconnect {
[_dispatchQueue cancelAllOperations];
if ([_keepAlive isValid]) {
[_keepAlive invalidate];
}
_keepAlive = nil;
// Manually trigger a 'close' event as SocketRocket close method will
// not notify the delegate. SocketRocket only triggers the close event
// when the connection is closed from the server.
_eventHandler(facebook::flipper::SocketEvent::CLOSE);
__weak auto weakSelf = self;
NSBlockOperation* disconnectOperation =
[NSBlockOperation blockOperationWithBlock:^{
__strong auto strongSelf = weakSelf;
// Clear the socket delegate before close. Ensures that we won't get
// any messages after the disconnect takes place.
if (strongSelf->_socket) {
[strongSelf->_socket setDelegate:nil];
[strongSelf->_socket close];
if (_socket) {
// Clear the socket delegate before close. Ensures that we won't get
// any messages after the disconnect takes place.
_socket.delegate = nil;
[_socket close];
_socket = nil;
};
strongSelf->_socket = nil;
}
}];
[_dispatchQueue addOperations:@[ disconnectOperation ] waitUntilFinished:YES];
}
- (void)send:(NSString*)message
@@ -192,10 +193,12 @@ static constexpr int connectionKeepaliveSeconds = 10;
__weak auto weakSelf = self;
[_dispatchQueue addOperationWithBlock:^{
__strong auto strongSelf = weakSelf;
NSError* error = nil;
[strongSelf->_socket sendString:message error:&error];
if (completionHandler) {
completionHandler(error);
if (strongSelf) {
NSError* error = nil;
[strongSelf->_socket sendString:message error:&error];
if (completionHandler) {
completionHandler(error);
}
}
}];
}
@@ -210,7 +213,9 @@ static constexpr int connectionKeepaliveSeconds = 10;
__weak auto weakSelf = self;
[_dispatchQueue addOperationWithBlock:^{
__strong auto strongSelf = weakSelf;
[strongSelf->_socket sendPing:nil error:nil];
if (strongSelf) {
[strongSelf->_socket sendPing:nil error:nil];
}
}];
}
@@ -251,7 +256,6 @@ static constexpr int connectionKeepaliveSeconds = 10;
_keepAlive = nil;
_eventHandler(facebook::flipper::SocketEvent::CLOSE);
_socket = nil;
}
- (void)_webSocketDidReceiveMessage:(id)message {

View File

@@ -39,7 +39,7 @@ class FlipperWebSocket : public FlipperSocket {
virtual void setEventHandler(SocketEventHandler eventHandler) override;
virtual void setMessageHandler(SocketMessageHandler messageHandler) override;
virtual bool connect(FlipperConnectionManager* manager) override;
virtual void connect(FlipperConnectionManager* manager) override;
virtual void disconnect() override;
virtual void send(const folly::dynamic& message, SocketSendHandler completion)

View File

@@ -53,9 +53,9 @@ void FlipperWebSocket::setMessageHandler(SocketMessageHandler messageHandler) {
messageHandler_ = std::move(messageHandler);
}
bool FlipperWebSocket::connect(FlipperConnectionManager* manager) {
void FlipperWebSocket::connect(FlipperConnectionManager* manager) {
if (socket_ != NULL) {
return true;
return;
}
std::string connectionURL = endpoint_.secure ? "wss://" : "ws://";
@@ -72,43 +72,16 @@ bool FlipperWebSocket::connect(FlipperConnectionManager* manager) {
connectionURL += payload;
}
__block bool fullfilled = false;
__block std::promise<bool> promise;
auto connected = promise.get_future();
NSURL* urlObjc = [NSURL
URLWithString:[NSString stringWithUTF8String:connectionURL.c_str()]];
auto eventHandler = eventHandler_;
socket_ = [[FlipperPlatformWebSocket alloc] initWithURL:urlObjc];
socket_.eventHandler = ^(SocketEvent event) {
/**
Only fulfill the promise the first time the event handler is used. If the
open event is received, then set the promise value to true. For any other
event, consider a failure and set to false.
*/
if (!fullfilled) {
fullfilled = true;
if (event == SocketEvent::OPEN) {
promise.set_value(true);
} else if (event == SocketEvent::SSL_ERROR) {
try {
promise.set_exception(
std::make_exception_ptr(SSLException("SSL handshake failed")));
} catch (...) {
// set_exception() may throw an exception
// In that case, just set the value to false.
promise.set_value(false);
}
} else {
promise.set_value(false);
}
}
eventHandler(event);
eventHandler_(event);
};
auto messageHandler = messageHandler_;
socket_.messageHandler = ^(const std::string& message) {
messageHandler(message);
messageHandler_(message);
};
if (endpoint_.secure) {
@@ -119,19 +92,12 @@ bool FlipperWebSocket::connect(FlipperConnectionManager* manager) {
return std::string("");
}
strncpy(password, pkcs12.second.c_str(), length);
password[length - 1] = '\0';
return pkcs12.first;
};
}
[socket_ connect];
auto state = connected.wait_for(std::chrono::seconds(10));
if (state == std::future_status::ready) {
return connected.get();
}
disconnect();
return false;
}
void FlipperWebSocket::disconnect() {
@@ -155,6 +121,14 @@ void FlipperWebSocket::send(
if (socket_ == NULL) {
return;
}
// Ensure the payload size is valid before sending.
// The maximum allowed size for a message payload is 2^53 - 1. But that is
// for the entire message, including any additional metadata.
if (message.length() > pow(2, 53) - 1) {
throw std::length_error("Payload is too big to send");
}
NSString* messageObjc = [NSString stringWithUTF8String:message.c_str()];
[socket_ send:messageObjc
withCompletionHandler:^(NSError*) {
@@ -163,9 +137,9 @@ void FlipperWebSocket::send(
}
/**
Only ever used for insecure connections to receive the device_id from a
signCertificate request. If the intended usage ever changes, then a better
approach needs to be put in place.
* Only ever used for insecure connections to receive the device_id from a
* signCertificate request. If the intended usage ever changes, then a better
* approach needs to be put in place.
*/
void FlipperWebSocket::sendExpectResponse(
const std::string& message,

View File

@@ -12,7 +12,7 @@
@protocol FlipperConnection;
typedef void (^ConnectBlock)(id<FlipperConnection>);
typedef void (^DisconnectBlock)();
typedef void (^DisconnectBlock)(void);
@interface BlockBasedSonarPlugin : NSObject<FlipperPlugin>

View File

@@ -38,6 +38,10 @@
[self sendInternal:method withParams:params loggedTo:&_sent];
}
- (void)send:(NSString*)method withRawParams:(NSString*)params {
[self sendInternal:method withParams:params loggedTo:&_sent];
}
- (void)send:(NSString*)method withArrayParams:(NSArray*)params {
[self sendInternal:method withParams:params loggedTo:&_sentWithArray];
}

View File

@@ -0,0 +1,36 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <Foundation/Foundation.h>
#include <objc/NSObjCRuntime.h>
@interface DatabaseExecuteSqlResponse : NSObject
@property(nonatomic, strong) NSString* type;
@property(nonatomic, strong) NSArray* columns;
@property(nonatomic, strong) NSArray* values;
@property(nonatomic, strong) NSNumber* insertedId;
@property(nonatomic, assign) NSInteger affectedCount;
- (instancetype)initWithType:(NSString*)type
columns:(NSArray*)columns
values:(NSArray*)values
insertedId:(NSNumber*)insertedId
affectedCount:(NSInteger)affectedCount;
@end
@interface DatabaseExecuteSqlRequest : NSObject
@property(nonatomic, assign, readonly) NSInteger databaseId;
@property(nonatomic, copy, readonly) NSString* value;
- (instancetype)initWithDatabaseId:(NSInteger)databaseId value:(NSString*)value;
+ (DatabaseExecuteSqlRequest*)getExecuteSqlRequestFromDictionary:
(NSDictionary*)dictionary;
@end

View File

@@ -0,0 +1,54 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "DatabaseExecuteSql.h"
#include <objc/NSObjCRuntime.h>
@implementation DatabaseExecuteSqlResponse
- (instancetype)initWithType:(NSString*)type
columns:(NSArray*)columns
values:(NSArray*)values
insertedId:(NSNumber*)insertedId
affectedCount:(NSInteger)affectedCount {
self = [super init];
if (self) {
_type = type;
_columns = [columns copy];
_values = [values copy];
_insertedId = insertedId;
_affectedCount = affectedCount;
}
return self;
}
@end
@implementation DatabaseExecuteSqlRequest
- (instancetype)initWithDatabaseId:(NSInteger)databaseId
value:(NSString*)value {
self = [super init];
if (self) {
_databaseId = databaseId;
_value = [value copy];
}
return self;
}
+ (DatabaseExecuteSqlRequest*)getExecuteSqlRequestFromDictionary:
(NSDictionary*)dictionary {
NSInteger databaseId = [dictionary[@"databaseId"] integerValue];
NSString* value = dictionary[@"value"];
if (databaseId <= 0 || value.length == 0) {
return nil;
}
return [[DatabaseExecuteSqlRequest alloc] initWithDatabaseId:databaseId
value:value];
}
@end

View File

@@ -0,0 +1,45 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <Foundation/Foundation.h>
@interface DatabaseGetTableDataResponse : NSObject
@property(nonatomic, strong, readonly) NSArray<NSString*>* columns;
@property(nonatomic, strong, readonly) NSArray<NSArray*>* values;
@property(nonatomic, assign, readonly) NSInteger start;
@property(nonatomic, assign, readonly) NSInteger count;
@property(nonatomic, assign, readonly) NSInteger total;
- (instancetype)initWithColumns:(NSArray<NSString*>*)columns
values:(NSArray<NSArray*>*)values
start:(NSInteger)start
count:(NSInteger)count
total:(NSInteger)total;
@end
@interface DatabaseGetTableDataRequest : NSObject
@property(nonatomic, assign, readonly) NSInteger databaseId;
@property(nonatomic, copy, readonly) NSString* table;
@property(nonatomic, copy, readonly) NSString* order;
@property(nonatomic, assign, readonly) BOOL reverse;
@property(nonatomic, assign, readonly) NSInteger start;
@property(nonatomic, assign, readonly) NSInteger count;
- (instancetype)initWithDatabaseId:(NSInteger)databaseId
table:(NSString*)table
order:(NSString*)order
reverse:(BOOL)reverse
start:(NSInteger)start
count:(NSInteger)count;
+ (DatabaseGetTableDataRequest*)getTableDataRequestFromDictionary:
(NSDictionary*)dictionary;
@end

View File

@@ -0,0 +1,69 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "DatabaseGetTableData.h"
@implementation DatabaseGetTableDataResponse
- (instancetype)initWithColumns:(NSArray<NSString*>*)columns
values:(NSArray<NSArray*>*)values
start:(NSInteger)start
count:(NSInteger)count
total:(NSInteger)total {
self = [super init];
if (self) {
_columns = [columns copy];
_values = [values copy];
_start = start;
_count = count;
_total = total;
}
return self;
}
@end
@implementation DatabaseGetTableDataRequest
- (instancetype)initWithDatabaseId:(NSInteger)databaseId
table:(NSString*)table
order:(NSString*)order
reverse:(BOOL)reverse
start:(NSInteger)start
count:(NSInteger)count {
self = [super init];
if (self) {
_databaseId = databaseId;
_table = [table copy];
_order = [order copy];
_reverse = reverse;
_start = start;
_count = count;
}
return self;
}
+ (DatabaseGetTableDataRequest*)getTableDataRequestFromDictionary:
(NSDictionary*)dictionary {
NSInteger databaseId = [dictionary[@"databaseId"] integerValue];
NSString* table = dictionary[@"table"];
NSString* order = dictionary[@"order"];
BOOL reverse = [dictionary[@"reverse"] boolValue];
NSInteger start = [dictionary[@"start"] integerValue];
NSInteger count = [dictionary[@"count"] integerValue];
if (databaseId <= 0 || table.length == 0) {
return nil;
}
return [[DatabaseGetTableDataRequest alloc] initWithDatabaseId:databaseId
table:table
order:order
reverse:reverse
start:start
count:count];
}
@end

View File

@@ -0,0 +1,27 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <Foundation/Foundation.h>
@interface DatabaseGetTableInfoResponse : NSObject
@property(nonatomic, strong) NSString* definition;
- (instancetype)initWithDefinition:(NSString*)definition;
@end
@interface DatabaseGetTableInfoRequest : NSObject
@property(nonatomic, assign, readonly) NSInteger databaseId;
@property(nonatomic, copy, readonly) NSString* table;
- (instancetype)initWithDatabaseId:(NSInteger)databaseId table:(NSString*)table;
+ (DatabaseGetTableInfoRequest*)getTableInfoRequestFromDictionary:
(NSDictionary*)dictionary;
@end

View File

@@ -0,0 +1,46 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "DatabaseGetTableInfo.h"
@implementation DatabaseGetTableInfoResponse
- (instancetype)initWithDefinition:(NSString*)definition {
self = [super init];
if (self) {
_definition = definition;
}
return self;
}
@end
@implementation DatabaseGetTableInfoRequest
- (instancetype)initWithDatabaseId:(NSInteger)databaseId
table:(NSString*)table {
self = [super init];
if (self) {
_databaseId = databaseId;
_table = [table copy];
}
return self;
}
+ (DatabaseGetTableInfoRequest*)getTableInfoRequestFromDictionary:
(NSDictionary*)dictionary {
NSNumber* databaseId = @([dictionary[@"databaseId"] integerValue]);
NSString* table = dictionary[@"table"];
if (databaseId == nil || table == nil) {
return nil;
}
return [[DatabaseGetTableInfoRequest alloc]
initWithDatabaseId:databaseId.intValue
table:table];
}
@end

View File

@@ -0,0 +1,36 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <Foundation/Foundation.h>
@interface DatabaseGetTableStructureResponse : NSObject
@property(nonatomic, strong, readonly) NSArray<NSString*>* structureColumns;
@property(nonatomic, strong, readonly)
NSArray<NSArray<NSString*>*>* structureValues;
@property(nonatomic, strong, readonly) NSArray<NSString*>* indexesColumns;
@property(nonatomic, strong, readonly)
NSArray<NSArray<NSString*>*>* indexesValues;
- (instancetype)
initWithStructureColumns:(NSArray<NSString*>*)structureColumns
structureValues:(NSArray<NSArray<NSString*>*>*)structureValues
indexesColumns:(NSArray<NSString*>*)indexesColumns
indexesValues:(NSArray<NSArray<NSString*>*>*)indexesValues;
@end
@interface DatabaseGetTableStructureRequest : NSObject
@property(nonatomic, assign, readonly) NSInteger databaseId;
@property(nonatomic, copy, readonly) NSString* table;
- (instancetype)initWithDatabaseId:(NSInteger)databaseId table:(NSString*)table;
+ (DatabaseGetTableStructureRequest*)getTableStructureRequestFromDictionary:
(NSDictionary*)dictionary;
@end

View File

@@ -0,0 +1,52 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "DatabaseGetTableStructure.h"
@implementation DatabaseGetTableStructureResponse
- (instancetype)
initWithStructureColumns:(NSArray<NSString*>*)structureColumns
structureValues:(NSArray<NSArray<NSString*>*>*)structureValues
indexesColumns:(NSArray<NSString*>*)indexesColumns
indexesValues:(NSArray<NSArray<NSString*>*>*)indexesValues {
self = [super init];
if (self) {
_structureColumns = [structureColumns copy];
_structureValues = [structureValues copy];
_indexesColumns = [indexesColumns copy];
_indexesValues = [indexesValues copy];
}
return self;
}
@end
@implementation DatabaseGetTableStructureRequest
- (instancetype)initWithDatabaseId:(NSInteger)databaseId
table:(NSString*)table {
self = [super init];
if (self) {
_databaseId = databaseId;
_table = [table copy];
}
return self;
}
+ (DatabaseGetTableStructureRequest*)getTableStructureRequestFromDictionary:
(NSDictionary*)params {
int databaseId = [params[@"databaseId"] integerValue];
NSString* table = params[@"table"];
if (databaseId <= 0 || !table) {
return nil;
}
return [[DatabaseGetTableStructureRequest alloc] initWithDatabaseId:databaseId
table:table];
}
@end

View File

@@ -0,0 +1,12 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <Foundation/Foundation.h>
@protocol DatabaseDescriptor<NSObject>
- (NSString*)name;
@end

View File

@@ -0,0 +1,23 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <Foundation/Foundation.h>
#import "DatabaseDescriptor.h"
#import "DatabaseDriver.h"
@interface DatabaseDescriptorHolder : NSObject
@property(nonatomic, assign, readonly) NSInteger identifier;
@property(nonatomic, strong, readonly) id<DatabaseDriver> databaseDriver;
@property(nonatomic, strong, readonly) id<DatabaseDescriptor>
databaseDescriptor;
- (instancetype)initWithIdentifier:(NSInteger)identifier
databaseDriver:(id<DatabaseDriver>)databaseDriver
databaseDescriptor:(id<DatabaseDescriptor>)databaseDescriptor;
@end

View File

@@ -0,0 +1,24 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "DatabaseDescriptorHolder.h"
@implementation DatabaseDescriptorHolder
- (instancetype)initWithIdentifier:(NSInteger)identifier
databaseDriver:(id<DatabaseDriver>)databaseDriver
databaseDescriptor:(id<DatabaseDescriptor>)databaseDescriptor {
self = [super init];
if (self) {
_identifier = identifier;
_databaseDriver = databaseDriver;
_databaseDescriptor = databaseDescriptor;
}
return self;
}
@end

View File

@@ -0,0 +1,37 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <Foundation/Foundation.h>
@protocol DatabaseDescriptor;
@class DatabaseGetTableStructureResponse;
@class DatabaseGetTableInfoResponse;
@class DatabaseGetTableDataResponse;
@class DatabaseExecuteSqlResponse;
@protocol DatabaseDriver<NSObject>
- (NSArray<id<DatabaseDescriptor>>*)getDatabases;
- (NSArray<NSString*>*)getTableNames:(id<DatabaseDescriptor>)databaseDescriptor;
- (DatabaseGetTableStructureResponse*)
getTableStructureWithDatabaseDescriptor:
(id<DatabaseDescriptor>)databaseDescriptor
forTable:(NSString*)tableName;
- (DatabaseGetTableInfoResponse*)
getTableInfoWithDatabaseDescriptor:
(id<DatabaseDescriptor>)databaseDescriptor
forTable:(NSString*)tableName;
- (DatabaseGetTableDataResponse*)
getTableDataWithDatabaseDescriptor:
(id<DatabaseDescriptor>)databaseDescriptor
forTable:(NSString*)tableName
order:(NSString*)order
reverse:(BOOL)reverse
start:(NSInteger)start
count:(NSInteger)count;
- (DatabaseExecuteSqlResponse*)executeSQL:(NSString*)sql;
@end

View File

@@ -0,0 +1,21 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <Foundation/Foundation.h>
typedef NS_ENUM(NSInteger, DatabasesErrorCodes) {
DatabasesErrorCodesInvalidRequest = 1,
DatabasesErrorCodesDatabaseInvalid = 2,
DatabasesErrorCodesSqlExecutionException = 3,
};
static NSString* const kDatabasesErrorCodesInvalidRequestMessage =
@"The request received was invalid";
static NSString* const kDatabasesErrorCodesDatabaseInvalidMessage =
@"Could not access database";
static NSString* const kDatabasesErrorCodesSqlExecutionExceptionMessage =
@"SQL execution exception: ";

View File

@@ -0,0 +1,24 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <Foundation/Foundation.h>
@protocol DatabaseDriver;
@protocol DatabaseDescriptor;
@protocol FlipperConnection;
@interface DatabasesManager : NSObject
@property(nonatomic, strong) id<FlipperConnection> connection;
- (instancetype)init;
- (void)setConnection:(id<FlipperConnection>)connection;
- (BOOL)isConnected;
- (void)addDatabaseDriver:(id<DatabaseDriver>)driver;
- (void)removeDatabaseDriver:(id<DatabaseDriver>)driver;
@end

View File

@@ -0,0 +1,259 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "DatabasesManager.h"
#import <FlipperKit/FlipperClient.h>
#import <FlipperKit/FlipperConnection.h>
#import <FlipperKit/FlipperResponder.h>
#include <Foundation/Foundation.h>
#import "DatabaseDescriptor.h"
#import "DatabaseDescriptorHolder.h"
#import "DatabaseDriver.h"
#import "DatabaseErrorCodes.h"
#import "DatabaseExecuteSql.h"
#import "DatabaseGetTableData.h"
#import "DatabaseGetTableInfo.h"
#import "DatabaseGetTableStructure.h"
#import "ObjectMapper.h"
@interface DatabasesManager ()
@property(nonatomic, strong)
NSMutableDictionary<NSNumber*, DatabaseDescriptorHolder*>*
databaseDescriptorHolders;
@property(nonatomic, strong)
NSMutableSet<DatabaseDescriptorHolder*>* databaseDescriptorHolderSet;
@property(nonatomic, strong) NSMutableSet<id<DatabaseDriver>>* databaseDrivers;
@end
@implementation DatabasesManager
- (instancetype)init {
self = [super init];
if (self) {
_databaseDrivers = [NSMutableSet new];
_databaseDescriptorHolders = [NSMutableDictionary new];
_databaseDescriptorHolderSet = [NSMutableSet new];
}
return self;
}
- (void)setConnection:(id<FlipperConnection>)connection {
_connection = connection;
if (connection) {
[self listenForCommands];
}
}
- (BOOL)isConnected {
return _connection != nil;
}
- (void)listenForCommands {
[self.connection
receive:@"databaseList"
withBlock:^(NSDictionary* params, id<FlipperResponder> responder) {
NSInteger databaseId = 1;
[self.databaseDescriptorHolders removeAllObjects];
[self.databaseDescriptorHolderSet removeAllObjects];
for (id<DatabaseDriver> databaseDriver in self.databaseDrivers) {
NSArray<id<DatabaseDescriptor>>* databaseDescriptorList =
[databaseDriver getDatabases];
for (id<DatabaseDescriptor> databaseDescriptor in
databaseDescriptorList) {
DatabaseDescriptorHolder* databaseDescriptorHolder =
[[DatabaseDescriptorHolder alloc]
initWithIdentifier:databaseId
databaseDriver:databaseDriver
databaseDescriptor:databaseDescriptor];
self.databaseDescriptorHolders[@(databaseId)] =
databaseDescriptorHolder;
[self.databaseDescriptorHolderSet
addObject:databaseDescriptorHolder];
databaseId++;
}
}
id result = [ObjectMapper
databaseListToFlipperArray:self.databaseDescriptorHolderSet];
[responder success:result];
}];
[self.connection
receive:@"getTableData"
withBlock:^(NSDictionary* params, id<FlipperResponder> responder) {
DatabaseGetTableDataRequest* request = [DatabaseGetTableDataRequest
getTableDataRequestFromDictionary:params];
if (!request) {
[DatabasesManager raiseInvalidRequestError:responder];
return;
}
DatabaseDescriptorHolder* descriptorHolder =
self.databaseDescriptorHolders[@(request.databaseId)];
if (!descriptorHolder) {
[DatabasesManager raiseDatabaseInvalidError:responder];
return;
}
@try {
DatabaseGetTableDataResponse* tableDataResponse =
[descriptorHolder.databaseDriver
getTableDataWithDatabaseDescriptor:descriptorHolder
.databaseDescriptor
forTable:request.table
order:request.order
reverse:request.reverse
start:request.start
count:request.count];
NSDictionary* response = [ObjectMapper
databaseGetTableDataResponseToDictionary:tableDataResponse];
[responder success:response];
} @catch (NSException* exception) {
NSString* reason = exception.reason ?: @"Unknown error";
NSDictionary* errorResponse = [ObjectMapper
errorWithCode:DatabasesErrorCodesSqlExecutionException
message:[kDatabasesErrorCodesSqlExecutionExceptionMessage
stringByAppendingString:reason]];
[responder error:errorResponse];
}
}];
[self.connection
receive:@"getTableStructure"
withBlock:^(NSDictionary* params, id<FlipperResponder> responder) {
DatabaseGetTableStructureRequest* request =
[DatabaseGetTableStructureRequest
getTableStructureRequestFromDictionary:params];
if (!request) {
[DatabasesManager raiseInvalidRequestError:responder];
return;
}
DatabaseDescriptorHolder* descriptorHolder =
self.databaseDescriptorHolders[@(request.databaseId)];
if (!descriptorHolder) {
[DatabasesManager raiseDatabaseInvalidError:responder];
return;
}
@try {
DatabaseGetTableStructureResponse* tableStructure =
[descriptorHolder.databaseDriver
getTableStructureWithDatabaseDescriptor:
descriptorHolder.databaseDescriptor
forTable:request.table];
NSDictionary* response = [ObjectMapper
databaseGetTableStructureResponseToDictionary:tableStructure];
[responder success:response];
} @catch (NSException* exception) {
NSString* reason = exception.reason ?: @"Unknown error";
NSDictionary* errorResponse = [ObjectMapper
errorWithCode:DatabasesErrorCodesSqlExecutionException
message:[kDatabasesErrorCodesSqlExecutionExceptionMessage
stringByAppendingString:reason]];
[responder error:errorResponse];
}
}];
[self.connection
receive:@"getTableInfo"
withBlock:^(NSDictionary* params, id<FlipperResponder> responder) {
DatabaseGetTableInfoRequest* request = [DatabaseGetTableInfoRequest
getTableInfoRequestFromDictionary:params];
if (!request) {
[DatabasesManager raiseInvalidRequestError:responder];
return;
}
DatabaseDescriptorHolder* descriptorHolder =
self.databaseDescriptorHolders[@(request.databaseId)];
if (!descriptorHolder) {
[DatabasesManager raiseDatabaseInvalidError:responder];
return;
}
@try {
DatabaseGetTableInfoResponse* tableInfo =
[descriptorHolder.databaseDriver
getTableInfoWithDatabaseDescriptor:descriptorHolder
.databaseDescriptor
forTable:request.table];
NSDictionary* response =
[ObjectMapper databaseGetTableInfoResponseToDictionary:tableInfo];
[responder success:response];
} @catch (NSException* exception) {
NSString* reason = exception.reason ?: @"Unknown error";
NSDictionary* errorResponse = [ObjectMapper
errorWithCode:DatabasesErrorCodesSqlExecutionException
message:[kDatabasesErrorCodesSqlExecutionExceptionMessage
stringByAppendingString:reason]];
[responder error:errorResponse];
}
}];
[self.connection
receive:@"execute"
withBlock:^(NSDictionary* params, id<FlipperResponder> responder) {
DatabaseExecuteSqlRequest* request = [DatabaseExecuteSqlRequest
getExecuteSqlRequestFromDictionary:params];
if (!request) {
[DatabasesManager raiseInvalidRequestError:responder];
return;
}
DatabaseDescriptorHolder* descriptorHolder =
self.databaseDescriptorHolders[@(request.databaseId)];
if (!descriptorHolder) {
[DatabasesManager raiseDatabaseInvalidError:responder];
return;
}
@try {
DatabaseExecuteSqlResponse* sqlResponse =
[descriptorHolder.databaseDriver executeSQL:request.value];
NSDictionary* response =
[ObjectMapper databaseExecuteSqlResponseToDictionary:sqlResponse];
[responder success:response];
} @catch (NSException* exception) {
NSString* reason = exception.reason ?: @"Unknown error";
NSDictionary* errorResponse = [ObjectMapper
errorWithCode:DatabasesErrorCodesSqlExecutionException
message:[kDatabasesErrorCodesSqlExecutionExceptionMessage
stringByAppendingString:reason]];
[responder error:errorResponse];
}
}];
}
- (void)addDatabaseDriver:(id<DatabaseDriver>)driver {
if ([self.databaseDrivers containsObject:driver]) {
return;
}
[self.databaseDrivers addObject:driver];
}
- (void)removeDatabaseDriver:(id<DatabaseDriver>)driver {
if (![self.databaseDrivers containsObject:driver]) {
return;
}
[self.databaseDrivers removeObject:driver];
}
+ (void)raiseInvalidRequestError:(id<FlipperResponder>)responder {
NSDictionary* errorResponse =
[ObjectMapper errorWithCode:DatabasesErrorCodesInvalidRequest
message:kDatabasesErrorCodesInvalidRequestMessage];
[responder error:errorResponse];
}
+ (void)raiseDatabaseInvalidError:(id<FlipperResponder>)responder {
NSDictionary* errorResponse =
[ObjectMapper errorWithCode:DatabasesErrorCodesDatabaseInvalid
message:kDatabasesErrorCodesDatabaseInvalidMessage];
[responder error:errorResponse];
}
@end

View File

@@ -0,0 +1,26 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#if FB_SONARKIT_ENABLED
#import <FlipperKit/FlipperPlugin.h>
#import <Foundation/Foundation.h>
#import "DatabaseDriver.h"
@class DatabasesManager;
@interface FlipperKitDatabasesPlugin : NSObject<FlipperPlugin>
@property(nonatomic, strong) DatabasesManager* databasesManager;
- (instancetype)init NS_UNAVAILABLE;
+ (instancetype)sharedInstance;
- (void)addDatabaseDriver:(id<DatabaseDriver>)driver;
- (void)removeDatabaseDriver:(id<DatabaseDriver>)driver;
@end
#endif

View File

@@ -0,0 +1,69 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#if FB_SONARKIT_ENABLED
#import "FlipperKitDatabasesPlugin.h"
#import <FlipperKit/FlipperClient.h>
#import <FlipperKit/FlipperConnection.h>
#import <FlipperKit/FlipperResponder.h>
#import "DatabaseDriver.h"
#import "DatabasesManager.h"
#import "MockDatabaseDriver.h"
@interface FlipperKitDatabasesPlugin ()
@property(strong, nonatomic) id<FlipperConnection> connection;
@end
@implementation FlipperKitDatabasesPlugin
- (instancetype)init {
if (self = [super init]) {
_databasesManager = [DatabasesManager new];
}
return self;
}
+ (instancetype)sharedInstance {
static FlipperKitDatabasesPlugin* sInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sInstance = [FlipperKitDatabasesPlugin new];
});
return sInstance;
}
- (void)didConnect:(id<FlipperConnection>)connection {
self.connection = connection;
[self.databasesManager setConnection:connection];
}
- (void)didDisconnect {
[self.databasesManager setConnection:nil];
}
- (NSString*)identifier {
return @"Databases";
}
- (BOOL)runInBackground {
return NO;
}
- (void)addDatabaseDriver:(id<DatabaseDriver>)driver {
[self.databasesManager addDatabaseDriver:driver];
}
- (void)removeDatabaseDriver:(id<DatabaseDriver>)driver {
[self.databasesManager addDatabaseDriver:driver];
}
@end
#endif

View File

@@ -0,0 +1,13 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <Foundation/Foundation.h>
#import "DatabaseDescriptor.h"
@interface MockDatabaseDescriptor : NSObject<DatabaseDescriptor>
@end

View File

@@ -0,0 +1,16 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "MockDatabaseDescriptor.h"
@implementation MockDatabaseDescriptor
- (NSString*)name {
return @"MockDatabase";
}
@end

View File

@@ -0,0 +1,13 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <FlipperKitDatabasesPlugin/DatabaseDriver.h>
#import <Foundation/Foundation.h>
@interface MockDatabaseDriver : NSObject<DatabaseDriver>
@end

View File

@@ -0,0 +1,134 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "MockDatabaseDriver.h"
#include <Foundation/Foundation.h>
#include <objc/NSObjCRuntime.h>
#import "DatabaseExecuteSql.h"
#import "DatabaseGetTableData.h"
#import "DatabaseGetTableInfo.h"
#import "DatabaseGetTableStructure.h"
#import "MockDatabaseDescriptor.h"
@implementation MockDatabaseDriver
- (NSArray<id<DatabaseDescriptor>>*)getDatabases {
MockDatabaseDescriptor* mockDescriptor =
[[MockDatabaseDescriptor alloc] init];
return @[ mockDescriptor ];
}
- (NSArray<NSString*>*)getTableNames:
(id<DatabaseDescriptor>)databaseDescriptor {
return @[ @"MockTable1", @"MockTable2" ];
}
- (DatabaseGetTableStructureResponse*)
getTableStructureWithDatabaseDescriptor:
(id<DatabaseDescriptor>)databaseDescriptor
forTable:(NSString*)tableName {
NSMutableArray<NSString*>* structureColumns =
[NSMutableArray arrayWithObjects:@"id", @"name", @"age", nil];
NSMutableArray<NSArray<NSString*>*>* structureValues =
[NSMutableArray arrayWithObjects:@[ @"1", @"John", @"25" ],
@[ @"2", @"Jane", @"30" ],
nil];
NSMutableArray<NSString*>* indexesColumns = [NSMutableArray
arrayWithObjects:@"index_name", @"unique", @"indexed_column_name", nil];
NSMutableArray<NSArray<NSString*>*>* indexesValues = [NSMutableArray
arrayWithObjects:@[ @"index_name1", @"false", @"id,name" ],
@[ @"index_name2", @"true", @"age" ],
nil];
return [[DatabaseGetTableStructureResponse alloc]
initWithStructureColumns:[structureColumns copy]
structureValues:[structureValues copy]
indexesColumns:[indexesColumns copy]
indexesValues:[indexesValues copy]];
}
- (DatabaseGetTableInfoResponse*)
getTableInfoWithDatabaseDescriptor:
(id<DatabaseDescriptor>)databaseDescriptor
forTable:(NSString*)tableName {
return [[DatabaseGetTableInfoResponse alloc]
initWithDefinition:@"This is mocked table definition"];
}
- (DatabaseGetTableDataResponse*)
getTableDataWithDatabaseDescriptor:
(id<DatabaseDescriptor>)databaseDescriptor
forTable:(NSString*)tableName
order:(NSString*)order
reverse:(BOOL)reverse
start:(NSInteger)start
count:(NSInteger)count {
NSMutableArray* columns = [NSMutableArray array];
NSMutableArray* values = [NSMutableArray array];
NSUInteger numColums = 10;
NSUInteger numRows = 100;
for (int i = 0; i < numColums; i++) {
NSString* columnName = [NSString stringWithFormat:@"column%d", i + 1];
[columns addObject:columnName];
}
for (int i = 0; i < numRows; i++) {
NSMutableArray* valueRow = [NSMutableArray array];
for (int j = 0; j < numColums; j++) {
[valueRow addObject:[NSString stringWithFormat:@"value%d", j]];
}
[values addObject:valueRow];
}
return [[DatabaseGetTableDataResponse alloc] initWithColumns:[columns copy]
values:[values copy]
start:0
count:numRows
total:numRows];
}
- (DatabaseExecuteSqlResponse*)executeSQL:(NSString*)sql {
// Generate a mock response with a random type
NSString* type;
NSArray* columns = @[ @"id", @"name", @"age" ];
NSMutableArray* values = [NSMutableArray array];
NSUInteger numRows = 100;
for (int i = 0; i < numRows; i++) {
NSUInteger randomAge = arc4random_uniform(40);
[values addObject:@[
@(i),
[NSString stringWithFormat:@"Name %d", i],
@(randomAge)
]];
}
// Randomly select a type
NSArray<NSString*>* types = @[ @"select", @"insert", @"update_delete" ];
int index = arc4random_uniform((u_int32_t)types.count);
type = types[index];
// Set affectedCount and insertedId based on type
NSInteger affectedCount = 0;
NSNumber* insertedId = nil;
if ([type isEqualToString:@"insert"]) {
affectedCount = 1;
insertedId = @(15);
} else if ([type isEqualToString:@"update_delete"]) {
affectedCount = values.count;
}
DatabaseExecuteSqlResponse* response =
[[DatabaseExecuteSqlResponse alloc] initWithType:type
columns:columns
values:[values copy]
insertedId:insertedId
affectedCount:affectedCount];
return response;
}
@end

View File

@@ -0,0 +1,30 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <Foundation/Foundation.h>
@class DatabaseDescriptorHolder;
@class DatabaseExecuteSqlResponse;
@class DatabaseGetTableDataResponse;
@class DatabaseGetTableInfoResponse;
@class DatabaseGetTableStructureResponse;
@interface ObjectMapper : NSObject
+ (NSMutableArray*)databaseListToFlipperArray:
(NSMutableSet<DatabaseDescriptorHolder*>*)databaseDescriptorHolderSet;
+ (NSDictionary*)databaseGetTableDataResponseToDictionary:
(DatabaseGetTableDataResponse*)response;
+ (NSDictionary*)databaseGetTableStructureResponseToDictionary:
(DatabaseGetTableStructureResponse*)response;
+ (NSDictionary*)databaseGetTableInfoResponseToDictionary:
(DatabaseGetTableInfoResponse*)response;
+ (NSDictionary*)databaseExecuteSqlResponseToDictionary:
(DatabaseExecuteSqlResponse*)response;
+ (NSDictionary*)errorWithCode:(NSInteger)code message:(NSString*)message;
@end

View File

@@ -0,0 +1,216 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "ObjectMapper.h"
#include <Foundation/Foundation.h>
#import "DatabaseDescriptorHolder.h"
#import "DatabaseExecuteSql.h"
#import "DatabaseGetTableData.h"
#import "DatabaseGetTableInfo.h"
#import "DatabaseGetTableStructure.h"
@implementation ObjectMapper
static const int MAX_BLOB_LENGTH = 100 * 1024;
static NSString* const UNKNOWN_BLOB_LABEL_FORMAT = @"{%d-byte %@ blob}";
+ (NSMutableArray*)databaseListToFlipperArray:
(NSMutableSet<DatabaseDescriptorHolder*>*)databaseDescriptorHolderSet {
NSMutableArray* result = [NSMutableArray new];
for (DatabaseDescriptorHolder* holder in databaseDescriptorHolderSet) {
NSArray<NSString*>* tables =
[holder.databaseDriver getTableNames:holder.databaseDescriptor];
NSArray<NSString*>* sortedTableNames =
[tables sortedArrayUsingSelector:@selector(compare:)];
NSString* idString = [NSString stringWithFormat:@"%ld", holder.identifier];
NSDictionary* databaseInfo = @{
@"id" : idString,
@"name" : holder.databaseDescriptor.name,
@"tables" : sortedTableNames
};
[result addObject:databaseInfo];
}
return result;
}
+ (NSDictionary*)databaseGetTableDataResponseToDictionary:
(DatabaseGetTableDataResponse*)response {
NSMutableArray* rows = [NSMutableArray array];
for (NSArray* row in response.values) {
NSMutableArray* rowValues = [NSMutableArray array];
for (id item in row) {
[rowValues addObject:[self objectAndTypeToFlipperObject:item]];
}
[rows addObject:rowValues];
}
return @{
@"columns" : response.columns,
@"values" : rows,
@"start" : @(response.start),
@"count" : @(response.count),
@"total" : @(response.total)
};
}
+ (NSDictionary*)errorWithCode:(NSInteger)code message:(NSString*)message {
return @{@"code" : @(code), @"message" : message};
}
+ (NSDictionary*)databaseGetTableStructureResponseToDictionary:
(DatabaseGetTableStructureResponse*)response {
NSMutableArray* structureValues = [NSMutableArray array];
for (NSArray* row in response.structureValues) {
NSMutableArray* rowValues = [NSMutableArray array];
for (id item in row) {
[rowValues addObject:[self objectAndTypeToFlipperObject:item]];
}
[structureValues addObject:rowValues];
}
NSMutableArray* indexesValues = [NSMutableArray array];
for (NSArray* row in response.indexesValues) {
NSMutableArray* rowValues = [NSMutableArray array];
for (id item in row) {
[rowValues addObject:[self objectAndTypeToFlipperObject:item]];
}
[indexesValues addObject:rowValues];
}
return @{
@"structureColumns" : response.structureColumns,
@"structureValues" : structureValues,
@"indexesColumns" : response.indexesColumns,
@"indexesValues" : indexesValues
};
}
+ (NSDictionary*)databaseGetTableInfoResponseToDictionary:
(DatabaseGetTableInfoResponse*)response {
return @{
@"definition" : response.definition,
};
}
+ (NSDictionary*)databaseExecuteSqlResponseToDictionary:
(DatabaseExecuteSqlResponse*)response {
NSMutableArray* rows = [NSMutableArray array];
if (response.values) {
for (NSArray* row in response.values) {
NSMutableArray* rowValues = [NSMutableArray array];
for (id item in row) {
[rowValues addObject:[self objectAndTypeToFlipperObject:item]];
}
[rows addObject:rowValues];
}
}
NSMutableDictionary* result = [NSMutableDictionary dictionaryWithDictionary:@{
@"type" : response.type,
@"columns" : response.columns,
@"values" : rows,
@"affectedCount" : @(response.affectedCount)
}];
if (response.insertedId) {
result[@"insertedId"] = response.insertedId;
}
return result;
}
+ (NSDictionary*)objectAndTypeToFlipperObject:(id)object {
if (!object || [object isKindOfClass:[NSNull class]]) {
return @{@"type" : @"null"};
} else if ([object isKindOfClass:[NSNumber class]]) {
NSNumber* number = (NSNumber*)object;
NSString* type = [NSString stringWithUTF8String:[number objCType]];
if ([type isEqualToString:@"i"]) {
return @{@"type" : @"integer", @"value" : number};
} else if ([type isEqualToString:@"f"] || [type isEqualToString:@"d"]) {
return @{@"type" : @"float", @"value" : number};
} else if ([type isEqualToString:@"B"]) {
return @{@"type" : @"boolean", @"value" : number};
} else {
return @{@"type" : @"integer", @"value" : @([number integerValue])};
}
return @{@"type" : @"integer", @"value" : object};
} else if ([object isKindOfClass:[NSDecimalNumber class]]) {
return @{@"type" : @"float", @"value" : object};
} else if ([object isKindOfClass:[NSString class]]) {
return @{@"type" : @"string", @"value" : object};
} else if ([object isKindOfClass:[NSData class]]) {
NSString* blobString = [self blobToString:(NSData*)object];
return @{@"type" : @"blob", @"value" : blobString};
} else if ([object isKindOfClass:[NSDictionary class]]) {
// Usualy the dictionary is a Json blob, and we can parse it as string.
NSError* error;
NSData* jsonData = [NSJSONSerialization dataWithJSONObject:object
options:0
error:&error];
if (!jsonData) {
NSString* reason = [NSString
stringWithFormat:@"NSDictionary is not in a json format: %@",
[error localizedDescription]];
@throw [NSException exceptionWithName:@"InvalidArgumentException"
reason:reason
userInfo:nil];
}
NSString* jsonString = [[NSString alloc] initWithData:jsonData
encoding:NSUTF8StringEncoding];
return @{@"type" : @"blob", @"value" : jsonString};
} else if ([object isKindOfClass:[NSValue class]]) {
return @{@"type" : @"boolean", @"value" : object};
} else {
@throw [NSException exceptionWithName:@"InvalidArgumentException"
reason:@"type of Object is invalid"
userInfo:nil];
}
}
+ (NSString*)blobToString:(NSData*)data {
const uint8_t* bytes = data.bytes;
uint length = data.length;
if (length <= MAX_BLOB_LENGTH) {
if ([self fastIsAscii:bytes length:length]) {
NSStringEncoding encoding = NSASCIIStringEncoding;
return [[NSString alloc] initWithBytesNoCopy:(void*)bytes
length:length
encoding:encoding
freeWhenDone:NO];
} else {
// try UTF-8
NSStringEncoding encoding = NSUTF8StringEncoding;
return [[NSString alloc] initWithBytesNoCopy:(void*)bytes
length:length
encoding:encoding
freeWhenDone:NO];
}
}
return
[NSString stringWithFormat:UNKNOWN_BLOB_LABEL_FORMAT, length, @"binary"];
}
+ (BOOL)fastIsAscii:(const uint8_t*)bytes length:(NSUInteger)length {
for (int i = 0; i < length; i++) {
uint8_t b = bytes[i];
if ((b & ~0x7f) != 0) {
return NO;
}
}
return YES;
}
@end

View File

@@ -11,7 +11,7 @@
#import <FlipperKitLayoutTextSearchable/FKTextSearchable.h>
@implementation SKNodeDescriptor {
id<SKDescriptorMapperProtocol> _mapper;
__weak id<SKDescriptorMapperProtocol> _mapper;
}
- (void)setUp {

View File

@@ -1,37 +0,0 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <Foundation/Foundation.h>
#define APPLY_ENUM_TO_YOGA_PROPERTY(varName, enumName) \
^(NSString * newValue) { \
NSNumber* varName = \
[[enumName##EnumMap allKeysForObject:newValue] lastObject]; \
if (varName == nil) { \
return; \
} \
node.yoga.varName = (enumName)[varName unsignedIntegerValue]; \
}
#define APPLY_VALUE_TO_YGVALUE(varName) \
^(NSNumber * value) { \
YGValue newValue = node.yoga.varName; \
newValue.value = [value floatValue]; \
node.yoga.varName = newValue; \
}
#define APPLY_UNIT_TO_YGVALUE(varName, enumName) \
^(NSString * value) { \
NSNumber* varName = \
[[enumName##EnumMap allKeysForObject:value] lastObject]; \
if (varName == nil) { \
return; \
} \
YGValue newValue = node.yoga.varName; \
newValue.unit = (enumName)[varName unsignedIntegerValue]; \
node.yoga.varName = newValue; \
}

View File

@@ -11,8 +11,6 @@
#import <FlipperKitLayoutHelpers/SKNodeDescriptor.h>
@class SKDescriptorMapper;
@interface SKViewControllerDescriptor : SKNodeDescriptor<UIViewController*>
@end

View File

@@ -14,29 +14,13 @@
#import <FlipperKitLayoutHelpers/SKHiddenWindow.h>
#import <FlipperKitLayoutHelpers/SKNamed.h>
#import <FlipperKitLayoutHelpers/SKObject.h>
#import <FlipperKitLayoutHelpers/SKYogaKitHelper.h>
#import <FlipperKitLayoutHelpers/UIColor+SKSonarValueCoder.h>
#import <YogaKit/UIView+Yoga.h>
@implementation SKViewDescriptor
static NSDictionary* YGDirectionEnumMap = nil;
static NSDictionary* YGFlexDirectionEnumMap = nil;
static NSDictionary* YGJustifyEnumMap = nil;
static NSDictionary* YGAlignEnumMap = nil;
static NSDictionary* YGPositionTypeEnumMap = nil;
static NSDictionary* YGWrapEnumMap = nil;
static NSDictionary* YGOverflowEnumMap = nil;
static NSDictionary* YGDisplayEnumMap = nil;
static NSDictionary* YGUnitEnumMap = nil;
- (instancetype)initWithDescriptorMapper:
(id<SKDescriptorMapperProtocol>)mapper {
if (self = [super initWithDescriptorMapper:mapper]) {
initEnumDictionaries();
}
return self;
return [super initWithDescriptorMapper:mapper];
}
- (NSString*)identifierForNode:(UIView*)node {
@@ -125,102 +109,6 @@ static NSDictionary* YGUnitEnumMap = nil;
@"shouldGroupAccessibilityChildren" : SKMutableObject(
@(node.shouldGroupAccessibilityChildren)),
}],
!node.isYogaEnabled
? nil
: [SKNamed
newWithName:@"YGLayout"
withValue:@{
@"direction" : SKMutableObject(
YGDirectionEnumMap[@(node.yoga.direction)]),
@"justifyContent" : SKMutableObject(
YGJustifyEnumMap[@(node.yoga.justifyContent)]),
@"aligns" : @{
@"alignContent" : SKMutableObject(
YGAlignEnumMap[@(node.yoga.alignContent)]),
@"alignItems" : SKMutableObject(
YGAlignEnumMap[@(node.yoga.alignItems)]),
@"alignSelf" : SKMutableObject(
YGAlignEnumMap[@(node.yoga.alignSelf)]),
},
@"position" : @{
@"type" : SKMutableObject(
YGPositionTypeEnumMap[@(node.yoga.position)]),
@"left" : SKYGValueObject(node.yoga.left),
@"top" : SKYGValueObject(node.yoga.top),
@"right" : SKYGValueObject(node.yoga.right),
@"bottom" : SKYGValueObject(node.yoga.bottom),
@"start" : SKYGValueObject(node.yoga.start),
@"end" : SKYGValueObject(node.yoga.end),
},
@"overflow" : SKMutableObject(
YGOverflowEnumMap[@(node.yoga.overflow)]),
@"display" : SKMutableObject(
YGDisplayEnumMap[@(node.yoga.display)]),
@"flex" : @{
@"flexDirection" :
SKMutableObject(YGFlexDirectionEnumMap[
@(node.yoga.flexDirection)]),
@"flexWrap" : SKMutableObject(
YGWrapEnumMap[@(node.yoga.flexWrap)]),
@"flexGrow" : SKMutableObject(@(node.yoga.flexGrow)),
@"flexShrink" :
SKMutableObject(@(node.yoga.flexShrink)),
@"flexBasis" : SKYGValueObject(node.yoga.flexBasis),
},
@"margin" : @{
@"left" : SKYGValueObject(node.yoga.marginLeft),
@"top" : SKYGValueObject(node.yoga.marginTop),
@"right" : SKYGValueObject(node.yoga.marginRight),
@"bottom" : SKYGValueObject(node.yoga.marginBottom),
@"start" : SKYGValueObject(node.yoga.marginStart),
@"end" : SKYGValueObject(node.yoga.marginEnd),
@"horizontal" :
SKYGValueObject(node.yoga.marginHorizontal),
@"vertical" :
SKYGValueObject(node.yoga.marginVertical),
@"all" : SKYGValueObject(node.yoga.margin),
},
@"padding" : @{
@"left" : SKYGValueObject(node.yoga.paddingLeft),
@"top" : SKYGValueObject(node.yoga.paddingTop),
@"right" : SKYGValueObject(node.yoga.paddingRight),
@"bottom" : SKYGValueObject(node.yoga.paddingBottom),
@"start" : SKYGValueObject(node.yoga.paddingStart),
@"end" : SKYGValueObject(node.yoga.paddingEnd),
@"horizontal" :
SKYGValueObject(node.yoga.paddingHorizontal),
@"vertical" :
SKYGValueObject(node.yoga.paddingVertical),
@"all" : SKYGValueObject(node.yoga.padding),
},
@"border" : @{
@"leftWidth" :
SKMutableObject(@(node.yoga.borderLeftWidth)),
@"topWidth" :
SKMutableObject(@(node.yoga.borderTopWidth)),
@"rightWidth" :
SKMutableObject(@(node.yoga.borderRightWidth)),
@"bottomWidth" :
SKMutableObject(@(node.yoga.borderBottomWidth)),
@"startWidth" :
SKMutableObject(@(node.yoga.borderStartWidth)),
@"endWidth" :
SKMutableObject(@(node.yoga.borderEndWidth)),
@"all" : SKMutableObject(@(node.yoga.borderWidth)),
},
@"dimensions" : @{
@"width" : SKYGValueObject(node.yoga.width),
@"height" : SKYGValueObject(node.yoga.height),
@"minWidth" : SKYGValueObject(node.yoga.minWidth),
@"minHeight" : SKYGValueObject(node.yoga.minHeight),
@"maxWidth" : SKYGValueObject(node.yoga.maxWidth),
@"maxHeight" : SKYGValueObject(node.yoga.maxHeight),
},
@"aspectRatio" :
SKMutableObject(@(node.yoga.aspectRatio)),
@"resolvedDirection" : SKObject(
YGDirectionEnumMap[@(node.yoga.resolvedDirection)]),
}],
nil];
}
@@ -290,109 +178,6 @@ static NSDictionary* YGUnitEnumMap = nil;
@"CALayer.masksToBounds": ^(NSNumber *value) {
node.layer.masksToBounds = [value boolValue];
},
// YGLayout
@"YGLayout.direction": APPLY_ENUM_TO_YOGA_PROPERTY(direction, YGDirection),
@"YGLayout.justifyContent": APPLY_ENUM_TO_YOGA_PROPERTY(justifyContent, YGJustify),
@"YGLayout.aligns.alignContent": APPLY_ENUM_TO_YOGA_PROPERTY(alignContent, YGAlign),
@"YGLayout.aligns.alignItems": APPLY_ENUM_TO_YOGA_PROPERTY(alignItems, YGAlign),
@"YGLayout.aligns.alignSelf": APPLY_ENUM_TO_YOGA_PROPERTY(alignSelf, YGAlign),
@"YGLayout.position.type": APPLY_ENUM_TO_YOGA_PROPERTY(position, YGPositionType),
@"YGLayout.position.left.value": APPLY_VALUE_TO_YGVALUE(left),
@"YGLayout.position.left.unit": APPLY_UNIT_TO_YGVALUE(left, YGUnit),
@"YGLayout.position.top.value": APPLY_VALUE_TO_YGVALUE(top),
@"YGLayout.position.top.unit": APPLY_UNIT_TO_YGVALUE(top, YGUnit),
@"YGLayout.position.right.value": APPLY_VALUE_TO_YGVALUE(right),
@"YGLayout.position.right.unit": APPLY_UNIT_TO_YGVALUE(right, YGUnit),
@"YGLayout.position.bottom.value": APPLY_VALUE_TO_YGVALUE(bottom),
@"YGLayout.position.bottom.unit": APPLY_UNIT_TO_YGVALUE(bottom, YGUnit),
@"YGLayout.position.start.value": APPLY_VALUE_TO_YGVALUE(start),
@"YGLayout.position.start.unit": APPLY_UNIT_TO_YGVALUE(start, YGUnit),
@"YGLayout.position.end.value": APPLY_VALUE_TO_YGVALUE(end),
@"YGLayout.position.end.unit": APPLY_UNIT_TO_YGVALUE(end, YGUnit),
@"YGLayout.overflow": APPLY_ENUM_TO_YOGA_PROPERTY(overflow, YGOverflow),
@"YGLayout.display": APPLY_ENUM_TO_YOGA_PROPERTY(display, YGDisplay),
@"YGLayout.flex.flexDirection": APPLY_ENUM_TO_YOGA_PROPERTY(flexDirection, YGFlexDirection),
@"YGLayout.flex.flexWrap": APPLY_ENUM_TO_YOGA_PROPERTY(flexWrap, YGWrap),
@"YGLayout.flex.flexGrow": ^(NSNumber *value) {
node.yoga.flexGrow = [value floatValue];
},
@"YGLayout.flex.flexShrink": ^(NSNumber *value) {
node.yoga.flexShrink = [value floatValue];
},
@"YGLayout.flex.flexBasis.value": APPLY_VALUE_TO_YGVALUE(flexBasis),
@"YGLayout.flex.flexBasis.unit": APPLY_UNIT_TO_YGVALUE(flexBasis, YGUnit),
@"YGLayout.margin.left.value": APPLY_VALUE_TO_YGVALUE(marginLeft),
@"YGLayout.margin.left.unit": APPLY_UNIT_TO_YGVALUE(marginLeft, YGUnit),
@"YGLayout.margin.top.value": APPLY_VALUE_TO_YGVALUE(marginTop),
@"YGLayout.margin.top.unit": APPLY_UNIT_TO_YGVALUE(marginTop, YGUnit),
@"YGLayout.margin.right.value": APPLY_VALUE_TO_YGVALUE(marginRight),
@"YGLayout.margin.right.unit": APPLY_UNIT_TO_YGVALUE(marginRight, YGUnit),
@"YGLayout.margin.bottom.value": APPLY_VALUE_TO_YGVALUE(marginBottom),
@"YGLayout.margin.bottom.unit": APPLY_UNIT_TO_YGVALUE(marginBottom, YGUnit),
@"YGLayout.margin.start.value": APPLY_VALUE_TO_YGVALUE(marginStart),
@"YGLayout.margin.start.unit": APPLY_UNIT_TO_YGVALUE(marginStart, YGUnit),
@"YGLayout.margin.end.value": APPLY_VALUE_TO_YGVALUE(marginEnd),
@"YGLayout.margin.end.unit": APPLY_UNIT_TO_YGVALUE(marginEnd, YGUnit),
@"YGLayout.margin.horizontal.value": APPLY_VALUE_TO_YGVALUE(marginHorizontal),
@"YGLayout.margin.horizontal.unit": APPLY_UNIT_TO_YGVALUE(marginHorizontal, YGUnit),
@"YGLayout.margin.vertical.value": APPLY_VALUE_TO_YGVALUE(marginVertical),
@"YGLayout.margin.vertical.unit": APPLY_UNIT_TO_YGVALUE(marginVertical, YGUnit),
@"YGLayout.margin.all.value": APPLY_VALUE_TO_YGVALUE(margin),
@"YGLayout.margin.all.unit": APPLY_UNIT_TO_YGVALUE(margin, YGUnit),
@"YGLayout.padding.left.value": APPLY_VALUE_TO_YGVALUE(paddingLeft),
@"YGLayout.padding.left.unit": APPLY_UNIT_TO_YGVALUE(paddingLeft, YGUnit),
@"YGLayout.padding.top.value": APPLY_VALUE_TO_YGVALUE(paddingTop),
@"YGLayout.padding.top.unit": APPLY_UNIT_TO_YGVALUE(paddingTop, YGUnit),
@"YGLayout.padding.right.value": APPLY_VALUE_TO_YGVALUE(paddingRight),
@"YGLayout.padding.right.unit": APPLY_UNIT_TO_YGVALUE(paddingRight, YGUnit),
@"YGLayout.padding.bottom.value": APPLY_VALUE_TO_YGVALUE(paddingBottom),
@"YGLayout.padding.bottom.unit": APPLY_UNIT_TO_YGVALUE(paddingBottom, YGUnit),
@"YGLayout.padding.start.value": APPLY_VALUE_TO_YGVALUE(paddingStart),
@"YGLayout.padding.start.unit": APPLY_UNIT_TO_YGVALUE(paddingStart, YGUnit),
@"YGLayout.padding.end.value": APPLY_VALUE_TO_YGVALUE(paddingEnd),
@"YGLayout.padding.end.unit": APPLY_UNIT_TO_YGVALUE(paddingEnd, YGUnit),
@"YGLayout.padding.horizontal.value": APPLY_VALUE_TO_YGVALUE(paddingHorizontal),
@"YGLayout.padding.horizontal.unit": APPLY_UNIT_TO_YGVALUE(paddingHorizontal, YGUnit),
@"YGLayout.padding.vertical.value": APPLY_VALUE_TO_YGVALUE(paddingVertical),
@"YGLayout.padding.vertical.unit": APPLY_UNIT_TO_YGVALUE(paddingVertical, YGUnit),
@"YGLayout.padding.all.value": APPLY_VALUE_TO_YGVALUE(padding),
@"YGLayout.padding.all.unit": APPLY_UNIT_TO_YGVALUE(padding, YGUnit),
@"YGLayout.border.leftWidth": ^(NSNumber *value) {
node.yoga.borderLeftWidth = [value floatValue];
},
@"YGLayout.border.topWidth": ^(NSNumber *value) {
node.yoga.borderTopWidth = [value floatValue];
},
@"YGLayout.border.rightWidth": ^(NSNumber *value) {
node.yoga.borderRightWidth = [value floatValue];
},
@"YGLayout.border.bottomWidth": ^(NSNumber *value) {
node.yoga.borderBottomWidth = [value floatValue];
},
@"YGLayout.border.startWidth": ^(NSNumber *value) {
node.yoga.borderStartWidth = [value floatValue];
},
@"YGLayout.border.endWidth": ^(NSNumber *value) {
node.yoga.borderEndWidth = [value floatValue];
},
@"YGLayout.border.all": ^(NSNumber *value) {
node.yoga.borderWidth = [value floatValue];
},
@"YGLayout.dimensions.width.value": APPLY_VALUE_TO_YGVALUE(width),
@"YGLayout.dimensions.width.unit": APPLY_UNIT_TO_YGVALUE(width, YGUnit),
@"YGLayout.dimensions.height.value": APPLY_VALUE_TO_YGVALUE(height),
@"YGLayout.dimensions.height.unit": APPLY_UNIT_TO_YGVALUE(height, YGUnit),
@"YGLayout.dimensions.minWidth.value": APPLY_VALUE_TO_YGVALUE(minWidth),
@"YGLayout.dimensions.minWidth.unit": APPLY_UNIT_TO_YGVALUE(minWidth, YGUnit),
@"YGLayout.dimensions.minHeight.value": APPLY_VALUE_TO_YGVALUE(minHeight),
@"YGLayout.dimensions.minHeight.unit": APPLY_UNIT_TO_YGVALUE(minHeight, YGUnit),
@"YGLayout.dimensions.maxWidth.value": APPLY_VALUE_TO_YGVALUE(maxWidth),
@"YGLayout.dimensions.maxWidth.unit": APPLY_UNIT_TO_YGVALUE(maxWidth, YGUnit),
@"YGLayout.dimensions.maxHeight.value": APPLY_VALUE_TO_YGVALUE(maxHeight),
@"YGLayout.dimensions.maxHeight.unit": APPLY_UNIT_TO_YGVALUE(maxHeight, YGUnit),
@"YGLayout.aspectRatio": ^(NSNumber *value) {
node.yoga.aspectRatio = [value floatValue];
},
// Accessibility
@"Accessibility.isAccessibilityElement": ^(NSNumber *value) {
node.isAccessibilityElement = [value boolValue];
@@ -544,79 +329,6 @@ return dataMutations;
}
}
static void initEnumDictionaries() {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
YGDirectionEnumMap = @{
@(YGDirectionInherit) : @"inherit",
@(YGDirectionLTR) : @"LTR",
@(YGDirectionRTL) : @"RTL",
};
YGFlexDirectionEnumMap = @{
@(YGFlexDirectionColumn) : @"column",
@(YGFlexDirectionColumnReverse) : @"column-reverse",
@(YGFlexDirectionRow) : @"row",
@(YGFlexDirectionRowReverse) : @"row-reverse",
};
YGJustifyEnumMap = @{
@(YGJustifyFlexStart) : @"flex-start",
@(YGJustifyCenter) : @"center",
@(YGJustifyFlexEnd) : @"flex-end",
@(YGJustifySpaceBetween) : @"space-between",
@(YGJustifySpaceAround) : @"space-around",
};
YGAlignEnumMap = @{
@(YGAlignAuto) : @"auto",
@(YGAlignFlexStart) : @"flex-start",
@(YGAlignCenter) : @"end",
@(YGAlignFlexEnd) : @"flex-end",
@(YGAlignStretch) : @"stretch",
@(YGAlignBaseline) : @"baseline",
@(YGAlignSpaceBetween) : @"space-between",
@(YGAlignSpaceAround) : @"space-around",
};
YGPositionTypeEnumMap = @{
@(YGPositionTypeRelative) : @"relative",
@(YGPositionTypeAbsolute) : @"absolute",
};
YGWrapEnumMap = @{
@(YGWrapNoWrap) : @"no-wrap",
@(YGWrapWrap) : @"wrap",
@(YGWrapWrapReverse) : @"wrap-reverse",
};
YGOverflowEnumMap = @{
@(YGOverflowVisible) : @"visible",
@(YGOverflowHidden) : @"hidden",
@(YGOverflowScroll) : @"scroll",
};
YGDisplayEnumMap = @{
@(YGDisplayFlex) : @"flex",
@(YGDisplayNone) : @"none",
};
YGUnitEnumMap = @{
@(YGUnitUndefined) : @"undefined",
@(YGUnitPoint) : @"point",
@(YGUnitPercent) : @"percent",
@(YGUnitAuto) : @"auto",
};
});
}
static NSDictionary* SKYGValueObject(YGValue value) {
return @{
@"value" : SKMutableObject(@(value.value)),
@"unit" : SKMutableObject(YGUnitEnumMap[@(value.unit)]),
};
}
/*
Takes the originalTraits, and set all bits from toggleTraits to the toggleValue
e.g. originalTraits = UIAccessibilityTraitButton | UIAccessibilityTraitSelected

View File

@@ -0,0 +1,30 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#if FB_SONARKIT_ENABLED
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@class UIDFrameworkEventMetadata;
@class UIDFrameworkEvent;
@protocol UIDFrameworkEventManager<NSObject>
- (void)registerEventMetadata:(UIDFrameworkEventMetadata*)eventMetadata;
- (void)emitEvent:(UIDFrameworkEvent*)event;
- (NSArray<UIDFrameworkEventMetadata*>*)eventsMetadata;
- (NSArray<UIDFrameworkEvent*>*)events;
- (void)enable;
- (void)disable;
@end
NS_ASSUME_NONNULL_END
#endif

View File

@@ -0,0 +1,21 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#if FB_SONARKIT_ENABLED
#import <Foundation/Foundation.h>
#import "UIDFrameworkEventManager.h"
NS_ASSUME_NONNULL_BEGIN
@interface UIDSerialFrameworkEventManager : NSObject<UIDFrameworkEventManager>
@end
NS_ASSUME_NONNULL_END
#endif

View File

@@ -0,0 +1,75 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#if FB_SONARKIT_ENABLED
#import "UIDSerialFrameworkEventManager.h"
@interface UIDSerialFrameworkEventManager () {
dispatch_queue_t _queue;
NSMutableArray<UIDFrameworkEventMetadata*>* _eventMetadata;
NSMutableArray<UIDFrameworkEvent*>* _events;
BOOL _enabled;
}
@end
@implementation UIDSerialFrameworkEventManager
- (instancetype)init {
if (self = [super init]) {
_enabled = false;
_eventMetadata = [NSMutableArray new];
_events = [NSMutableArray new];
_queue = dispatch_queue_create(
"ui-debugger.framework-events", DISPATCH_QUEUE_SERIAL);
}
return self;
}
- (void)emitEvent:(nonnull UIDFrameworkEvent*)event {
dispatch_async(_queue, ^{
if (self->_enabled) {
[self->_events addObject:event];
}
});
}
- (void)registerEventMetadata:
(nonnull UIDFrameworkEventMetadata*)eventMetadata {
[_eventMetadata addObject:eventMetadata];
}
- (NSArray<UIDFrameworkEventMetadata*>*)eventsMetadata {
return [_eventMetadata copy];
}
- (NSArray<UIDFrameworkEvent*>*)events {
__block NSArray* events = nil;
dispatch_sync(_queue, ^{
events = [_events copy];
[_events removeAllObjects];
});
return events ?: @[];
}
- (void)enable {
dispatch_async(_queue, ^{
self->_enabled = true;
});
}
- (void)disable {
dispatch_async(_queue, ^{
self->_enabled = false;
[self->_events removeAllObjects];
});
}
@end
#endif

View File

@@ -0,0 +1,17 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#if FB_SONARKIT_ENABLED
@protocol UIDConnectionListener
- (void)onDidConnect;
- (void)onDidDisconnect;
@end
#endif

View File

@@ -0,0 +1,45 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#if FB_SONARKIT_ENABLED
#import <FlipperKit/FlipperConnection.h>
#import <Foundation/Foundation.h>
#import "UIDConnectionListener.h"
#import "UIDFrameworkEventManager.h"
#import "UIDUpdateDigester.h"
NS_ASSUME_NONNULL_BEGIN
@class UIDDescriptorRegister;
@class UIDTreeObserverFactory;
@class UIDFrameworkEvent;
@class UIDFrameworkEventMetadata;
@interface UIDContext : NSObject
@property(nonatomic, nullable) UIApplication* application;
@property(nonatomic, nullable) id<FlipperConnection> connection;
@property(nonatomic, readonly) UIDDescriptorRegister* descriptorRegister;
@property(nonatomic, readonly) UIDTreeObserverFactory* observerFactory;
@property(nonatomic, nullable) id<UIDUpdateDigester> updateDigester;
@property(nonatomic, strong, readonly) id<UIDFrameworkEventManager>
frameworkEventManager;
- (instancetype)initWithApplication:(UIApplication*)application
descriptorRegister:(UIDDescriptorRegister*)descriptorRegister
observerFactory:(UIDTreeObserverFactory*)observerFactory;
- (NSSet<id<UIDConnectionListener>>*)connectionListeners;
- (void)addConnectionListener:(id<UIDConnectionListener>)listener;
- (void)removeConnectionListener:(id<UIDConnectionListener>)listener;
@end
NS_ASSUME_NONNULL_END
#endif

View File

@@ -0,0 +1,64 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#if FB_SONARKIT_ENABLED
#import "UIDContext.h"
#import "UIDDescriptorRegister.h"
#import "UIDSerialFrameworkEventManager.h"
#import "UIDTreeObserverFactory.h"
@interface UIDContext () {
NSMutableSet<id<UIDConnectionListener>>* _connectionListeners;
dispatch_queue_t _accessQueue;
}
@end
@implementation UIDContext
- (instancetype)initWithApplication:(UIApplication*)application
descriptorRegister:(UIDDescriptorRegister*)descriptorRegister
observerFactory:(UIDTreeObserverFactory*)observerFactory {
self = [super init];
if (self) {
_application = application;
_descriptorRegister = descriptorRegister;
_observerFactory = observerFactory;
_connection = nil;
_frameworkEventManager = [UIDSerialFrameworkEventManager new];
_connectionListeners = [NSMutableSet new];
_accessQueue =
dispatch_queue_create("ui-debugger.context", DISPATCH_QUEUE_SERIAL);
}
return self;
}
- (NSSet<id<UIDConnectionListener>>*)connectionListeners {
__block NSSet* listeners = nil;
dispatch_sync(_accessQueue, ^{
listeners = [_connectionListeners copy];
});
return listeners;
}
- (void)addConnectionListener:(id<UIDConnectionListener>)listener {
dispatch_sync(_accessQueue, ^{
[_connectionListeners addObject:listener];
});
}
- (void)removeConnectionListener:(id<UIDConnectionListener>)listener {
dispatch_sync(_accessQueue, ^{
[_connectionListeners removeObject:listener];
});
}
@end
#endif

View File

@@ -0,0 +1,31 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#if FB_SONARKIT_ENABLED
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@protocol UIDUpdate
@end
@interface UIDSubtreeUpdate : NSObject<UIDUpdate>
@property(nonatomic) NSString* observerType;
@property(nonatomic) NSUInteger rootId;
@property(nonatomic) NSArray* nodes;
@property(nonatomic) NSTimeInterval timestamp;
@property(nonatomic) long traversalMS;
@property(nonatomic) long snapshotMS;
@property(nonatomic) UIImage* snapshot;
@end
NS_ASSUME_NONNULL_END
#endif

View File

@@ -0,0 +1,15 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#if FB_SONARKIT_ENABLED
#import "UIDUpdate.h"
@implementation UIDSubtreeUpdate
@end
#endif

View File

@@ -0,0 +1,23 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#if FB_SONARKIT_ENABLED
#import <Foundation/Foundation.h>
#import "UIDUpdate.h"
NS_ASSUME_NONNULL_BEGIN
@protocol UIDUpdateDigester
- (void)digest:(id<UIDUpdate>)update;
@end
NS_ASSUME_NONNULL_END
#endif

View File

@@ -0,0 +1,52 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#if FB_SONARKIT_ENABLED
#import <Foundation/Foundation.h>
#import "UIDNodeDescriptor.h"
NS_ASSUME_NONNULL_BEGIN
/**
A chained descriptor is a special type of descriptor that models the
inheritance hierarchy in native UI frameworks. With this setup you can define
a descriptor for each class in the inheritance chain and given that we record
the super class's descriptor we can automatically aggregate the results for
each descriptor in the inheritance hierarchy in a chain.
The result is that each descriptor in the inheritance chain only exports the
attributes that it knows about but we still get all the attributes from the
parent classes.
*/
@interface UIDChainedDescriptor<__covariant T> : UIDNodeDescriptor<T>
@property(strong, nonatomic) UIDChainedDescriptor* parent;
/** The children this node exposes in the inspector. */
- (NSArray<id<NSObject>>*)aggregateChildrenOfNode:(T)node;
/**
Get the attributes to show for this node in the sidebar of the inspector. The
object will be shown in order and with a header matching the given name.
*/
- (void)aggregateAttributes:(UIDMutableAttributes*)attributes forNode:(id)node;
/**
These are shown inline in the tree view on the desktop, will likely be removed
in the future.
*/
- (void)aggregateInlineAttributes:(UIDMutableInlineAttributes*)attributes
forNode:(id)node;
- (NSSet<NSString*>*)aggregateTagsForNode:(id)node;
@end
NS_ASSUME_NONNULL_END
#endif

View File

@@ -0,0 +1,87 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#if FB_SONARKIT_ENABLED
#import "UIDChainedDescriptor.h"
#import "UIDInspectable.h"
@implementation UIDChainedDescriptor
- (NSSet<NSString*>*)tagsForNode:(id)node {
NSSet* tags = [self aggregateTagsForNode:node] ?: [_parent tagsForNode:node];
return tags ?: [NSSet set];
}
- (NSSet<NSString*>*)aggregateTagsForNode:(id)node {
return nil;
}
- (UIDBounds*)boundsForNode:(id)node {
return [_parent boundsForNode:node];
}
- (id<NSObject>)activeChildForNode:(id)node {
return [_parent activeChildForNode:node];
}
- (UIImage*)snapshotForNode:(id)node {
return [_parent snapshotForNode:node];
}
- (NSArray<id<NSObject>>*)childrenOfNode:(id)node {
NSArray<id<NSObject>>* children = [self aggregateChildrenOfNode:node];
if (!children) {
children = [_parent childrenOfNode:node];
}
return children != NULL ? children : [NSArray array];
}
- (NSArray<id<NSObject>>*)aggregateChildrenOfNode:(id)node {
return nil;
}
- (UIDAttributes*)attributesForNode:(id)node {
UIDMutableAttributes* attributes = [NSMutableDictionary new];
[self aggregateAttributes:attributes forNode:node];
UIDChainedDescriptor* currentDescriptor = _parent;
while (currentDescriptor) {
[currentDescriptor aggregateAttributes:attributes forNode:node];
currentDescriptor = currentDescriptor.parent;
}
return attributes;
}
- (void)aggregateAttributes:(UIDMutableAttributes*)attributes forNode:(id)node {
}
- (UIDInlineAttributes*)inlineAttributesForNode:(id)node {
UIDMutableInlineAttributes* attributes = [NSMutableDictionary new];
[attributes addEntriesFromDictionary:[super inlineAttributesForNode:node]];
[self aggregateInlineAttributes:attributes forNode:node];
UIDChainedDescriptor* currentDescriptor = _parent;
while (currentDescriptor) {
[currentDescriptor aggregateInlineAttributes:attributes forNode:node];
currentDescriptor = currentDescriptor.parent;
}
return attributes;
}
- (void)aggregateInlineAttributes:(UIDMutableInlineAttributes*)attributes
forNode:(id)node {
}
@end
#endif

View File

@@ -0,0 +1,69 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#if FB_SONARKIT_ENABLED
#import <UIKit/UIKit.h>
#import "UIDBounds.h"
#import "UIDInspectable.h"
NS_ASSUME_NONNULL_BEGIN
@protocol UIDDescriptor<NSObject>
/**
A globally unique ID used to identify a node in the hierarchy.
*/
- (NSUInteger)UID_identifier;
/**
The name used to identify this node in the inspector. Does not need to be
unique. A good default is to use the class name of the node.
*/
- (NSString*)UID_name;
/**
Get the attributes to show for this node in the sidebar of the inspector. The
object will be shown in order and with a header matching the given name.
*/
- (void)UID_aggregateAttributes:(UIDMutableAttributes*)attributes;
@optional
/**
These are shown inline in the tree view on the desktop, will likely be removed
in the future.
*/
- (UIDInlineAttributes*)UID_inlineAttributes;
/** The children this node exposes in the inspector. */
- (NSArray<id<NSObject>>*)UID_children;
/** Should be w.r.t the direct parent */
- (UIDBounds*)UID_bounds;
/**
If the type has the concept of overlapping children, then this indicates
which child is active / on top, we will only listen to / traverse this child.
If return null we assume all children are 'active'.
*/
- (id<NSObject>)UID_activeChild;
/** Get a snapshot of the node. */
- (UIImage*)UID_snapshot;
/**
Set of tags to describe this node in an abstract way for the UI. Unfortunately
this can't be an enum as we have to plugin 3rd party frameworks dynamically.
*/
- (NSSet<NSString*>*)UID_tags;
@end
NS_ASSUME_NONNULL_END
#endif

View File

@@ -0,0 +1,26 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#if FB_SONARKIT_ENABLED
#import <Foundation/Foundation.h>
#import "UIDNodeDescriptor.h"
NS_ASSUME_NONNULL_BEGIN
@interface UIDDescriptorRegister : NSObject
+ (instancetype)defaultRegister;
- (void)registerDescriptor:(UIDNodeDescriptor*)descriptor forClass:(Class)clazz;
- (UIDNodeDescriptor*)descriptorForClass:(Class)clazz;
@end
NS_ASSUME_NONNULL_END
#endif

View File

@@ -0,0 +1,107 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#if FB_SONARKIT_ENABLED
#import "UIDDescriptorRegister.h"
#import <objc/runtime.h>
#import "UIDChainedDescriptor.h"
#import "UIDUIAccessibilityElementDescriptor.h"
#import "UIDUIApplicationDescriptor.h"
#import "UIDUILabelDescriptor.h"
#import "UIDUINavigationControllerDescriptor.h"
#import "UIDUITabBarControllerDescriptor.h"
#import "UIDUIViewControllerDescriptor.h"
#import "UIDUIViewDescriptor.h"
#import "UIDUIWindowDescriptor.h"
@interface UIDDescriptorRegister ()
- (void)prepareChain;
@end
@implementation UIDDescriptorRegister {
NSMutableDictionary<NSString*, UIDNodeDescriptor*>* _register;
}
- (instancetype)init {
self = [super init];
if (self) {
_register = [NSMutableDictionary new];
}
return self;
}
+ (instancetype)defaultRegister {
static UIDDescriptorRegister* defaultRegister = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
defaultRegister = [UIDDescriptorRegister new];
[defaultRegister registerDescriptor:[UIDUIApplicationDescriptor new]
forClass:[UIApplication class]];
[defaultRegister registerDescriptor:[UIDUIWindowDescriptor new]
forClass:[UIWindow class]];
[defaultRegister registerDescriptor:[UIDUIViewControllerDescriptor new]
forClass:[UIViewController class]];
[defaultRegister registerDescriptor:[UIDUIViewDescriptor new]
forClass:[UIView class]];
[defaultRegister registerDescriptor:[UIDUILabelDescriptor new]
forClass:[UILabel class]];
[defaultRegister
registerDescriptor:[UIDUIAccessibilityElementDescriptor new]
forClass:[UIAccessibilityElement class]];
});
return defaultRegister;
}
- (void)registerDescriptor:(UIDNodeDescriptor*)descriptor
forClass:(Class)clazz {
NSString* key = NSStringFromClass(clazz);
_register[key] = descriptor;
[self prepareChain];
}
- (UIDNodeDescriptor*)descriptorForClass:(Class)clazz {
UIDNodeDescriptor* classDescriptor = nil;
while (classDescriptor == nil && clazz != nil) {
classDescriptor = [_register objectForKey:NSStringFromClass(clazz)];
clazz = [clazz superclass];
}
return classDescriptor;
}
- (void)prepareChain {
NSEnumerator* enumerator = [_register keyEnumerator];
NSString* key;
while ((key = [enumerator nextObject])) {
UIDNodeDescriptor* descriptor = [_register objectForKey:key];
if ([descriptor isKindOfClass:[UIDChainedDescriptor class]]) {
UIDChainedDescriptor* chainedDescriptor =
(UIDChainedDescriptor*)descriptor;
Class clazz = NSClassFromString(key);
Class superclass = [clazz superclass];
UIDNodeDescriptor* superDescriptor = [self descriptorForClass:superclass];
if ([superDescriptor isKindOfClass:[UIDChainedDescriptor class]]) {
chainedDescriptor.parent = (UIDChainedDescriptor*)superDescriptor;
}
}
}
}
@end
#endif

View File

@@ -0,0 +1,51 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#if FB_SONARKIT_ENABLED
#import <Foundation/Foundation.h>
#import "UIDMetadata.h"
NS_ASSUME_NONNULL_BEGIN
FOUNDATION_EXPORT NSString* const UIDEBUGGER_METADATA_TYPE_IDENTIFIER;
FOUNDATION_EXPORT NSString* const UIDEBUGGER_METADATA_TYPE_ATTRIBUTE;
FOUNDATION_EXPORT NSString* const UIDEBUGGER_METADATA_TYPE_LAYOUT;
FOUNDATION_EXPORT NSString* const UIDEBUGGER_METADATA_TYPE_DOCUMENTATION;
@interface UIDMetadataRegister : NSObject
+ (instancetype)shared;
- (UIDMetadataId)registerMetadataWithType:(NSString*)type name:(NSString*)name;
- (UIDMetadataId)registerMetadataWithType:(NSString*)type
name:(NSString*)name
isMutable:(bool)isMutable
definedBy:(UIDMetadataId)parent;
- (UIDMetadataId)registerMetadataWithType:(NSString*)type
name:(NSString*)name
isMutable:(bool)isMutable
definedBy:(UIDMetadataId)parent
customAttributes:
(nullable NSDictionary<NSString*, id>*)
customAttributes;
- (NSDictionary<UIDMetadataId, UIDMetadata*>*)extractPendingMetadata;
- (UIDMetadataId)findMetadataDefinedBy:(UIDMetadataId)definedById
name:(NSString*)name;
- (UIDMetadataId)findRootMetadataWithName:(NSString*)name;
- (void)reset;
@end
NS_ASSUME_NONNULL_END
#endif

View File

@@ -0,0 +1,148 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#if FB_SONARKIT_ENABLED
#import "UIDMetadataRegister.h"
NSString* const UIDEBUGGER_METADATA_TYPE_IDENTIFIER = @"identity";
NSString* const UIDEBUGGER_METADATA_TYPE_ATTRIBUTE = @"attribute";
NSString* const UIDEBUGGER_METADATA_TYPE_LAYOUT = @"layout";
NSString* const UIDEBUGGER_METADATA_TYPE_DOCUMENTATION = @"documentation";
typedef NSMutableDictionary<NSString*, UIDMetadata*>* UIDNamedMetadata;
@interface UIDMetadataRegister () {
NSMutableDictionary<UIDMetadataId, UIDNamedMetadata>* _register;
NSMutableArray<UIDMetadata*>* _pending;
uint32_t _generator;
UIDMetadataId _rootId;
NSLock* _lock;
}
@end
@implementation UIDMetadataRegister
- (instancetype)init {
self = [super init];
if (self) {
_lock = [NSLock new];
_register = [NSMutableDictionary new];
_pending = [NSMutableArray new];
_rootId = @0;
_generator = 0;
[_register setObject:[NSMutableDictionary new] forKey:_rootId];
}
return self;
}
+ (instancetype)shared {
static UIDMetadataRegister* instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [UIDMetadataRegister new];
});
return instance;
}
- (UIDMetadataId)registerMetadataWithType:(NSString*)type name:(NSString*)name {
return [self registerMetadataWithType:type
name:name
isMutable:false
definedBy:_rootId];
}
- (UIDMetadataId)registerMetadataWithType:(NSString*)type
name:(NSString*)name
isMutable:(bool)isMutable
definedBy:(UIDMetadataId)parent {
return [self registerMetadataWithType:type
name:name
isMutable:isMutable
definedBy:parent
customAttributes:nil];
}
- (UIDMetadataId)registerMetadataWithType:(NSString*)type
name:(NSString*)name
isMutable:(bool)isMutable
definedBy:(UIDMetadataId)parent
customAttributes:
(nullable NSDictionary<NSString*, id>*)
customAttributes {
if (!parent) {
parent = _rootId;
}
UIDMetadataId identifier = @(++_generator);
UIDMetadata* metadata =
[[UIDMetadata alloc] initWithIdentifier:identifier
type:type
name:name
isMutable:isMutable
parent:parent
possibleValues:nil
tags:nil
customAttributes:customAttributes];
[_lock lock];
if (![_register objectForKey:parent]) {
[_register setObject:[NSMutableDictionary new] forKey:parent];
}
[[_register objectForKey:parent] setObject:metadata forKey:name];
[_pending addObject:metadata];
[_lock unlock];
return identifier;
}
- (UIDMetadataId)findRootMetadataWithName:(NSString*)name {
return [self findMetadataDefinedBy:_rootId name:name];
}
- (UIDMetadataId)findMetadataDefinedBy:(UIDMetadataId)parentId
name:(NSString*)name {
[_lock lock];
UIDMetadata* metadata = [[_register objectForKey:parentId] objectForKey:name];
[_lock unlock];
if (metadata) {
return metadata.identifier;
}
return nil;
}
- (NSDictionary<UIDMetadataId, UIDMetadata*>*)extractPendingMetadata {
NSMutableDictionary* pendingMetadata = [NSMutableDictionary new];
[_lock lock];
for (UIDMetadata* metadata in _pending) {
[pendingMetadata setObject:metadata forKey:metadata.identifier];
}
[_pending removeAllObjects];
[_lock unlock];
return pendingMetadata;
}
- (void)reset {
[_lock lock];
[_pending removeAllObjects];
for (id key in _register) {
UIDNamedMetadata value = [_register objectForKey:key];
[_pending addObjectsFromArray:value.allValues];
}
[_lock unlock];
}
@end
#endif

View File

@@ -0,0 +1,72 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#if FB_SONARKIT_ENABLED
#import <UIKit/UIKit.h>
#import "UIDBounds.h"
#import "UIDInspectable.h"
NS_ASSUME_NONNULL_BEGIN
@interface UIDNodeDescriptor<__covariant T> : NSObject
/**
A globally unique ID used to identify a node in the hierarchy.
*/
- (NSUInteger)identifierForNode:(T)node;
/**
The name used to identify this node in the inspector. Does not need to be
unique. A good default is to use the class name of the node.
*/
- (NSString*)nameForNode:(T)node;
/**
Get the attributes to show for this node in the sidebar of the inspector. The
object will be shown in order and with a header matching the given name.
*/
- (UIDAttributes*)attributesForNode:(T)node;
/**
These are shown inline in the tree view on the desktop, will likely be removed
in the future.
*/
- (UIDInlineAttributes*)inlineAttributesForNode:(T)node;
/**
These contain additional contextual data (currently: Bloks node metadata).
*/
- (UIDGenericAttributes*)hiddenAttributesForNode:(T)node;
/** The children this node exposes in the inspector. */
- (NSArray<id<NSObject>>*)childrenOfNode:(T)node;
/** Should be w.r.t the direct parent */
- (UIDBounds*)boundsForNode:(T)node;
/**
If the type has the concept of overlapping children, then this indicates
which child is active / on top, we will only listen to / traverse this child.
If return null we assume all children are 'active'.
*/
- (id<NSObject>)activeChildForNode:(T)node;
/** Get a snapshot of the node. */
- (UIImage*)snapshotForNode:(T)node;
/**
Set of tags to describe this node in an abstract way for the UI. Unfortunately
this can't be an enum as we have to plugin 3rd party frameworks dynamically.
*/
- (NSSet<NSString*>*)tagsForNode:(T)node;
@end
NS_ASSUME_NONNULL_END
#endif

View File

@@ -0,0 +1,56 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#if FB_SONARKIT_ENABLED
#import "UIDNodeDescriptor.h"
@implementation UIDNodeDescriptor
- (NSUInteger)identifierForNode:(id)node {
return [node hash];
}
- (NSString*)nameForNode:(id)node {
return NSStringFromClass([node class]);
}
- (UIDInlineAttributes*)inlineAttributesForNode:(id)node {
return @{@"address" : [NSString stringWithFormat:@"%p", node]};
}
- (UIDGenericAttributes*)hiddenAttributesForNode:(id)node {
return nil;
}
- (UIDAttributes*)attributesForNode:(id)node {
return [NSDictionary dictionary];
}
- (NSArray<id<NSObject>>*)childrenOfNode:(id)node {
return [NSArray array];
}
- (NSSet<NSString*>*)tagsForNode:(id)node {
return [NSSet set];
}
- (UIDBounds*)boundsForNode:(id)node {
return nil;
}
- (id<NSObject>)activeChildForNode:(id)node {
return nil;
}
- (UIImage*)snapshotForNode:(id)node {
return nil;
}
@end
#endif

View File

@@ -0,0 +1,22 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#if FB_SONARKIT_ENABLED
#import <UIKit/UIKit.h>
#import "UIDNodeDescriptor.h"
NS_ASSUME_NONNULL_BEGIN
@interface UIDUIAccessibilityElementDescriptor
: UIDNodeDescriptor<UIAccessibilityElement*>
@end
NS_ASSUME_NONNULL_END
#endif

View File

@@ -0,0 +1,22 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#if FB_SONARKIT_ENABLED
#import "UIDUIAccessibilityElementDescriptor.h"
#import "UIDBounds.h"
#import "UIDSnapshot.h"
@implementation UIDUIAccessibilityElementDescriptor
- (UIDBounds*)boundsForNode:(UIAccessibilityElement*)node {
return [UIDBounds fromRect:node.accessibilityFrame];
}
@end
#endif

View File

@@ -0,0 +1,21 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#if FB_SONARKIT_ENABLED
#import <UIKit/UIKit.h>
#import "UIDNodeDescriptor.h"
NS_ASSUME_NONNULL_BEGIN
@interface UIDUIApplicationDescriptor : UIDNodeDescriptor<UIApplication*>
@end
NS_ASSUME_NONNULL_END
#endif

View File

@@ -0,0 +1,50 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#if FB_SONARKIT_ENABLED
#import "UIDUIApplicationDescriptor.h"
#import <objc/runtime.h>
#import <string>
#import <unordered_set>
#import "UIDBounds.h"
#import "UIDSnapshot.h"
@implementation UIDUIApplicationDescriptor
- (NSArray<id<NSObject>>*)childrenOfNode:(UIApplication*)node {
static std::unordered_set<std::string> ignoredWindows(
{"FBStatusBarTrackingWindow",
"FBAccessibilityOverlayWindow",
"UITextEffectsWindow"});
NSMutableArray<UIWindow*>* children = [NSMutableArray new];
for (UIWindow* window in node.windows) {
if (ignoredWindows.find(class_getName(window.class)) !=
ignoredWindows.end()) {
continue;
}
[children addObject:window];
}
return children;
}
- (id<NSObject>)activeChildForNode:(UIApplication*)node {
return node.keyWindow;
}
- (UIDBounds*)boundsForNode:(UIApplication*)node {
return [UIDBounds fromRect:[UIScreen mainScreen].bounds];
}
- (UIImage*)snapshotForNode:(UIApplication*)node {
return UIDApplicationSnapshot(node, [self childrenOfNode:node]);
}
@end
#endif

View File

@@ -0,0 +1,21 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#if FB_SONARKIT_ENABLED
#import <UIKit/UIKit.h>
#import "UIDChainedDescriptor.h"
NS_ASSUME_NONNULL_BEGIN
@interface UIDUILabelDescriptor : UIDChainedDescriptor<UILabel*>
@end
NS_ASSUME_NONNULL_END
#endif

View File

@@ -0,0 +1,248 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#if FB_SONARKIT_ENABLED
#import "UIDUILabelDescriptor.h"
#import "UIDInspectable.h"
#import "UIDMetadata.h"
#import "UIDMetadataRegister.h"
@interface UIDUILabelDescriptor () {
UIDMetadataId UILabelAttributeId;
UIDMetadataId TextAttributeId;
UIDMetadataId AttributedTextAttributeId;
UIDMetadataId FontAttributeId;
UIDMetadataId TextColorAttributeId;
UIDMetadataId TextAlignmentAttributeId;
UIDMetadataId LineBreakModeAttributeId;
UIDMetadataId LineBreakStrategyAttributeId;
UIDMetadataId EnabledAttributeId;
UIDMetadataId ShowExpansionTextAttributeId;
UIDMetadataId AdjustsFontSizeToFitWidthAttributeId;
UIDMetadataId AllowsDefaultTightneningForTruncationAttributeId;
UIDMetadataId BaselineAdjustmentAttributeId;
UIDMetadataId MinScaleFactorAttributeId;
UIDMetadataId NumberOfLinesAttributeId;
UIDMetadataId HighlightedTextColorAttributeId;
UIDMetadataId HighlightedAttributeId;
UIDMetadataId ShadowColorAttributeId;
UIDMetadataId ShadowOffsetAttributeId;
NSDictionary* NSTextAlignmentEnum;
NSDictionary* NSLineBreakModeEnum;
NSDictionary* NSLineBreakStrategyEnum;
NSDictionary* UIBaselineAdjustmentEnum;
}
@end
@implementation UIDUILabelDescriptor
- (instancetype)init {
self = [super init];
if (self) {
NSTextAlignmentEnum = @{
@(NSTextAlignmentLeft) : @"LEFT",
@(NSTextAlignmentRight) : @"RIGHT",
@(NSTextAlignmentCenter) : @"CENTER",
@(NSTextAlignmentJustified) : @"JUSTIFIED",
@(NSTextAlignmentNatural) : @"NATURAL",
};
NSLineBreakModeEnum = @{
@(NSLineBreakByWordWrapping) : @"WORD WRAPPING",
@(NSLineBreakByCharWrapping) : @"CHAR WRAPPING",
@(NSLineBreakByClipping) : @"CLIPPING",
@(NSLineBreakByTruncatingHead) : @"TRUNCATING HEAD",
@(NSLineBreakByTruncatingTail) : @"TRUNCATING TAIL",
@(NSLineBreakByTruncatingMiddle) : @"TRUNCATING MIDDLE",
};
if (@available(iOS 14.0, *)) {
NSLineBreakStrategyEnum = @{
@(NSLineBreakStrategyNone) : @"NONE",
@(NSLineBreakStrategyPushOut) : @"PUSH OUT",
@(NSLineBreakStrategyHangulWordPriority) : @"HANGUL WORD PRIORITY",
@(NSLineBreakStrategyStandard) : @"STANDARD",
};
} else {
NSLineBreakStrategyEnum = @{
@(NSLineBreakStrategyNone) : @"NONE",
@(NSLineBreakStrategyPushOut) : @"PUSH OUT",
};
}
UIBaselineAdjustmentEnum = @{
@(UIBaselineAdjustmentAlignBaselines) : @"ALIGN BASELINES",
@(UIBaselineAdjustmentAlignCenters) : @"ALIGN CENTERS",
@(UIBaselineAdjustmentNone) : @"NONE",
};
UILabelAttributeId = [[UIDMetadataRegister shared]
registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE
name:@"UILabel"];
TextAttributeId = [[UIDMetadataRegister shared]
registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE
name:@"text"
isMutable:false
definedBy:UILabelAttributeId];
AttributedTextAttributeId = [[UIDMetadataRegister shared]
registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE
name:@"attributedText"
isMutable:false
definedBy:UILabelAttributeId];
FontAttributeId = [[UIDMetadataRegister shared]
registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE
name:@"font"
isMutable:false
definedBy:UILabelAttributeId];
TextColorAttributeId = [[UIDMetadataRegister shared]
registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE
name:@"textColor"
isMutable:false
definedBy:UILabelAttributeId];
TextAlignmentAttributeId = [[UIDMetadataRegister shared]
registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE
name:@"textAlignment"
isMutable:false
definedBy:UILabelAttributeId];
LineBreakModeAttributeId = [[UIDMetadataRegister shared]
registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE
name:@"lineBreakMode"
isMutable:false
definedBy:UILabelAttributeId];
LineBreakStrategyAttributeId = [[UIDMetadataRegister shared]
registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE
name:@"lineBreakStrategy"
isMutable:false
definedBy:UILabelAttributeId];
EnabledAttributeId = [[UIDMetadataRegister shared]
registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE
name:@"enabled"
isMutable:false
definedBy:UILabelAttributeId];
if (@available(iOS 15.0, macCatalyst 15.0, *)) {
ShowExpansionTextAttributeId = [[UIDMetadataRegister shared]
registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE
name:@"showsExpansionTextWhenTruncated"
isMutable:false
definedBy:UILabelAttributeId];
}
AdjustsFontSizeToFitWidthAttributeId = [[UIDMetadataRegister shared]
registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE
name:@"adjustsFontSizeToFitWidth"
isMutable:false
definedBy:UILabelAttributeId];
AllowsDefaultTightneningForTruncationAttributeId =
[[UIDMetadataRegister shared]
registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE
name:@"allowsDefaultTighteningForTruncation"
isMutable:false
definedBy:UILabelAttributeId];
BaselineAdjustmentAttributeId = [[UIDMetadataRegister shared]
registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE
name:@"baselineAdjustment"
isMutable:false
definedBy:UILabelAttributeId];
MinScaleFactorAttributeId = [[UIDMetadataRegister shared]
registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE
name:@"minimumScaleFactor"
isMutable:false
definedBy:UILabelAttributeId];
NumberOfLinesAttributeId = [[UIDMetadataRegister shared]
registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE
name:@"numberOfLines"
isMutable:false
definedBy:UILabelAttributeId];
HighlightedTextColorAttributeId = [[UIDMetadataRegister shared]
registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE
name:@"highlightedTextColor"
isMutable:false
definedBy:UILabelAttributeId];
HighlightedAttributeId = [[UIDMetadataRegister shared]
registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE
name:@"highligted"
isMutable:false
definedBy:UILabelAttributeId];
ShadowColorAttributeId = [[UIDMetadataRegister shared]
registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE
name:@"shadowColor"
isMutable:false
definedBy:UILabelAttributeId];
ShadowOffsetAttributeId = [[UIDMetadataRegister shared]
registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE
name:@"shadowOffset"
isMutable:false
definedBy:UILabelAttributeId];
}
return self;
}
- (void)aggregateAttributes:(UIDMutableAttributes*)attributes
forNode:(UILabel*)node {
NSMutableDictionary* labelAttributes = [NSMutableDictionary new];
[labelAttributes setObject:[UIDInspectableText fromText:node.text]
forKey:TextAttributeId];
[labelAttributes
setObject:[UIDInspectableText fromText:[node.attributedText string]]
forKey:AttributedTextAttributeId];
[labelAttributes setObject:[UIDInspectableText fromText:node.font.fontName]
forKey:FontAttributeId];
[labelAttributes setObject:[UIDInspectableColor fromColor:node.textColor]
forKey:TextColorAttributeId];
[labelAttributes
setObject:[UIDInspectableEnum
from:NSTextAlignmentEnum[@(node.textAlignment)]]
forKey:TextAlignmentAttributeId];
[labelAttributes
setObject:[UIDInspectableEnum
from:NSLineBreakModeEnum[@(node.lineBreakMode)]]
forKey:LineBreakModeAttributeId];
[labelAttributes setObject:[UIDInspectableBoolean fromBoolean:node.enabled]
forKey:EnabledAttributeId];
if (@available(iOS 15.0, macCatalyst 15.0, *)) {
[labelAttributes
setObject:[UIDInspectableBoolean
fromBoolean:node.showsExpansionTextWhenTruncated]
forKey:ShowExpansionTextAttributeId];
}
[labelAttributes setObject:[UIDInspectableBoolean
fromBoolean:node.adjustsFontSizeToFitWidth]
forKey:AdjustsFontSizeToFitWidthAttributeId];
[labelAttributes
setObject:[UIDInspectableBoolean
fromBoolean:node.allowsDefaultTighteningForTruncation]
forKey:AllowsDefaultTightneningForTruncationAttributeId];
[labelAttributes
setObject:[UIDInspectableEnum
from:UIBaselineAdjustmentEnum[@(node.baselineAdjustment)]]
forKey:BaselineAdjustmentAttributeId];
[labelAttributes
setObject:[UIDInspectableNumber fromCGFloat:node.minimumScaleFactor]
forKey:MinScaleFactorAttributeId];
[labelAttributes
setObject:[UIDInspectableNumber
fromNumber:[NSNumber numberWithInt:node.numberOfLines]]
forKey:NumberOfLinesAttributeId];
[labelAttributes
setObject:[UIDInspectableColor fromColor:node.highlightedTextColor]
forKey:HighlightedTextColorAttributeId];
[labelAttributes
setObject:[UIDInspectableBoolean fromBoolean:node.highlighted]
forKey:HighlightedAttributeId];
[labelAttributes setObject:[UIDInspectableColor fromColor:node.shadowColor]
forKey:ShadowColorAttributeId];
[labelAttributes setObject:[UIDInspectableSize fromSize:node.shadowOffset]
forKey:ShadowOffsetAttributeId];
[attributes setObject:[UIDInspectableObject fromFields:labelAttributes]
forKey:UILabelAttributeId];
}
@end
#endif

View File

@@ -0,0 +1,34 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#if FB_SONARKIT_ENABLED
#import <UIKit/UIKit.h>
#import "UIDNodeDescriptor.h"
NS_ASSUME_NONNULL_BEGIN
/**
UINavigationController
(https://developer.apple.com/documentation/uikit/uinavigationcontroller?language=objc)
Propeties:
- viewControllers: the view controllers currently on the navigation stack.
- topViewController: last controller pushed on top of the navigation
stack.
- visibleViewController: the view controller associated with the
currently visible view in the navigation stack. View Controller can also be
presented instead of pushed. If presented, then it hasn't changed the stack
even though its view is visible.
*/
@interface UIDUINavigationControllerDescriptor
: UIDNodeDescriptor<UINavigationController*>
@end
NS_ASSUME_NONNULL_END
#endif

View File

@@ -0,0 +1,28 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#if FB_SONARKIT_ENABLED
#import "UIDUINavigationControllerDescriptor.h"
@implementation UIDUINavigationControllerDescriptor
- (NSArray<id<NSObject>>*)childrenOfNode:(UINavigationController*)node {
return node.viewControllers;
}
- (id<NSObject>)activeChildForNode:(UINavigationController*)node {
return node.visibleViewController;
}
- (UIDBounds*)boundsForNode:(UINavigationController*)node {
return [UIDBounds fromRect:node.view.bounds];
}
@end
#endif

View File

@@ -0,0 +1,31 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#if FB_SONARKIT_ENABLED
#import <UIKit/UIKit.h>
#import "UIDNodeDescriptor.h"
NS_ASSUME_NONNULL_BEGIN
/**
UITabBarController
(https://developer.apple.com/documentation/uikit/uitabbarcontroller?language=objc)
Properties:
- viewControllers: root view controllers displayed by the tab bar user
interface.
- selectedViewController: the view controller associated with the
currently selected tab bar item.
*/
@interface UIDUITabBarControllerDescriptor
: UIDNodeDescriptor<UITabBarController*>
@end
NS_ASSUME_NONNULL_END
#endif

View File

@@ -0,0 +1,28 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#if FB_SONARKIT_ENABLED
#import "UIDUITabBarControllerDescriptor.h"
@implementation UIDUITabBarControllerDescriptor
- (NSArray<id<NSObject>>*)childrenOfNode:(UITabBarController*)node {
return node.viewControllers;
}
- (id<NSObject>)activeChildForNode:(UITabBarController*)node {
return node.selectedViewController;
}
- (UIDBounds*)boundsForNode:(UITabBarController*)node {
return [UIDBounds fromRect:node.view.bounds];
}
@end
#endif

View File

@@ -0,0 +1,21 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#if FB_SONARKIT_ENABLED
#import <UIKit/UIKit.h>
#import "UIDNodeDescriptor.h"
NS_ASSUME_NONNULL_BEGIN
@interface UIDUIViewControllerDescriptor : UIDNodeDescriptor<UIViewController*>
@end
NS_ASSUME_NONNULL_END
#endif

View File

@@ -0,0 +1,30 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#if FB_SONARKIT_ENABLED
#import "UIDUIViewControllerDescriptor.h"
@implementation UIDUIViewControllerDescriptor
- (NSArray<id<NSObject>>*)childrenOfNode:(UIViewController*)node {
if (node.view != nil) {
return [NSArray arrayWithObject:node.view];
}
return [NSArray array];
}
- (UIDBounds*)boundsForNode:(UIViewController*)node {
CGRect bounds = CGRectMake(
0, 0, node.view.bounds.size.width, node.view.bounds.size.height);
return [UIDBounds fromRect:bounds];
}
@end
#endif

View File

@@ -0,0 +1,21 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#if FB_SONARKIT_ENABLED
#import <UIKit/UIKit.h>
#import "UIDChainedDescriptor.h"
NS_ASSUME_NONNULL_BEGIN
@interface UIDUIViewDescriptor : UIDChainedDescriptor<UIView*>
@end
NS_ASSUME_NONNULL_END
#endif

View File

@@ -0,0 +1,34 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#if FB_SONARKIT_ENABLED
#import "UIDUIViewDescriptor.h"
#import "UIView+UIDDescriptor.h"
@implementation UIDUIViewDescriptor
- (void)aggregateAttributes:(UIDMutableAttributes*)attributes
forNode:(UIView*)node {
[node UID_aggregateAttributes:attributes];
}
- (NSArray<id<NSObject>>*)childrenOfNode:(UIView*)node {
return [node UID_children];
}
- (UIImage*)snapshotForNode:(UIView*)node {
return [node UID_snapshot];
}
- (UIDBounds*)boundsForNode:(UIView*)node {
return [node UID_bounds];
}
@end
#endif

View File

@@ -0,0 +1,21 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#if FB_SONARKIT_ENABLED
#import <UIKit/UIKit.h>
#import "UIDChainedDescriptor.h"
NS_ASSUME_NONNULL_BEGIN
@interface UIDUIWindowDescriptor : UIDChainedDescriptor<UIWindow*>
@end
NS_ASSUME_NONNULL_END
#endif

View File

@@ -0,0 +1,25 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#if FB_SONARKIT_ENABLED
#import "UIDUIWindowDescriptor.h"
#import "UIView+UIDDescriptor.h"
@implementation UIDUIWindowDescriptor
- (id<NSObject>)activeChildForNode:(UIWindow*)node {
return [node UID_activeChild];
}
- (UIDBounds*)boundsForNode:(UIWindow*)node {
return [UIDBounds fromRect:node.bounds];
}
@end
#endif

View File

@@ -0,0 +1,23 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#if FB_SONARKIT_ENABLED
#import <FlipperKit/SKMacros.h>
#import <FlipperKitUIDebuggerPlugin/UIDDescriptor.h>
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
FB_LINK_REQUIRE_CATEGORY(UIView_UIDDescriptor)
@interface UIView (UIDDescriptor)<UIDDescriptor>
@end
NS_ASSUME_NONNULL_END
#endif

View File

@@ -0,0 +1,606 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#if FB_SONARKIT_ENABLED
#import "UIDInspectable.h"
#import "UIDMetadata.h"
#import "UIDMetadataRegister.h"
#import "UIDSnapshot.h"
#import "UIView+UIDDescriptor.h"
FB_LINKABLE(UIView_UIDDescriptor)
@implementation UIView (UIDDescriptor)
- (NSUInteger)UID_identifier {
return [self hash];
}
- (nonnull NSString*)UID_name {
return NSStringFromClass([self class]);
}
- (void)UID_aggregateAttributes:(nonnull UIDMutableAttributes*)attributes {
static UIDMetadataId UIViewAttributeId;
static UIDMetadataId FrameAttributeId;
static UIDMetadataId BoundsAttributeId;
static UIDMetadataId CenterAttributeId;
static UIDMetadataId AnchorPointAttributeId;
static UIDMetadataId SafeAreaInsetsAttributeId;
static UIDMetadataId ClipsToBoundsAttributeId;
static UIDMetadataId HiddenAttributeId;
static UIDMetadataId AlphaAttributeId;
static UIDMetadataId OpaqueAttributeId;
static UIDMetadataId ClearContextBeforeDrawingAttributeId;
static UIDMetadataId BackgroundColorAttributeId;
static UIDMetadataId TintColorAttributeId;
static UIDMetadataId TagAttributeId;
static UIDMetadataId CALayerAttributeId;
static UIDMetadataId CALayerShadowColorAttributeId;
static UIDMetadataId CALayerShadowOpacityAttributeId;
static UIDMetadataId CALayerShadowRadiusAttributeId;
static UIDMetadataId CALayerShadowOffsetAttributeId;
static UIDMetadataId CALayerBackgroundColorAttributeId;
static UIDMetadataId CALayerBorderColorAttributeId;
static UIDMetadataId CALayerBorderWidthAttributeId;
static UIDMetadataId CALayerCornerRadiusAttributeId;
static UIDMetadataId CALayerMasksToBoundsAttributeId;
static UIDMetadataId AccessibilityAttributeId;
static UIDMetadataId IsAccessibilityElementAttributeId;
static UIDMetadataId AccessibilityLabelAttributeId;
static UIDMetadataId AccessibilityIdentifierAttributeId;
static UIDMetadataId AccessibilityValueAttributeId;
static UIDMetadataId AccessibilityHintAttributeId;
static UIDMetadataId AccessibilityTraitsAttributeId;
static UIDMetadataId AccessibilityViewIsModalAttributeId;
static UIDMetadataId ShouldGroupAccessibilityChildrenAttributeId;
static UIDMetadataId AccessibilityTraitNoneAttributeId;
static UIDMetadataId AccessibilityTraitButtonAttributeId;
static UIDMetadataId AccessibilityTraitLinkAttributeId;
static UIDMetadataId AccessibilityTraitImageAttributeId;
static UIDMetadataId AccessibilityTraitSearchFieldAttributeId;
static UIDMetadataId AccessibilityTraitKeyboardKeyAttributeId;
static UIDMetadataId AccessibilityTraitStaticTextAttributeId;
static UIDMetadataId AccessibilityTraitHeaderAttributeId;
static UIDMetadataId AccessibilityTraitTabBarAttributeId;
static UIDMetadataId AccessibilityTraitSummaryElementAttributeId;
static UIDMetadataId AccessibilityTraitSelectedAttributeId;
static UIDMetadataId AccessibilityTraitNotEnabledAttributeId;
static UIDMetadataId AccessibilityTraitAdjustableAttributeId;
static UIDMetadataId AccessibilityTraitAllowsDirectInteractionAttributeId;
static UIDMetadataId AccessibilityTraitUpdatesFrequentlyAttributeId;
static UIDMetadataId AccessibilityTraitCausesPageTurnAttributeId;
static UIDMetadataId AccessibilityTraitPlaysSoundAttributeId;
static UIDMetadataId AccessibilityTraitStartsMediaSessionAttributeId;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
UIViewAttributeId = [[UIDMetadataRegister shared]
registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE
name:@"UIView"];
FrameAttributeId = [[UIDMetadataRegister shared]
registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_LAYOUT
name:@"frame"
isMutable:false
definedBy:UIViewAttributeId];
BoundsAttributeId = [[UIDMetadataRegister shared]
registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_LAYOUT
name:@"bounds"
isMutable:false
definedBy:UIViewAttributeId];
CenterAttributeId = [[UIDMetadataRegister shared]
registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_LAYOUT
name:@"center"
isMutable:false
definedBy:UIViewAttributeId];
AnchorPointAttributeId = [[UIDMetadataRegister shared]
registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_LAYOUT
name:@"anchorPoint"
isMutable:false
definedBy:UIViewAttributeId];
SafeAreaInsetsAttributeId = [[UIDMetadataRegister shared]
registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_LAYOUT
name:@"safeAreaInset"
isMutable:false
definedBy:UIViewAttributeId];
ClipsToBoundsAttributeId = [[UIDMetadataRegister shared]
registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_LAYOUT
name:@"clipsToBounds"
isMutable:false
definedBy:UIViewAttributeId];
HiddenAttributeId = [[UIDMetadataRegister shared]
registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE
name:@"hidden"
isMutable:false
definedBy:UIViewAttributeId];
AlphaAttributeId = [[UIDMetadataRegister shared]
registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE
name:@"alpha"
isMutable:false
definedBy:UIViewAttributeId];
OpaqueAttributeId = [[UIDMetadataRegister shared]
registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE
name:@"opaque"
isMutable:false
definedBy:UIViewAttributeId];
ClearContextBeforeDrawingAttributeId = [[UIDMetadataRegister shared]
registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE
name:@"clearContextBeforeDrawing"
isMutable:false
definedBy:UIViewAttributeId];
BackgroundColorAttributeId = [[UIDMetadataRegister shared]
registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE
name:@"backgroundColor"
isMutable:false
definedBy:UIViewAttributeId];
TintColorAttributeId = [[UIDMetadataRegister shared]
registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE
name:@"tintColor"
isMutable:false
definedBy:UIViewAttributeId];
TagAttributeId = [[UIDMetadataRegister shared]
registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE
name:@"tag"
isMutable:false
definedBy:UIViewAttributeId];
CALayerAttributeId = [[UIDMetadataRegister shared]
registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE
name:@"CALayer"];
CALayerShadowColorAttributeId = [[UIDMetadataRegister shared]
registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE
name:@"shadowColor"
isMutable:false
definedBy:CALayerAttributeId];
CALayerShadowOpacityAttributeId = [[UIDMetadataRegister shared]
registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE
name:@"shadowOpacity"
isMutable:false
definedBy:CALayerAttributeId];
CALayerShadowRadiusAttributeId = [[UIDMetadataRegister shared]
registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE
name:@"shadowRadius"
isMutable:false
definedBy:CALayerAttributeId];
CALayerShadowOffsetAttributeId = [[UIDMetadataRegister shared]
registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE
name:@"shadowOffset"
isMutable:false
definedBy:CALayerAttributeId];
CALayerBackgroundColorAttributeId = [[UIDMetadataRegister shared]
registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE
name:@"shadowBackgroundColor"
isMutable:false
definedBy:CALayerAttributeId];
CALayerBorderColorAttributeId = [[UIDMetadataRegister shared]
registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE
name:@"borderColor"
isMutable:false
definedBy:CALayerAttributeId];
CALayerBorderWidthAttributeId = [[UIDMetadataRegister shared]
registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE
name:@"borderWidth"
isMutable:false
definedBy:CALayerAttributeId];
CALayerCornerRadiusAttributeId = [[UIDMetadataRegister shared]
registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE
name:@"cornerRadius"
isMutable:false
definedBy:CALayerAttributeId];
CALayerMasksToBoundsAttributeId = [[UIDMetadataRegister shared]
registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE
name:@"masksToBounds"
isMutable:false
definedBy:CALayerAttributeId];
AccessibilityAttributeId = [[UIDMetadataRegister shared]
registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE
name:@"Accessibility"];
IsAccessibilityElementAttributeId = [[UIDMetadataRegister shared]
registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE
name:@"isAccessibilityElement"
isMutable:false
definedBy:AccessibilityAttributeId];
AccessibilityLabelAttributeId = [[UIDMetadataRegister shared]
registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE
name:@"accessibilityLabel"
isMutable:false
definedBy:AccessibilityAttributeId];
AccessibilityIdentifierAttributeId = [[UIDMetadataRegister shared]
registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE
name:@"accessibilityIdentifier"
isMutable:false
definedBy:AccessibilityAttributeId];
AccessibilityValueAttributeId = [[UIDMetadataRegister shared]
registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE
name:@"accessibilityValue"
isMutable:false
definedBy:AccessibilityAttributeId];
AccessibilityHintAttributeId = [[UIDMetadataRegister shared]
registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE
name:@"accessibilityHint"
isMutable:false
definedBy:AccessibilityAttributeId];
AccessibilityTraitsAttributeId = [[UIDMetadataRegister shared]
registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE
name:@"accessibilityTraits"
isMutable:false
definedBy:AccessibilityAttributeId];
AccessibilityViewIsModalAttributeId = [[UIDMetadataRegister shared]
registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE
name:@"accessibilityViewIsModal"
isMutable:false
definedBy:AccessibilityAttributeId];
ShouldGroupAccessibilityChildrenAttributeId = [[UIDMetadataRegister shared]
registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE
name:@"shouldGroupAccessibilityChildren"
isMutable:false
definedBy:AccessibilityAttributeId];
AccessibilityTraitNoneAttributeId = [[UIDMetadataRegister shared]
registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE
name:@"none"
isMutable:false
definedBy:AccessibilityTraitsAttributeId];
AccessibilityTraitButtonAttributeId = [[UIDMetadataRegister shared]
registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE
name:@"button"
isMutable:false
definedBy:AccessibilityTraitsAttributeId];
AccessibilityTraitLinkAttributeId = [[UIDMetadataRegister shared]
registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE
name:@"link"
isMutable:false
definedBy:AccessibilityTraitsAttributeId];
AccessibilityTraitImageAttributeId = [[UIDMetadataRegister shared]
registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE
name:@"image"
isMutable:false
definedBy:AccessibilityTraitsAttributeId];
AccessibilityTraitSearchFieldAttributeId = [[UIDMetadataRegister shared]
registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE
name:@"searchField"
isMutable:false
definedBy:AccessibilityTraitsAttributeId];
AccessibilityTraitKeyboardKeyAttributeId = [[UIDMetadataRegister shared]
registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE
name:@"keyboardKey"
isMutable:false
definedBy:AccessibilityTraitsAttributeId];
AccessibilityTraitStaticTextAttributeId = [[UIDMetadataRegister shared]
registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE
name:@"staticText"
isMutable:false
definedBy:AccessibilityTraitsAttributeId];
AccessibilityTraitHeaderAttributeId = [[UIDMetadataRegister shared]
registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE
name:@"header"
isMutable:false
definedBy:AccessibilityTraitsAttributeId];
AccessibilityTraitTabBarAttributeId = [[UIDMetadataRegister shared]
registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE
name:@"tabBar"
isMutable:false
definedBy:AccessibilityTraitsAttributeId];
AccessibilityTraitSummaryElementAttributeId = [[UIDMetadataRegister shared]
registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE
name:@"summaryElement"
isMutable:false
definedBy:AccessibilityTraitsAttributeId];
AccessibilityTraitSelectedAttributeId = [[UIDMetadataRegister shared]
registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE
name:@"selected"
isMutable:false
definedBy:AccessibilityTraitsAttributeId];
AccessibilityTraitNotEnabledAttributeId = [[UIDMetadataRegister shared]
registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE
name:@"notEnabled"
isMutable:false
definedBy:AccessibilityTraitsAttributeId];
AccessibilityTraitAdjustableAttributeId = [[UIDMetadataRegister shared]
registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE
name:@"adjustable"
isMutable:false
definedBy:AccessibilityTraitsAttributeId];
AccessibilityTraitAllowsDirectInteractionAttributeId =
[[UIDMetadataRegister shared]
registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE
name:@"directInteraction"
isMutable:false
definedBy:AccessibilityTraitsAttributeId];
AccessibilityTraitUpdatesFrequentlyAttributeId =
[[UIDMetadataRegister shared]
registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE
name:@"updatedFrequently"
isMutable:false
definedBy:AccessibilityTraitsAttributeId];
AccessibilityTraitCausesPageTurnAttributeId = [[UIDMetadataRegister shared]
registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE
name:@"causesPageTurn"
isMutable:false
definedBy:AccessibilityTraitsAttributeId];
AccessibilityTraitPlaysSoundAttributeId = [[UIDMetadataRegister shared]
registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE
name:@"playsSound"
isMutable:false
definedBy:AccessibilityTraitsAttributeId];
AccessibilityTraitStartsMediaSessionAttributeId =
[[UIDMetadataRegister shared]
registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE
name:@"startsMediaSession"
isMutable:false
definedBy:AccessibilityTraitsAttributeId];
});
NSMutableDictionary* viewAttributes = [NSMutableDictionary new];
[viewAttributes setObject:[UIDInspectableBounds fromRect:self.frame]
forKey:FrameAttributeId];
[viewAttributes setObject:[UIDInspectableBounds fromRect:self.bounds]
forKey:BoundsAttributeId];
[viewAttributes setObject:[UIDInspectableCoordinate fromPoint:self.center]
forKey:CenterAttributeId];
if (@available(iOS 16.0, *)) {
[viewAttributes
setObject:[UIDInspectableCoordinate fromPoint:self.anchorPoint]
forKey:AnchorPointAttributeId];
}
[viewAttributes
setObject:[UIDInspectableEdgeInsets fromUIEdgeInsets:self.safeAreaInsets]
forKey:SafeAreaInsetsAttributeId];
[viewAttributes
setObject:[UIDInspectableBoolean fromBoolean:self.clipsToBounds]
forKey:ClipsToBoundsAttributeId];
[viewAttributes setObject:[UIDInspectableBoolean fromBoolean:self.isHidden]
forKey:HiddenAttributeId];
[viewAttributes setObject:[UIDInspectableNumber fromCGFloat:self.alpha]
forKey:AlphaAttributeId];
[viewAttributes setObject:[UIDInspectableBoolean fromBoolean:self.opaque]
forKey:OpaqueAttributeId];
[viewAttributes setObject:[UIDInspectableBoolean
fromBoolean:self.clearsContextBeforeDrawing]
forKey:ClearContextBeforeDrawingAttributeId];
[viewAttributes setObject:[UIDInspectableColor fromColor:self.backgroundColor]
forKey:BackgroundColorAttributeId];
[viewAttributes setObject:[UIDInspectableColor fromColor:self.tintColor]
forKey:TintColorAttributeId];
[viewAttributes setObject:[UIDInspectableNumber fromNumber:@(self.tag)]
forKey:TagAttributeId];
[attributes setObject:[UIDInspectableObject fromFields:viewAttributes]
forKey:UIViewAttributeId];
NSMutableDictionary* layerAttributes = [NSMutableDictionary new];
[layerAttributes
setObject:[UIDInspectableColor
fromColor:[UIColor colorWithCGColor:self.layer.shadowColor]]
forKey:CALayerShadowColorAttributeId];
[layerAttributes
setObject:[UIDInspectableNumber fromCGFloat:self.layer.shadowOpacity]
forKey:CALayerShadowOpacityAttributeId];
[layerAttributes
setObject:[UIDInspectableNumber fromCGFloat:self.layer.shadowRadius]
forKey:CALayerShadowRadiusAttributeId];
[layerAttributes
setObject:[UIDInspectableSize fromSize:self.layer.shadowOffset]
forKey:CALayerShadowOffsetAttributeId];
[layerAttributes
setObject:[UIDInspectableColor
fromColor:[UIColor
colorWithCGColor:self.layer.backgroundColor]]
forKey:CALayerBackgroundColorAttributeId];
[layerAttributes
setObject:[UIDInspectableColor
fromColor:[UIColor colorWithCGColor:self.layer.borderColor]]
forKey:CALayerBorderColorAttributeId];
[layerAttributes
setObject:[UIDInspectableNumber fromCGFloat:self.layer.borderWidth]
forKey:CALayerBorderWidthAttributeId];
[layerAttributes
setObject:[UIDInspectableNumber fromCGFloat:self.layer.cornerRadius]
forKey:CALayerCornerRadiusAttributeId];
[layerAttributes
setObject:[UIDInspectableBoolean fromBoolean:self.layer.masksToBounds]
forKey:CALayerMasksToBoundsAttributeId];
[attributes setObject:[UIDInspectableObject fromFields:layerAttributes]
forKey:CALayerAttributeId];
NSMutableDictionary* accessibilityAttributes = [NSMutableDictionary new];
[accessibilityAttributes
setObject:[UIDInspectableBoolean fromBoolean:self.isAccessibilityElement]
forKey:IsAccessibilityElementAttributeId];
[accessibilityAttributes
setObject:[UIDInspectableText fromText:self.accessibilityLabel]
forKey:AccessibilityLabelAttributeId];
[accessibilityAttributes
setObject:[UIDInspectableText fromText:self.accessibilityIdentifier]
forKey:AccessibilityIdentifierAttributeId];
[accessibilityAttributes
setObject:[UIDInspectableText fromText:self.accessibilityValue]
forKey:AccessibilityValueAttributeId];
[accessibilityAttributes
setObject:[UIDInspectableText fromText:self.accessibilityHint]
forKey:AccessibilityHintAttributeId];
[accessibilityAttributes
setObject:[UIDInspectableBoolean
fromBoolean:self.accessibilityViewIsModal]
forKey:AccessibilityViewIsModalAttributeId];
[accessibilityAttributes
setObject:[UIDInspectableBoolean
fromBoolean:self.shouldGroupAccessibilityChildren]
forKey:ShouldGroupAccessibilityChildrenAttributeId];
NSMutableDictionary* accessibilityTraitsAttributes =
[NSMutableDictionary new];
[accessibilityTraitsAttributes
setObject:[UIDInspectableBoolean fromBoolean:(self.accessibilityTraits &
UIAccessibilityTraitNone)]
forKey:AccessibilityTraitNoneAttributeId];
[accessibilityTraitsAttributes
setObject:[UIDInspectableBoolean fromBoolean:(self.accessibilityTraits &
UIAccessibilityTraitButton)]
forKey:AccessibilityTraitButtonAttributeId];
[accessibilityTraitsAttributes
setObject:[UIDInspectableBoolean fromBoolean:(self.accessibilityTraits &
UIAccessibilityTraitLink)]
forKey:AccessibilityTraitLinkAttributeId];
[accessibilityTraitsAttributes
setObject:[UIDInspectableBoolean fromBoolean:(self.accessibilityTraits &
UIAccessibilityTraitImage)]
forKey:AccessibilityTraitImageAttributeId];
[accessibilityTraitsAttributes
setObject:[UIDInspectableBoolean
fromBoolean:(self.accessibilityTraits &
UIAccessibilityTraitSearchField)]
forKey:AccessibilityTraitSearchFieldAttributeId];
[accessibilityTraitsAttributes
setObject:[UIDInspectableBoolean
fromBoolean:(self.accessibilityTraits &
UIAccessibilityTraitKeyboardKey)]
forKey:AccessibilityTraitKeyboardKeyAttributeId];
[accessibilityTraitsAttributes
setObject:[UIDInspectableBoolean
fromBoolean:(self.accessibilityTraits &
UIAccessibilityTraitStaticText)]
forKey:AccessibilityTraitStaticTextAttributeId];
[accessibilityTraitsAttributes
setObject:[UIDInspectableBoolean fromBoolean:(self.accessibilityTraits &
UIAccessibilityTraitHeader)]
forKey:AccessibilityTraitHeaderAttributeId];
[accessibilityTraitsAttributes
setObject:[UIDInspectableBoolean fromBoolean:(self.accessibilityTraits &
UIAccessibilityTraitTabBar)]
forKey:AccessibilityTraitTabBarAttributeId];
[accessibilityTraitsAttributes
setObject:[UIDInspectableBoolean
fromBoolean:(self.accessibilityTraits &
UIAccessibilityTraitSummaryElement)]
forKey:AccessibilityTraitSummaryElementAttributeId];
[accessibilityTraitsAttributes
setObject:[UIDInspectableBoolean
fromBoolean:(self.accessibilityTraits &
UIAccessibilityTraitSelected)]
forKey:AccessibilityTraitSelectedAttributeId];
[accessibilityTraitsAttributes
setObject:[UIDInspectableBoolean
fromBoolean:(self.accessibilityTraits &
UIAccessibilityTraitNotEnabled)]
forKey:AccessibilityTraitNotEnabledAttributeId];
[accessibilityTraitsAttributes
setObject:[UIDInspectableBoolean
fromBoolean:(self.accessibilityTraits &
UIAccessibilityTraitAdjustable)]
forKey:AccessibilityTraitAdjustableAttributeId];
[accessibilityTraitsAttributes
setObject:[UIDInspectableBoolean
fromBoolean:(self.accessibilityTraits &
UIAccessibilityTraitAllowsDirectInteraction)]
forKey:AccessibilityTraitAllowsDirectInteractionAttributeId];
[accessibilityTraitsAttributes
setObject:[UIDInspectableBoolean
fromBoolean:(self.accessibilityTraits &
UIAccessibilityTraitUpdatesFrequently)]
forKey:AccessibilityTraitUpdatesFrequentlyAttributeId];
[accessibilityTraitsAttributes
setObject:[UIDInspectableBoolean
fromBoolean:(self.accessibilityTraits &
UIAccessibilityTraitCausesPageTurn)]
forKey:AccessibilityTraitCausesPageTurnAttributeId];
[accessibilityTraitsAttributes
setObject:[UIDInspectableBoolean
fromBoolean:(self.accessibilityTraits &
UIAccessibilityTraitPlaysSound)]
forKey:AccessibilityTraitPlaysSoundAttributeId];
[accessibilityTraitsAttributes
setObject:[UIDInspectableBoolean
fromBoolean:(self.accessibilityTraits &
UIAccessibilityTraitStartsMediaSession)]
forKey:AccessibilityTraitStartsMediaSessionAttributeId];
[accessibilityAttributes
setObject:[UIDInspectableObject fromFields:accessibilityTraitsAttributes]
forKey:AccessibilityTraitsAttributeId];
[attributes
setObject:[UIDInspectableObject fromFields:accessibilityAttributes]
forKey:AccessibilityAttributeId];
}
- (NSArray<id<NSObject>>*)UID_children {
NSMutableArray* children = [NSMutableArray new];
// Use UIViewControllers for children which responds to a different
// view controller than their parent.
for (UIView* child in self.subviews) {
BOOL isController =
[child.nextResponder isKindOfClass:[UIViewController class]];
if (!child.isHidden) {
if (isController && child.nextResponder != self.nextResponder) {
[children addObject:child.nextResponder];
} else {
[children addObject:child];
}
}
}
return children;
}
/**
In the context of UIView, the active child is defined as the last view in
the subviews collection. If said item's next responder is a UIViewController,
then return this instead.
*/
- (id<NSObject>)UID_activeChild {
if (self.subviews && self.subviews.count > 0) {
UIView* activeChild = [self.subviews lastObject];
BOOL isController =
[activeChild.nextResponder isKindOfClass:[UIViewController class]];
if (!activeChild.isHidden) {
if (isController && activeChild.nextResponder != self.nextResponder) {
return activeChild.nextResponder;
}
return activeChild;
}
}
return nil;
}
- (UIImage*)UID_snapshot {
return UIDViewSnapshot(self);
}
- (UIDBounds*)UID_bounds {
if ([self.superview isKindOfClass:[UIScrollView class]]) {
UIScrollView* parent = (UIScrollView*)self.superview;
CGFloat x = self.frame.origin.x - parent.contentOffset.x;
CGFloat y = self.frame.origin.y - parent.contentOffset.y;
CGFloat width = self.frame.size.width;
CGFloat height = self.frame.size.height;
return [UIDBounds fromRect:CGRectMake(x, y, width, height)];
}
return [UIDBounds fromRect:self.frame];
}
@end
#endif

View File

@@ -0,0 +1,30 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#if FB_SONARKIT_ENABLED
#import <Foundation/Foundation.h>
#import "UIDTraversalMode.h"
NS_ASSUME_NONNULL_BEGIN
@class UIDFrameworkEventMetadata;
@interface UIDInitEvent : NSObject
@property(nonatomic) NSUInteger rootId;
@property(nonatomic) UIDTraversalMode currentTraversalMode;
@property(nonatomic, strong)
NSArray<UIDFrameworkEventMetadata*>* frameworkEventMetadata;
+ (NSString*)name;
@end
NS_ASSUME_NONNULL_END
#endif

View File

@@ -0,0 +1,20 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#if FB_SONARKIT_ENABLED
#import "UIDInitEvent.h"
@implementation UIDInitEvent
+ (NSString*)name {
return @"init";
}
@end
#endif

View File

@@ -0,0 +1,27 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#if FB_SONARKIT_ENABLED
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@class UIDMetadata;
@interface UIDMetadataUpdateEvent : NSObject
@property(nonatomic, strong)
NSDictionary<NSNumber*, UIDMetadata*>* attributeMetadata;
+ (NSString*)name;
@end
NS_ASSUME_NONNULL_END
#endif

View File

@@ -0,0 +1,20 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#if FB_SONARKIT_ENABLED
#import "UIDMetadataUpdateEvent.h"
@implementation UIDMetadataUpdateEvent
+ (NSString*)name {
return @"metadataUpdate";
}
@end
#endif

View File

@@ -0,0 +1,38 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#if FB_SONARKIT_ENABLED
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface UIDPerfStatsEvent : NSObject
@property(nonatomic) double txId;
@property(nonatomic, strong) NSString* observerType;
@property(nonatomic) NSUInteger nodesCount;
@property(nonatomic) long start;
@property(nonatomic) long traversalMS;
@property(nonatomic) long snapshotMS;
@property(nonatomic) long queuingMS;
@property(nonatomic) long deferredComputationMS;
@property(nonatomic) long serializationMS;
@property(nonatomic) long socketMS;
@property(nonatomic) long frameworkEventsMS;
@property(nonatomic) long payloadSize;
@property(nonatomic) NSUInteger eventsCount;
@property(nonatomic) NSDictionary<NSString*, NSNumber*>* dynamicMeasures;
+ (NSString*)name;
@end
NS_ASSUME_NONNULL_END
#endif

View File

@@ -0,0 +1,20 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#if FB_SONARKIT_ENABLED
#import "UIDPerfStatsEvent.h"
@implementation UIDPerfStatsEvent
+ (NSString*)name {
return @"performanceStats";
}
@end
#endif

View File

@@ -0,0 +1,31 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#if FB_SONARKIT_ENABLED
#import <UIKit/UIKit.h>
#import "UIDFrameworkEvent.h"
#import "UIDNode.h"
NS_ASSUME_NONNULL_BEGIN
@interface UIDSubtreeUpdateEvent : NSObject
@property(nonatomic) double txId;
@property(nonatomic, strong) NSString* observerType;
@property(nonatomic) NSUInteger rootId;
@property(nonatomic, strong) NSArray<UIDNode*>* nodes;
@property(nonatomic, strong) UIImage* snapshot;
@property(nonatomic, strong) NSArray<UIDFrameworkEvent*>* frameworkEvents;
+ (NSString*)name;
@end
NS_ASSUME_NONNULL_END
#endif

View File

@@ -0,0 +1,20 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#if FB_SONARKIT_ENABLED
#import "UIDSubtreeUpdateEvent.h"
@implementation UIDSubtreeUpdateEvent
+ (NSString*)name {
return @"subtreeUpdate";
}
@end
#endif

View File

@@ -0,0 +1,27 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#if FB_SONARKIT_ENABLED
#import <FlipperKit/FlipperPlugin.h>
#import <Foundation/Foundation.h>
@class FlipperClient;
@interface FlipperKitUIDebuggerPlugin : NSObject<FlipperPlugin>
@end
#ifdef __cplusplus
extern "C" {
#endif
void FlipperKitUIDebuggerAddPlugin(FlipperClient*);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,75 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#if FB_SONARKIT_ENABLED
#import "FlipperKitUIDebuggerPlugin.h"
#import <FlipperKit/FlipperClient.h>
#import <FlipperKit/FlipperConnection.h>
#import <FlipperKit/FlipperResponder.h>
#import <UIKit/UIKit.h>
#import "Core/UIDContext.h"
#import "Descriptors/UIDDescriptorRegister.h"
#import "Observer/UIDTreeObserverFactory.h"
#import "Observer/UIDTreeObserverManager.h"
#import "UIDTraversalMode.h"
@implementation FlipperKitUIDebuggerPlugin {
UIDContext* _context;
}
- (instancetype)initWithContext:(UIDContext*)context {
if (self = [super init]) {
self->_context = context;
}
return self;
}
- (instancetype)init {
UIDContext* context = [[UIDContext alloc]
initWithApplication:[UIApplication sharedApplication]
descriptorRegister:[UIDDescriptorRegister defaultRegister]
observerFactory:[UIDTreeObserverFactory shared]];
return [self initWithContext:context];
}
- (NSString*)identifier {
return @"ui-debugger";
}
- (void)didConnect:(id<FlipperConnection>)connection {
if (!_context.application) {
_context.application = [UIApplication sharedApplication];
}
_context.connection = connection;
[[UIDTreeObserverManager shared] startWithContext:_context];
NSSet<id<UIDConnectionListener>>* connectionListeners =
_context.connectionListeners;
for (id<UIDConnectionListener> listener in connectionListeners) {
[listener onDidConnect];
}
}
- (void)didDisconnect {
_context.connection = nil;
[[UIDTreeObserverManager shared] stop];
NSSet<id<UIDConnectionListener>>* connectionListeners =
_context.connectionListeners;
for (id<UIDConnectionListener> listener in connectionListeners) {
[listener onDidDisconnect];
}
}
@end
#endif

View File

@@ -0,0 +1,37 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#if FB_SONARKIT_ENABLED
#import <FlipperKit/FlipperClient.h>
#import "FlipperKitUIDebuggerPlugin.h"
#import "UIDContext.h"
#import "UIDDescriptorRegister.h"
#import "UIDTreeObserverFactory.h"
@interface FlipperKitUIDebuggerPlugin ()
- (instancetype)initWithContext:(UIDContext*)context;
@end
#ifdef __cplusplus
extern "C" {
#endif
void FlipperKitUIDebuggerAddPlugin(FlipperClient* client) {
UIDContext* context = [[UIDContext alloc]
initWithApplication:[UIApplication sharedApplication]
descriptorRegister:[UIDDescriptorRegister defaultRegister]
observerFactory:[UIDTreeObserverFactory shared]];
FlipperKitUIDebuggerPlugin* plugin =
[[FlipperKitUIDebuggerPlugin alloc] initWithContext:context];
[client addPlugin:plugin];
}
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,32 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#if FB_SONARKIT_ENABLED
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface UIDBounds : NSObject
@property(nonatomic) NSInteger x;
@property(nonatomic) NSInteger y;
@property(nonatomic) NSInteger width;
@property(nonatomic) NSInteger height;
- (instancetype)initWithX:(NSInteger)x
y:(NSInteger)y
width:(NSInteger)width
height:(NSInteger)height;
+ (instancetype)fromRect:(CGRect)rect;
@end
NS_ASSUME_NONNULL_END
#endif

View File

@@ -0,0 +1,37 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#if FB_SONARKIT_ENABLED
#import "UIDBounds.h"
@implementation UIDBounds
- (instancetype)initWithX:(NSInteger)x
y:(NSInteger)y
width:(NSInteger)width
height:(NSInteger)height {
self = [super init];
if (self) {
self.x = x;
self.y = y;
self.width = width;
self.height = height;
}
return self;
}
+ (instancetype)fromRect:(CGRect)rect {
return [[UIDBounds alloc] initWithX:rect.origin.x
y:rect.origin.y
width:rect.size.width
height:rect.size.height];
}
@end
#endif

View File

@@ -0,0 +1,32 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#if FB_SONARKIT_ENABLED
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface UIDEdgeInsets : NSObject
@property(nonatomic) CGFloat top;
@property(nonatomic) CGFloat right;
@property(nonatomic) CGFloat bottom;
@property(nonatomic) CGFloat left;
- (instancetype)initWithTop:(CGFloat)top
right:(CGFloat)right
bottom:(CGFloat)bottom
left:(CGFloat)left;
+ (instancetype)fromUIEdgeInsets:(UIEdgeInsets)edgeInsets;
@end
NS_ASSUME_NONNULL_END
#endif

View File

@@ -0,0 +1,37 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#if FB_SONARKIT_ENABLED
#import "UIDEdgeInsets.h"
@implementation UIDEdgeInsets
- (instancetype)initWithTop:(CGFloat)top
right:(CGFloat)right
bottom:(CGFloat)bottom
left:(CGFloat)left {
self = [super init];
if (self) {
_top = top;
_right = right;
_bottom = bottom;
_left = left;
}
return self;
}
+ (instancetype)fromUIEdgeInsets:(UIEdgeInsets)edgeInsets {
return [[UIDEdgeInsets alloc] initWithTop:edgeInsets.top
right:edgeInsets.right
bottom:edgeInsets.bottom
left:edgeInsets.left];
}
@end
#endif

View File

@@ -0,0 +1,29 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#if FB_SONARKIT_ENABLED
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
typedef NSDictionary<NSString*, id>* UIDEventPayload;
typedef NSArray<NSString*>* UIDStacktrace;
@interface UIDFrameworkEvent : NSObject
@property(nonatomic) NSUInteger nodeIdentifier;
@property(nonatomic, strong) NSString* type;
@property(nonatomic, strong) NSDate* timestamp;
@property(nonatomic, strong) UIDEventPayload payload;
@property(nonatomic, strong) UIDStacktrace stacktrace;
@end
NS_ASSUME_NONNULL_END
#endif

View File

@@ -0,0 +1,16 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#if FB_SONARKIT_ENABLED
#import "UIDFrameworkEvent.h"
@implementation UIDFrameworkEvent
@end
#endif

View File

@@ -0,0 +1,30 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#if FB_SONARKIT_ENABLED
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface UIDFrameworkEventMetadata : NSObject
@property(nonatomic, strong) NSString* type;
@property(nonatomic, strong) NSString* documentation;
+ (instancetype)newWithType:(NSString*)type
documentation:(NSString*)documentation;
+ (instancetype)new NS_UNAVAILABLE;
- (instancetype)init NS_UNAVAILABLE;
@end
NS_ASSUME_NONNULL_END
#endif

View File

@@ -0,0 +1,30 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#if FB_SONARKIT_ENABLED
#import "UIDFrameworkEventMetadata.h"
@implementation UIDFrameworkEventMetadata
- (instancetype)initWithType:(NSString*)type
documentation:(NSString*)documentation {
if (self = [super init]) {
self->_type = type;
self->_documentation = documentation;
}
return self;
}
+ (instancetype)newWithType:(NSString*)type
documentation:(NSString*)documentation {
return [[self alloc] initWithType:type documentation:documentation];
}
@end
#endif

View File

@@ -0,0 +1,175 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#if FB_SONARKIT_ENABLED
#import <UIKit/UIKit.h>
#import "UIDFoundation.h"
NS_ASSUME_NONNULL_BEGIN
@interface UIDInspectable : NSObject
@end
/**
Lazy inspectables can be used to defer materialisation
of the inspectable until a later stage, like for example,
during serialisation.
*/
@interface UIDLazyInspectable : UIDInspectable
- (UIDInspectable*)value;
+ (instancetype)from:(UIDInspectable* (^)(void))loader;
@end
@interface UIDInspectableValue : UIDInspectable
+ (instancetype)createWithText:(NSString*)text;
+ (instancetype)createWithBoolean:(bool)boolean;
+ (instancetype)createWithNumber:(NSNumber*)number;
@end
@interface UIDInspectableObject : UIDInspectable
@property(nonatomic, strong, readonly)
NSDictionary<NSNumber*, UIDInspectable*>* fields;
- (instancetype)initWithFields:
(NSDictionary<NSNumber*, UIDInspectable*>*)fields;
+ (instancetype)fromFields:(NSDictionary<NSNumber*, UIDInspectable*>*)fields;
@end
@interface UIDInspectableArray : UIDInspectable
@property(nonatomic, strong, readonly) NSArray<UIDInspectable*>* items;
- (instancetype)initWithItems:(NSArray<UIDInspectable*>*)items;
+ (instancetype)fromItems:(NSArray<UIDInspectable*>*)items;
@end
@interface UIDInspectableText : UIDInspectableValue
@property(nonatomic, strong, readonly) NSString* value;
- (instancetype)initWithValue:(NSString*)value;
+ (instancetype)fromText:(NSString*)value;
@end
@interface UIDInspectableBoolean : UIDInspectableValue
@property(nonatomic, readonly) bool value;
- (instancetype)initWithValue:(bool)value;
+ (instancetype)fromBoolean:(bool)value;
@end
@interface UIDInspectableNumber : UIDInspectableValue
@property(nonatomic, strong, readonly) NSNumber* value;
- (instancetype)initWithValue:(NSNumber*)value;
+ (instancetype)fromNumber:(NSNumber*)value;
+ (instancetype)fromCGFloat:(CGFloat)value;
@end
@class UIDBounds;
@interface UIDInspectableBounds : UIDInspectableValue
@property(nonatomic, strong, readonly) UIDBounds* value;
- (instancetype)initWithValue:(UIDBounds*)value;
+ (instancetype)fromBounds:(UIDBounds*)value;
+ (instancetype)fromRect:(CGRect)rect;
@end
@interface UIDInspectableCoordinate : UIDInspectableValue
@property(nonatomic, readonly) CGPoint value;
- (instancetype)initWithValue:(CGPoint)value;
+ (instancetype)fromPoint:(CGPoint)value;
@end
@interface UIDInspectableSize : UIDInspectableValue
@property(nonatomic, readonly) CGSize value;
- (instancetype)initWithValue:(CGSize)value;
+ (instancetype)fromSize:(CGSize)value;
@end
@interface UIDInspectableUnknown : UIDInspectableValue
@property(nonatomic, readonly) NSString* value;
- (instancetype)initWithValue:(NSString*)value;
+ (instancetype)unknown;
+ (instancetype)undefined;
+ (instancetype)null;
@end
@class UIDEdgeInsets;
@interface UIDInspectableEdgeInsets : UIDInspectableValue
@property(nonatomic, readonly) UIDEdgeInsets* value;
- (instancetype)initWithValue:(UIDEdgeInsets*)value;
+ (instancetype)fromEdgeInsets:(UIDEdgeInsets*)value;
+ (instancetype)fromUIEdgeInsets:(UIEdgeInsets)value;
@end
@interface UIDInspectableColor : UIDInspectableValue
@property(nonatomic, strong, readonly) UIColor* value;
- (instancetype)initWithValue:(UIColor*)value;
+ (instancetype)fromColor:(UIColor*)value;
@end
@interface UIDInspectableEnum : UIDInspectableValue
@property(nonatomic, readonly) NSString* value;
- (instancetype)initWithValue:(NSString*)value;
+ (instancetype)from:(NSString*)value;
@end
typedef NSDictionary<NSNumber*, UIDInspectable*> UIDAttributes;
typedef NSMutableDictionary<NSNumber*, UIDInspectable*> UIDMutableAttributes;
typedef NSDictionary<NSString*, NSString*> UIDInlineAttributes;
typedef NSMutableDictionary<NSString*, NSString*> UIDMutableInlineAttributes;
typedef NSDictionary<NSString*, id<UIDFoundation>> UIDGenericAttributes;
NS_ASSUME_NONNULL_END
#endif

View File

@@ -0,0 +1,275 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#if FB_SONARKIT_ENABLED
#import "UIDInspectable.h"
#import "UIDBounds.h"
#import "UIDEdgeInsets.h"
@implementation UIDInspectable
@end
@interface UIDLazyInspectable () {
UIDInspectable* _value;
UIDInspectable* (^_loader)(void);
}
@end
@implementation UIDLazyInspectable
- (instancetype)initWithLoader:(UIDInspectable* (^)(void))loader {
if (self = [super init]) {
self->_value = nil;
self->_loader = loader;
}
return self;
}
- (UIDInspectable*)value {
if (!_value && _loader) {
_value = _loader();
}
return _value;
}
+ (instancetype)from:(UIDInspectable* (^)(void))loader {
return [[self alloc] initWithLoader:loader];
}
@end
@implementation UIDInspectableObject
- (instancetype)initWithFields:
(NSDictionary<NSNumber*, UIDInspectable*>*)fields {
self = [super init];
if (self) {
_fields = fields;
}
return self;
}
+ (instancetype)fromFields:(NSDictionary<NSNumber*, UIDInspectable*>*)fields {
return [[UIDInspectableObject alloc] initWithFields:fields];
}
@end
@implementation UIDInspectableArray
- (instancetype)initWithItems:(NSArray<UIDInspectable*>*)items {
self = [super init];
if (self) {
_items = items;
}
return self;
}
+ (instancetype)fromItems:(NSArray<UIDInspectable*>*)items {
return [[UIDInspectableArray alloc] initWithItems:items];
}
@end
@implementation UIDInspectableValue
+ (instancetype)createWithText:(NSString*)text {
return [[UIDInspectableText alloc] initWithValue:text];
}
+ (instancetype)createWithBoolean:(bool)boolean {
return [[UIDInspectableBoolean alloc] initWithValue:boolean];
}
+ (instancetype)createWithNumber:(NSNumber*)number {
return [[UIDInspectableNumber alloc] initWithValue:number];
}
@end
@implementation UIDInspectableText
- (instancetype)initWithValue:(NSString*)value {
self = [super init];
if (self) {
_value = value;
}
return self;
}
+ (instancetype)fromText:(NSString*)value {
return [[UIDInspectableText alloc] initWithValue:value];
}
@end
@implementation UIDInspectableBoolean
- (instancetype)initWithValue:(bool)value {
self = [super init];
if (self) {
_value = value;
}
return self;
}
+ (instancetype)fromBoolean:(bool)value {
return [[UIDInspectableBoolean alloc] initWithValue:value];
}
@end
@implementation UIDInspectableNumber
- (instancetype)initWithValue:(NSNumber*)value {
self = [super init];
if (self) {
_value = value;
}
return self;
}
+ (instancetype)fromNumber:(NSNumber*)value {
return [[UIDInspectableNumber alloc] initWithValue:value];
}
+ (instancetype)fromCGFloat:(CGFloat)value {
return [self fromNumber:[NSNumber numberWithFloat:value]];
}
@end
@implementation UIDInspectableBounds
- (instancetype)initWithValue:(UIDBounds*)value {
self = [super init];
if (self) {
_value = value;
}
return self;
}
+ (instancetype)fromBounds:(UIDBounds*)bounds {
return [[UIDInspectableBounds alloc] initWithValue:bounds];
}
+ (instancetype)fromRect:(CGRect)rect {
return [self fromBounds:[UIDBounds fromRect:rect]];
}
@end
@implementation UIDInspectableCoordinate
- (instancetype)initWithValue:(CGPoint)value {
self = [super init];
if (self) {
_value = value;
}
return self;
}
+ (instancetype)fromPoint:(CGPoint)value {
return [[UIDInspectableCoordinate alloc] initWithValue:value];
}
@end
@implementation UIDInspectableSize
- (instancetype)initWithValue:(CGSize)value {
self = [super init];
if (self) {
_value = value;
}
return self;
}
+ (instancetype)fromSize:(CGSize)value {
return [[UIDInspectableSize alloc] initWithValue:value];
}
@end
@implementation UIDInspectableEdgeInsets
- (instancetype)initWithValue:(UIDEdgeInsets*)value {
self = [super init];
if (self) {
_value = value;
}
return self;
}
+ (instancetype)fromEdgeInsets:(UIDEdgeInsets*)value {
return [[UIDInspectableEdgeInsets alloc] initWithValue:value];
}
+ (instancetype)fromUIEdgeInsets:(UIEdgeInsets)value {
return [self fromEdgeInsets:[UIDEdgeInsets fromUIEdgeInsets:value]];
}
@end
@implementation UIDInspectableColor
- (instancetype)initWithValue:(UIColor*)value {
self = [super init];
if (self) {
_value = value;
}
return self;
}
+ (instancetype)fromColor:(UIColor*)value {
return [[UIDInspectableColor alloc] initWithValue:value];
}
@end
@implementation UIDInspectableUnknown
- (instancetype)initWithValue:(NSString*)value {
self = [super init];
if (self) {
_value = value;
}
return self;
}
+ (instancetype)unknown {
return [[UIDInspectableUnknown alloc] initWithValue:@"unknown"];
}
+ (instancetype)undefined {
return [[UIDInspectableUnknown alloc] initWithValue:@"undefined"];
}
+ (instancetype)null {
return [[UIDInspectableUnknown alloc] initWithValue:@"null"];
}
@end
@implementation UIDInspectableEnum
- (instancetype)initWithValue:(NSString*)value {
self = [super init];
if (self) {
_value = value;
}
return self;
}
+ (instancetype)from:(NSString*)value {
return [[UIDInspectableEnum alloc] initWithValue:value ?: @"UNKNOWN"];
}
@end
#endif

View File

@@ -0,0 +1,57 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#if FB_SONARKIT_ENABLED
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
typedef NSNumber* UIDMetadataId;
@class UIDInspectableValue;
/**
Represent metadata associated for an attribute. Metadata identifier is a
unique identifier used by attributes to refer to its metadata. Type refers to
attribute semantics. It can represent: identity, attributes, layout,
documentation, or a custom type.
*/
@interface UIDMetadata : NSObject
#pragma mark - Included in the wire protocol
@property(nonatomic, readonly) UIDMetadataId identifier;
@property(nonatomic, strong, readonly) NSString* type;
@property(nonatomic, strong, readonly) NSString* name;
@property(nonatomic, readonly) bool isMutable;
@property(nonatomic, readonly, nullable)
NSDictionary<NSString*, id>* customAttributes;
#pragma mark - Not included in the wire protocol
@property(nonatomic, readonly) UIDMetadataId parent;
@property(nonatomic, strong, readonly, nullable)
NSSet<UIDInspectableValue*>* possibleValues;
@property(nonatomic, strong, readonly, nullable) NSSet<NSString*>* tags;
#pragma mark -
- (instancetype)
initWithIdentifier:(UIDMetadataId)identifier
type:(NSString*)type
name:(NSString*)name
isMutable:(bool)isMutable
parent:(UIDMetadataId)parent
possibleValues:(nullable NSSet<UIDInspectableValue*>*)possibleValues
tags:(nullable NSSet<NSString*>*)tags
customAttributes:(nullable NSDictionary<NSString*, id>*)customAttributes;
@end
NS_ASSUME_NONNULL_END
#endif

View File

@@ -0,0 +1,40 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#if FB_SONARKIT_ENABLED
#import "UIDMetadata.h"
#import "UIDInspectable.h"
@implementation UIDMetadata
- (instancetype)initWithIdentifier:(UIDMetadataId)identifier
type:(NSString*)type
name:(NSString*)name
isMutable:(bool)isMutable
parent:(UIDMetadataId)parent
possibleValues:(NSSet<UIDInspectableValue*>*)possibleValues
tags:(NSSet<NSString*>*)tags
customAttributes:
(nullable NSDictionary<NSString*, id>*)customAttributes {
self = [super init];
if (self) {
_identifier = identifier;
_type = type;
_name = name;
_isMutable = isMutable;
_parent = parent ?: @0;
_possibleValues = possibleValues ?: [NSSet set];
_tags = tags ?: [NSSet set];
_customAttributes = customAttributes;
}
return self;
}
@end
#endif

View File

@@ -0,0 +1,41 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#if FB_SONARKIT_ENABLED
#import <Foundation/Foundation.h>
#import "UIDBounds.h"
#import "UIDInspectable.h"
#import "UIDMetadata.h"
NS_ASSUME_NONNULL_BEGIN
@interface UIDNode : NSObject
@property(nonatomic) NSUInteger identifier;
@property(nonatomic, strong) NSString* qualifiedName;
@property(nonatomic, strong) NSString* name;
@property(nonatomic, strong) UIDBounds* bounds;
@property(nonatomic, strong) NSSet<NSString*>* tags;
@property(nonatomic, strong) UIDAttributes* attributes;
@property(nonatomic, strong) UIDInlineAttributes* inlineAttributes;
@property(nonatomic, strong) UIDGenericAttributes* hiddenAttributes;
@property(nonatomic, nullable) NSNumber* parent;
@property(nonatomic, strong) NSArray<NSNumber*>* children;
@property(nonatomic) NSNumber* activeChild;
- (instancetype)initWithIdentifier:(NSUInteger)identifier
qualifiedName:(NSString*)qualifiedName
name:(NSString*)name
bounds:(UIDBounds*)bounds
tags:(NSSet<NSString*>*)tags;
@end
NS_ASSUME_NONNULL_END
#endif

View File

@@ -0,0 +1,36 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#if FB_SONARKIT_ENABLED
#import "UIDNode.h"
@implementation UIDNode
- (instancetype)initWithIdentifier:(NSUInteger)identifier
qualifiedName:(NSString*)qualifiedName
name:(NSString*)name
bounds:(UIDBounds*)bounds
tags:(NSSet<NSString*>*)tags {
self = [super init];
if (self) {
self.identifier = identifier;
self.qualifiedName = qualifiedName;
self.name = name;
self.bounds = bounds;
self.tags = tags;
self.parent = nil;
self.children = [NSArray array];
self.attributes = [NSDictionary dictionary];
}
return self;
}
@end
#endif

View File

@@ -0,0 +1,30 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#if FB_SONARKIT_ENABLED
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
typedef NS_CLOSED_ENUM(NSInteger, UIDTraversalMode) {
UIDTraversalModeViewHierarchy,
UIDTraversalModeAccessibilityHierarchy,
};
#ifdef __cplusplus
extern "C" {
#endif
UIDTraversalMode UIDTraversalModeFromString(NSString* string);
NSString* NSStringFromUIDTraversalMode(UIDTraversalMode mode);
#ifdef __cplusplus
}
#endif
NS_ASSUME_NONNULL_END
#endif

Some files were not shown because too many files have changed in this diff Show More