feat(smart-app): implement complete mobile app MVP

- App.tsx: full navigation (Auth stack + Main tabs with 5 screens)
- Auth: LoginScreen, RegisterScreen, ForgotPasswordScreen
- HomeScreen: dashboard with IoT metrics, weather widget, alerts, quick actions, sensors
- MapScreen: interactive map with layer toggles (6 layers)
- MarketplaceScreen: categories (6), products (5), search
- ChatScreen: AI chat with quick prompts (4), bot responses
- ProfileScreen: user info, stats, menu (9 items), logout
- AlertsScreen: alert list with severity, acknowledge
- SensorsScreen: sensor list with type filters (6 types), search
- ZonesScreen: zone cards with stats
- SettingsScreen: language picker (FR/EN/ES/DE), privacy, about
- Stores: iotStore (sensors, zones, alerts), notificationStore, uiStore + i18n
- Hooks: useSensors, useAlerts, useNotifications, useLocation
- Components: Card, Button, LoadingSpinner, ErrorBoundary, Header
- Services: iotService, notificationService (with axios API client)
- Utils: formatters (temp, AQI, noise, dates), validators (email, password, IBAN)
- Theme: colors.ts with full design system (Blue Ocean palette)
- Ditto: fixed MongoDB connection, new JWT secrets, official gateway image
This commit is contained in:
Eric FELIXINE
2026-06-01 18:00:35 -04:00
parent 08ca495bde
commit e30ae8ed09
35578 changed files with 3703534 additions and 43 deletions

View File

