Logrocket fixes
This commit is contained in:
113
Assets/Plugins/iOS/LogRocketUnityBridge.m
Normal file
113
Assets/Plugins/iOS/LogRocketUnityBridge.m
Normal 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;
|
||||
}
|
||||
Reference in New Issue
Block a user