Logrocket fixes

This commit is contained in:
Savya Bikram Shah
2026-06-01 15:47:14 +05:45
parent f9826325c6
commit 28810235a5
124 changed files with 1178949 additions and 405 deletions

View File

@@ -0,0 +1,113 @@
// Unity to LogRocket iOS bridge.
//
// Uses __has_include so this file compiles whether or not the LogRocket pod is
// integrated. When the pod is present the real, type-checked SDK calls compile
// in; when absent every entry point degrades to a no-op so non-LogRocket builds
// still link.
//
// IMPORTANT: this is a .m (Objective-C), NOT .mm (Objective-C++). LogRocket's
// umbrella header pulls in C headers (LROFileUtils.h, LROJSONCodec.h) that use
// the C `restrict` keyword, which is invalid in Objective-C++ and fails to
// compile there. As plain Objective-C the functions already have C linkage, so
// the _lr_* symbols resolve from C# DllImport("__Internal") without extern "C".
//
// API verified against the actual LogRocket.xcframework 3.1.0 ObjC header
// (LROSDK / LROConfiguration / LROCustomEventBuilder). The SDK is a Swift
// framework with ARC-managed objects - compile with ARC (-fobjc-arc), which
// Unity's generated project enables by default.
#import <Foundation/Foundation.h>
#if __has_include(<LogRocket/LogRocket-Swift.h>)
#import <LogRocket/LogRocket-Swift.h>
#define LR_AVAILABLE 1
#else
#define LR_AVAILABLE 0
#endif
static NSString* NSStr(const char* s) { return s ? [NSString stringWithUTF8String:s] : @""; }
// Parse a JSON object into an NSDictionary<NSString*, NSString*>. The SDK's
// identify userInfo and the event-builder put API are string-typed, so values
// are coerced to strings (matching the Android bridge).
static NSDictionary<NSString*, NSString*>* ParseJson(const char* json) {
if (!json) return @{};
NSData* data = [NSStr(json) dataUsingEncoding:NSUTF8StringEncoding];
if (!data) return @{};
id obj = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
if (![obj isKindOfClass:[NSDictionary class]]) return @{};
NSMutableDictionary<NSString*, NSString*>* out = [NSMutableDictionary dictionary];
[(NSDictionary*)obj enumerateKeysAndObjectsUsingBlock:^(id key, id val, BOOL* stop) {
if ([key isKindOfClass:[NSString class]]) {
out[key] = [val isKindOfClass:[NSString class]] ? val : [val description];
}
}];
return out;
}
// Cached session URL. getSessionURL: is asynchronous and its handler may run on
// a background thread, so we cache the latest value and hand the C# side a
// snapshot. The first read may be NULL until the callback resolves.
static NSString* gSessionUrl = nil;
void _lr_init(const char* appId) {
#if LR_AVAILABLE
LROConfiguration* cfg = [[LROConfiguration alloc] initWithAppID:NSStr(appId)];
[LROSDK initializeWithConfiguration:cfg];
NSLog(@"[LogRocketBridge] initialized appId=%@", NSStr(appId));
#else
NSLog(@"[LogRocketBridge] LogRocket pod not integrated - init no-op (appId=%@)", NSStr(appId));
#endif
}
void _lr_identify(const char* userId, const char* traitsJson) {
#if LR_AVAILABLE
[LROSDK identifyWithUserID:NSStr(userId) userInfo:ParseJson(traitsJson)];
#endif
}
void _lr_track(const char* eventName, const char* propsJson) {
#if LR_AVAILABLE
LROCustomEventBuilder* builder = [[LROCustomEventBuilder alloc] init:NSStr(eventName)];
[ParseJson(propsJson) enumerateKeysAndObjectsUsingBlock:^(NSString* key, NSString* val, BOOL* stop) {
[builder putString:key value:val];
}];
[LROSDK track:builder];
#endif
}
void _lr_log(const char* severity, const char* message) {
// The iOS SDK auto-captures application logs (NSLog/os_log), so emitting via
// NSLog is enough for it to be recorded - no manual Logger call required.
NSLog(@"[LogRocket:%@] %@", NSStr(severity), NSStr(message));
}
void _lr_logException(const char* message, const char* stack) {
NSLog(@"[LogRocket:error] %@\n%@", NSStr(message), NSStr(stack));
}
void _lr_startReplay(void) {
// Recording starts automatically at initialize. This rotates to a fresh
// session - useful for explicit restart after a stop. The SDK has no bare
// startNewSession; the no-config variant takes a (nullable) BOOL handler.
#if LR_AVAILABLE
[LROSDK startNewSessionNoConfig:nil];
#endif
}
void _lr_stopReplay(void) {
// endSession halts the current recording but keeps the SDK alive so
// startReplay (startNewSession) can resume. shutdown would be more terminal.
#if LR_AVAILABLE
[LROSDK endSession];
#endif
}
const char* _lr_sessionUrl(void) {
#if LR_AVAILABLE
[LROSDK getSessionURL:^(NSString* url) {
if (url) gSessionUrl = [url copy];
}];
#endif
return gSessionUrl ? [gSessionUrl UTF8String] : NULL;
}