@@ -0,0 +1,18 @@
/*
* 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 <UIKit/UIKit.h>
@class RCTLoadingProgress;
@protocol RCTDevLoadingViewProtocol <NSObject>
+ (void)setEnabled:(BOOL)enabled;
- (void)showMessage:(NSString *)message color:(UIColor *)color backgroundColor:(UIColor *)backgroundColor;
- (void)showWithURL:(NSURL *)URL;
- (void)updateProgress:(RCTLoadingProgress *)progress;
- (void)hide;
@end

View File

@@ -0,0 +1,11 @@
/*
* 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 <React/RCTDefines.h>
RCT_EXTERN void RCTDevLoadingViewSetEnabled(BOOL enabled);
RCT_EXTERN BOOL RCTDevLoadingViewGetEnabled(void);

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 "RCTDevLoadingViewSetEnabled.h"
#if RCT_DEV_MENU
static BOOL isDevLoadingViewEnabled = YES;
#else
static BOOL isDevLoadingViewEnabled = NO;
#endif
void RCTDevLoadingViewSetEnabled(BOOL enabled)
{
isDevLoadingViewEnabled = enabled;
}
BOOL RCTDevLoadingViewGetEnabled(void)
{
return isDevLoadingViewEnabled;
}

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>
#import <UIKit/UIKit.h>
#import <React/RCTDefines.h>
#import <React/RCTInspectorPackagerConnection.h>
#if RCT_DEV || RCT_REMOTE_PROFILE
@interface RCTInspectorDevServerHelper : NSObject
+ (id<RCTInspectorPackagerConnectionProtocol>)connectWithBundleURL:(NSURL *)bundleURL;
+ (void)disableDebugger;
+ (BOOL)isPackagerDisconnected;
+ (void)openDebugger:(NSURL *)bundleURL withErrorMessage:(NSString *)errorMessage;
@end
#endif

View File

@@ -0,0 +1,194 @@
/*
* 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 <React/RCTInspectorDevServerHelper.h>
#if RCT_DEV || RCT_REMOTE_PROFILE
#import <React/RCTLog.h>
#import <UIKit/UIKit.h>
#import <React/RCTCxxInspectorPackagerConnection.h>
#import <React/RCTDefines.h>
#import <React/RCTInspectorPackagerConnection.h>
#import <CommonCrypto/CommonCrypto.h>
#import <jsinspector-modern/InspectorFlags.h>
static NSString *const kDebuggerMsgDisable = @"{ \"id\":1,\"method\":\"Debugger.disable\" }";
static NSString *getServerHost(NSURL *bundleURL)
{
NSNumber *port = @8081;
NSString *portStr = [[[NSProcessInfo processInfo] environment] objectForKey:@"RCT_METRO_PORT"];
if (portStr && [portStr length] > 0) {
port = [NSNumber numberWithInt:[portStr intValue]];
}
if ([bundleURL port]) {
port = [bundleURL port];
}
NSString *host = [bundleURL host];
if (!host) {
host = @"localhost";
}
// this is consistent with the Android implementation, where http:// is the
// hardcoded implicit scheme for the debug server. Note, packagerURL
// technically looks like it could handle schemes/protocols other than HTTP,
// so rather than force HTTP, leave it be for now, in case someone is relying
// on that ability when developing against iOS.
return [NSString stringWithFormat:@"%@:%@", host, port];
}
static NSString *getSHA256(NSString *string)
{
const char *str = string.UTF8String;
unsigned char result[CC_SHA256_DIGEST_LENGTH];
CC_SHA256(str, (CC_LONG)strlen(str), result);
return [NSString stringWithFormat:@"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
result[0],
result[1],
result[2],
result[3],
result[4],
result[5],
result[6],
result[7],
result[8],
result[9],
result[10],
result[11],
result[12],
result[13],
result[14],
result[15],
result[16],
result[17],
result[18],
result[19]];
}
// Returns an opaque ID which is stable for the current combination of device and app, stable across installs,
// and unique across devices.
static NSString *getInspectorDeviceId()
{
// A bundle ID uniquely identifies a single app throughout the system. [Source: Apple docs]
NSString *bundleId = [[NSBundle mainBundle] bundleIdentifier];
// An alphanumeric string that uniquely identifies a device to the app's vendor. [Source: Apple docs]
NSString *identifierForVendor = [[UIDevice currentDevice] identifierForVendor].UUIDString;
NSString *rawDeviceId = [NSString stringWithFormat:@"apple-%@-%@", identifierForVendor, bundleId];
return getSHA256(rawDeviceId);
}
static NSURL *getInspectorDeviceUrl(NSURL *bundleURL)
{
NSString *escapedDeviceName = [[[UIDevice currentDevice] name]
stringByAddingPercentEncodingWithAllowedCharacters:NSCharacterSet.URLQueryAllowedCharacterSet];
NSString *escapedAppName = [[[NSBundle mainBundle] bundleIdentifier]
stringByAddingPercentEncodingWithAllowedCharacters:NSCharacterSet.URLQueryAllowedCharacterSet];
NSString *escapedInspectorDeviceId = [getInspectorDeviceId()
stringByAddingPercentEncodingWithAllowedCharacters:NSCharacterSet.URLQueryAllowedCharacterSet];
return [NSURL URLWithString:[NSString stringWithFormat:@"http://%@/inspector/device?name=%@&app=%@&device=%@",
getServerHost(bundleURL),
escapedDeviceName,
escapedAppName,
escapedInspectorDeviceId]];
}
@implementation RCTInspectorDevServerHelper
RCT_NOT_IMPLEMENTED(-(instancetype)init)
static NSMutableDictionary<NSString *, id<RCTInspectorPackagerConnectionProtocol>> *socketConnections = nil;
static void sendEventToAllConnections(NSString *event)
{
for (NSString *socketId in socketConnections) {
[socketConnections[socketId] sendEventToAllConnections:event];
}
}
+ (BOOL)isPackagerDisconnected
{
for (NSString *socketId in socketConnections) {
if ([socketConnections[socketId] isConnected]) {
return false;
}
}
return true;
}
+ (void)openDebugger:(NSURL *)bundleURL withErrorMessage:(NSString *)errorMessage
{
NSString *appId = [[[NSBundle mainBundle] bundleIdentifier]
stringByAddingPercentEncodingWithAllowedCharacters:NSCharacterSet.URLQueryAllowedCharacterSet];
NSString *escapedInspectorDeviceId = [getInspectorDeviceId()
stringByAddingPercentEncodingWithAllowedCharacters:NSCharacterSet.URLQueryAllowedCharacterSet];
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"http://%@/open-debugger?appId=%@&device=%@",
getServerHost(bundleURL),
appId,
escapedInspectorDeviceId]];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request setHTTPMethod:@"POST"];
[[[NSURLSession sharedSession]
dataTaskWithRequest:request
completionHandler:^(
__unused NSData *_Nullable data, __unused NSURLResponse *_Nullable response, NSError *_Nullable error) {
if (error != nullptr) {
RCTLogWarn(@"%@", errorMessage);
}
}] resume];
}
+ (void)disableDebugger
{
auto &inspectorFlags = facebook::react::jsinspector_modern::InspectorFlags::getInstance();
if (!inspectorFlags.getEnableModernCDPRegistry()) {
sendEventToAllConnections(kDebuggerMsgDisable);
}
}
+ (id<RCTInspectorPackagerConnectionProtocol>)connectWithBundleURL:(NSURL *)bundleURL
{
NSURL *inspectorURL = getInspectorDeviceUrl(bundleURL);
// Note, using a static dictionary isn't really the greatest design, but
// the packager connection does the same thing, so it's at least consistent.
// This is a static map that holds different inspector clients per the inspectorURL
if (socketConnections == nil) {
socketConnections = [NSMutableDictionary new];
}
NSString *key = [inspectorURL absoluteString];
id<RCTInspectorPackagerConnectionProtocol> connection = socketConnections[key];
if (!connection || !connection.isConnected) {
if (facebook::react::jsinspector_modern::InspectorFlags::getInstance().getEnableCxxInspectorPackagerConnection()) {
connection = [[RCTCxxInspectorPackagerConnection alloc] initWithURL:inspectorURL];
} else {
connection = [[RCTInspectorPackagerConnection alloc] initWithURL:inspectorURL];
}
socketConnections[key] = connection;
[connection connect];
}
return connection;
}
@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.
*/
#import <React/RCTDefines.h>
#if RCT_DEV // Only supported in dev mode
@class RCTPackagerClientResponder;
@class RCTReconnectingWebSocket;
#if defined(__cplusplus)
extern "C" {
#endif
extern const int RCT_PACKAGER_CLIENT_PROTOCOL_VERSION;
#if defined(__cplusplus)
}
#endif
@protocol RCTPackagerClientMethod <NSObject>
- (void)handleRequest:(NSDictionary<NSString *, id> *)params withResponder:(RCTPackagerClientResponder *)responder;
- (void)handleNotification:(NSDictionary<NSString *, id> *)params;
@optional
/** By default object will receive its methods on the main queue, unless this method is overridden. */
- (dispatch_queue_t)methodQueue;
@end
@interface RCTPackagerClientResponder : NSObject
- (instancetype)initWithId:(id)msgId socket:(RCTReconnectingWebSocket *)socket;
- (void)respondWithResult:(id)result;
- (void)respondWithError:(id)error;
@end
#endif

View File

@@ -0,0 +1,65 @@
/*
* 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 <React/RCTPackagerClient.h>
#import <React/RCTLog.h>
#import <React/RCTReconnectingWebSocket.h>
#import <React/RCTUtils.h>
#if RCT_DEV // Only supported in dev mode
const int RCT_PACKAGER_CLIENT_PROTOCOL_VERSION = 2;
@implementation RCTPackagerClientResponder {
id _msgId;
__weak RCTReconnectingWebSocket *_socket;
}
- (instancetype)initWithId:(id)msgId socket:(RCTReconnectingWebSocket *)socket
{
if (self = [super init]) {
_msgId = msgId;
_socket = socket;
}
return self;
}
- (void)respondWithResult:(id)result
{
NSDictionary<NSString *, id> *msg = @{
@"version" : @(RCT_PACKAGER_CLIENT_PROTOCOL_VERSION),
@"id" : _msgId,
@"result" : result,
};
NSError *jsError = nil;
NSString *message = RCTJSONStringify(msg, &jsError);
if (jsError) {
RCTLogError(@"%@ failed to stringify message with error %@", [self class], jsError);
} else {
[_socket send:message];
}
}
- (void)respondWithError:(id)error
{
NSDictionary<NSString *, id> *msg = @{
@"version" : @(RCT_PACKAGER_CLIENT_PROTOCOL_VERSION),
@"id" : _msgId,
@"error" : error,
};
NSError *jsError = nil;
NSString *message = RCTJSONStringify(msg, &jsError);
if (jsError) {
RCTLogError(@"%@ failed to stringify message with error %@", [self class], jsError);
} else {
[_socket send:message];
}
}
@end
#endif

View File

@@ -0,0 +1,78 @@
/*
* 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 <React/RCTDefines.h>
#if RCT_DEV
NS_ASSUME_NONNULL_BEGIN
@protocol RCTPackagerClientMethod;
@class RCTPackagerClientResponder;
typedef uint32_t RCTHandlerToken;
typedef void (^RCTNotificationHandler)(NSDictionary<NSString *, id> *);
typedef void (^RCTRequestHandler)(NSDictionary<NSString *, id> *, RCTPackagerClientResponder *);
typedef void (^RCTConnectedHandler)(void);
/** Encapsulates singleton connection to React Native packager. */
@interface RCTPackagerConnection : NSObject
+ (instancetype)sharedPackagerConnection;
/**
* Registers a handler for a notification broadcast from the packager. An
* example is "reload" - an instruction to reload from the packager.
* If multiple notification handlers are registered for the same method, they
* will all be invoked sequentially.
*/
- (RCTHandlerToken)addNotificationHandler:(RCTNotificationHandler)handler
queue:(dispatch_queue_t)queue
forMethod:(NSString *)method;
/**
* Registers a handler for a request from the packager. An example is
* pokeSamplingProfiler; it asks for profile data from the client.
* Only one handler can be registered for a given method; calling this
* displaces any previous request handler registered for that method.
*/
- (RCTHandlerToken)addRequestHandler:(RCTRequestHandler)handler
queue:(dispatch_queue_t)queue
forMethod:(NSString *)method;
/**
* Registers a handler that runs at most once, when the connection to the
* packager has been established. The handler will be dispatched immediately
* if the connection is already established.
*/
- (RCTHandlerToken)addConnectedHandler:(RCTConnectedHandler)handler queue:(dispatch_queue_t)queue;
/** Removes a handler. Silently does nothing if the token is not valid. */
- (void)removeHandler:(RCTHandlerToken)token;
/** Disconnects and removes all handlers. */
- (void)stop;
/** Reconnect with given packager server. */
- (void)reconnect:(NSString *)packagerServerHostPort;
/**
* Historically no distinction was made between notification and request
* handlers. If you use this method, it will be registered as *both* a
* notification handler *and* a request handler. You should migrate to the
* new block-based API instead.
*/
- (void)addHandler:(id<RCTPackagerClientMethod>)handler
forMethod:(NSString *)method __deprecated_msg("Use addRequestHandler or addNotificationHandler instead");
@end
NS_ASSUME_NONNULL_END
#endif

View File

@@ -0,0 +1,321 @@
/*
* 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 <React/RCTPackagerConnection.h>
#import <objc/runtime.h>
#import <algorithm>
#import <vector>
#import <React/RCTAssert.h>
#import <React/RCTBridge.h>
#import <React/RCTBundleURLProvider.h>
#import <React/RCTConstants.h>
#import <React/RCTConvert.h>
#import <React/RCTDefines.h>
#import <React/RCTLog.h>
#import <React/RCTPackagerClient.h>
#import <React/RCTReconnectingWebSocket.h>
#import <React/RCTUtils.h>
#if RCT_DEV
#import <SocketRocket/SRWebSocket.h>
@interface RCTPackagerConnection () <RCTReconnectingWebSocketDelegate>
@end
template <typename Handler>
struct Registration {
NSString *method;
Handler handler;
dispatch_queue_t queue;
uint32_t token;
};
@implementation RCTPackagerConnection {
std::mutex _mutex; // protects all ivars
RCTReconnectingWebSocket *_socket;
BOOL _socketConnected;
NSString *_serverHostPortForSocket;
NSString *_serverSchemeForSocket;
id _bundleURLChangeObserver;
uint32_t _nextToken;
std::vector<Registration<RCTNotificationHandler>> _notificationRegistrations;
std::vector<Registration<RCTRequestHandler>> _requestRegistrations;
std::vector<Registration<RCTConnectedHandler>> _connectedRegistrations;
}
+ (instancetype)sharedPackagerConnection
{
static RCTPackagerConnection *connection;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
connection = [RCTPackagerConnection new];
});
return connection;
}
- (instancetype)init
{
if (self = [super init]) {
_nextToken = 1; // Prevent randomly erasing a handler if you pass a bogus 0 token
_serverHostPortForSocket = [[RCTBundleURLProvider sharedSettings] packagerServerHostPort];
_serverSchemeForSocket = [[RCTBundleURLProvider sharedSettings] packagerScheme];
_socket = socketForLocation(_serverHostPortForSocket, _serverSchemeForSocket);
_socket.delegate = self;
[_socket start];
RCTPackagerConnection *const __weak weakSelf = self;
_bundleURLChangeObserver =
[[NSNotificationCenter defaultCenter] addObserverForName:RCTBundleURLProviderUpdatedNotification
object:nil
queue:[NSOperationQueue mainQueue]
usingBlock:^(NSNotification *_Nonnull __unused note) {
[weakSelf bundleURLSettingsChanged];
}];
}
return self;
}
static RCTReconnectingWebSocket *socketForLocation(NSString *const serverHostPort, NSString *scheme)
{
NSString *serverHost;
NSString *serverPort;
NSArray *locationComponents = [serverHostPort componentsSeparatedByString:@":"];
if ([locationComponents count] > 0) {
serverHost = locationComponents[0];
}
if ([locationComponents count] > 1) {
serverPort = locationComponents[1];
}
if (![scheme length]) {
scheme = @"http";
}
NSURLComponents *const components = [NSURLComponents new];
components.host = serverHost ?: @"localhost";
components.scheme = scheme;
components.port = serverPort ? @(serverPort.integerValue) : @(kRCTBundleURLProviderDefaultPort);
components.path = @"/message";
components.queryItems = @[ [NSURLQueryItem queryItemWithName:@"role" value:RCTPlatformName] ];
static dispatch_queue_t queue;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
queue = dispatch_queue_create("com.facebook.RCTPackagerConnectionQueue", DISPATCH_QUEUE_SERIAL);
});
return [[RCTReconnectingWebSocket alloc] initWithURL:components.URL queue:queue];
}
- (void)stop
{
std::lock_guard<std::mutex> l(_mutex);
if (_socket == nil) {
// Already stopped
return;
}
[[NSNotificationCenter defaultCenter] removeObserver:_bundleURLChangeObserver];
_bundleURLChangeObserver = nil;
_socketConnected = NO;
[_socket stop];
_socket = nil;
_notificationRegistrations.clear();
_requestRegistrations.clear();
}
- (void)reconnect:(NSString *)packagerServerHostPort
{
std::lock_guard<std::mutex> l(_mutex);
if (_socket == nil) {
return; // already stopped
}
NSString *const serverScheme = [[RCTBundleURLProvider sharedSettings] packagerScheme];
if ([packagerServerHostPort isEqual:_serverHostPortForSocket] && [serverScheme isEqual:_serverSchemeForSocket]) {
return; // unchanged
}
_socket.delegate = nil;
[_socket stop];
_serverHostPortForSocket = packagerServerHostPort;
_serverSchemeForSocket = serverScheme;
_socket = socketForLocation(packagerServerHostPort, serverScheme);
_socket.delegate = self;
[_socket start];
}
- (void)bundleURLSettingsChanged
{
[self reconnect:[[RCTBundleURLProvider sharedSettings] packagerServerHostPort]];
}
- (RCTHandlerToken)addNotificationHandler:(RCTNotificationHandler)handler
queue:(dispatch_queue_t)queue
forMethod:(NSString *)method
{
std::lock_guard<std::mutex> l(_mutex);
const auto token = _nextToken++;
_notificationRegistrations.push_back({method, handler, queue, token});
return token;
}
- (RCTHandlerToken)addRequestHandler:(RCTRequestHandler)handler
queue:(dispatch_queue_t)queue
forMethod:(NSString *)method
{
std::lock_guard<std::mutex> l(_mutex);
const auto token = _nextToken++;
_requestRegistrations.push_back({method, handler, queue, token});
return token;
}
- (RCTHandlerToken)addConnectedHandler:(RCTConnectedHandler)handler queue:(dispatch_queue_t)queue
{
std::lock_guard<std::mutex> l(_mutex);
if (_socketConnected) {
dispatch_async(queue, ^{
handler();
});
return 0; // _nextToken starts at 1, so 0 is a no-op token
} else {
const auto token = _nextToken++;
_connectedRegistrations.push_back({nil, handler, queue, token});
return token;
}
}
- (void)removeHandler:(RCTHandlerToken)token
{
std::lock_guard<std::mutex> l(_mutex);
eraseRegistrationsWithToken(_notificationRegistrations, token);
eraseRegistrationsWithToken(_requestRegistrations, token);
eraseRegistrationsWithToken(_connectedRegistrations, token);
}
template <typename Handler>
static void eraseRegistrationsWithToken(std::vector<Registration<Handler>> &registrations, RCTHandlerToken token)
{
registrations.erase(
std::remove_if(
registrations.begin(), registrations.end(), [&token](const auto &reg) { return reg.token == token; }),
registrations.end());
}
- (void)addHandler:(id<RCTPackagerClientMethod>)handler forMethod:(NSString *)method
{
dispatch_queue_t queue =
[handler respondsToSelector:@selector(methodQueue)] ? [handler methodQueue] : dispatch_get_main_queue();
[self
addNotificationHandler:^(NSDictionary<NSString *, id> *notification) {
[handler handleNotification:notification];
}
queue:queue
forMethod:method];
[self
addRequestHandler:^(NSDictionary<NSString *, id> *request, RCTPackagerClientResponder *responder) {
[handler handleRequest:request withResponder:responder];
}
queue:queue
forMethod:method];
}
static BOOL isSupportedVersion(NSNumber *version)
{
NSArray<NSNumber *> *const kSupportedVersions = @[ @(RCT_PACKAGER_CLIENT_PROTOCOL_VERSION) ];
return [kSupportedVersions containsObject:version];
}
#pragma mark - RCTReconnectingWebSocketDelegate
- (void)reconnectingWebSocketDidOpen:(__unused RCTReconnectingWebSocket *)webSocket
{
std::vector<Registration<RCTConnectedHandler>> registrations;
{
std::lock_guard<std::mutex> l(_mutex);
_socketConnected = YES;
registrations = _connectedRegistrations;
_connectedRegistrations.clear();
}
for (const auto &registration : registrations) {
// Beware: don't capture the reference to handler in a dispatched block!
RCTConnectedHandler handler = registration.handler;
dispatch_async(registration.queue, ^{
handler();
});
}
}
- (void)reconnectingWebSocket:(RCTReconnectingWebSocket *)webSocket didReceiveMessage:(id)message
{
NSError *error = nil;
NSDictionary<NSString *, id> *msg = RCTJSONParse(message, &error);
if (error) {
RCTLogError(@"%@ failed to parse message with error %@\n<message>\n%@\n</message>", [self class], error, msg);
return;
}
if (!isSupportedVersion(msg[@"version"])) {
RCTLogError(@"%@ received message with not supported version %@", [self class], msg[@"version"]);
return;
}
NSString *const method = msg[@"method"];
NSDictionary<NSString *, id> *const params = msg[@"params"];
id messageId = msg[@"id"];
if (messageId) { // Request
const std::vector<Registration<RCTRequestHandler>> registrations(
registrationsWithMethod(_mutex, _requestRegistrations, method));
if (registrations.empty()) {
RCTLogError(@"No handler found for packager method %@", msg[@"method"]);
[[[RCTPackagerClientResponder alloc] initWithId:messageId socket:webSocket]
respondWithError:[NSString stringWithFormat:@"No handler found for packager method %@", msg[@"method"]]];
} else {
// If there are multiple matching request registrations, only one can win;
// otherwise the packager would get multiple responses. Choose the last one.
RCTRequestHandler handler = registrations.back().handler;
dispatch_async(registrations.back().queue, ^{
handler(params, [[RCTPackagerClientResponder alloc] initWithId:messageId socket:webSocket]);
});
}
} else { // Notification
const std::vector<Registration<RCTNotificationHandler>> registrations(
registrationsWithMethod(_mutex, _notificationRegistrations, method));
for (const auto &registration : registrations) {
// Beware: don't capture the reference to handler in a dispatched block!
RCTNotificationHandler handler = registration.handler;
dispatch_async(registration.queue, ^{
handler(params);
});
}
}
}
- (void)reconnectingWebSocketDidClose:(__unused RCTReconnectingWebSocket *)webSocket
{
std::lock_guard<std::mutex> l(_mutex);
_socketConnected = NO;
}
template <typename Handler>
static std::vector<Registration<Handler>>
registrationsWithMethod(std::mutex &mutex, const std::vector<Registration<Handler>> &registrations, NSString *method)
{
std::lock_guard<std::mutex> l(mutex); // Scope lock acquisition to prevent deadlock when calling out
std::vector<Registration<Handler>> matches;
for (const auto &reg : registrations) {
if ([reg.method isEqual:method]) {
matches.push_back(reg);
}
}
return matches;
}
@end
#endif