Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 70 additions & 11 deletions ios/RNMParticle/RNMPRokt.mm
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,33 @@
#import <React/RCTViewManager.h>
#import <React/RCTUIManager.h>
#import <React/RCTBridge.h>
#import <os/log.h>
#import "RoktEventManager.h"

#ifdef RCT_NEW_ARCH_ENABLED
#import "RoktNativeLayoutComponentView.h"
#import <RNMParticle/RNMParticle.h>
#endif // RCT_NEW_ARCH_ENABLED

// os_log for [mParticle-Rokt] diagnostics: visible in production (Console.app, device logs)
// and does not trigger RCT LogBox/warning UI in debug.
static os_log_t _rokt_os_log(void) {
static os_log_t log;
static dispatch_once_t once;
dispatch_once(&once, ^{
log = os_log_create("com.mparticle.react-native", "rokt");
});
return log;
}

static void _rokt_log(NSString *format, ...) {
va_list args;
va_start(args, format);
NSString *msg = [[NSString alloc] initWithFormat:format arguments:args];
va_end(args);
os_log_with_type(_rokt_os_log(), OS_LOG_TYPE_INFO, "%{public}s", [msg UTF8String]);
}

@interface RNMPRokt ()

@property (nonatomic, nullable) RoktEventManager *eventManager;
Expand All @@ -43,11 +63,15 @@ + (NSString *)moduleName {
}

+ (void)load {
_rokt_log(@"[mParticle-Rokt] RNMPRokt module load");
RCTRegisterModule(self);
}

- (dispatch_queue_t)methodQueue
{
BOOL bridgeNil = (self.bridge == nil);
BOOL uiManagerNil = (self.bridge.uiManager == nil);
_rokt_log(@"[mParticle-Rokt] methodQueue called, bridge %@, uiManager %@", bridgeNil ? @"nil" : @"non-nil", uiManagerNil ? @"nil" : @"non-nil");
return self.bridge.uiManager.methodQueue;
}

Expand All @@ -65,8 +89,10 @@ - (void)setMethodQueue:(dispatch_queue_t)methodQueue
static NSDictionary * __attribute__((optnone)) safeExtractRoktConfigDict(
JS::NativeMPRokt::RoktConfigType &roktConfig) {
if (&roktConfig == nullptr) {
_rokt_log(@"[mParticle-Rokt] safeExtractRoktConfigDict: roktConfig ref is nullptr, returning nil");
return nil;
}
_rokt_log(@"[mParticle-Rokt] safeExtractRoktConfigDict: extracting config");
NSMutableDictionary *roktConfigDict = [[NSMutableDictionary alloc] init];
if (roktConfig.cacheConfig().has_value()) {
NSMutableDictionary *cacheConfigDict = [[NSMutableDictionary alloc] init];
Expand All @@ -78,7 +104,11 @@ - (void)setMethodQueue:(dispatch_queue_t)methodQueue
cacheConfigDict[@"cacheAttributes"] = cacheConfig.cacheAttributes();
}
roktConfigDict[@"cacheConfig"] = cacheConfigDict;
_rokt_log(@"[mParticle-Rokt] safeExtractRoktConfigDict: cacheConfig present, keys: %lu", (unsigned long)roktConfigDict.count);
} else {
_rokt_log(@"[mParticle-Rokt] safeExtractRoktConfigDict: cacheConfig has no value");
}
_rokt_log(@"[mParticle-Rokt] safeExtractRoktConfigDict: returning dict with %lu keys", (unsigned long)roktConfigDict.count);
return roktConfigDict;
}

Expand All @@ -89,6 +119,7 @@ - (void)selectPlacements:(NSString *)identifer
roktConfig:(JS::NativeMPRokt::RoktConfigType &)roktConfig
fontFilesMap:(NSDictionary *)fontFilesMap
{
_rokt_log(@"[mParticle-Rokt] New Architecture Implementation");
NSMutableDictionary *finalAttributes = [self convertToMutableDictionaryOfStrings:attributes];

NSDictionary *roktConfigDict = safeExtractRoktConfigDict(roktConfig);
Expand All @@ -97,51 +128,73 @@ - (void)selectPlacements:(NSString *)identifer
// Old Architecture Implementation
RCT_EXPORT_METHOD(selectPlacements:(NSString *) identifer attributes:(NSDictionary *)attributes placeholders:(NSDictionary * _Nullable)placeholders roktConfig:(NSDictionary * _Nullable)roktConfig fontFilesMap:(NSDictionary * _Nullable)fontFilesMap)
{
_rokt_log(@"[mParticle-Rokt] Old Architecture Implementation");
NSMutableDictionary *finalAttributes = [self convertToMutableDictionaryOfStrings:attributes];
MPRoktConfig *config = [self buildRoktConfigFromDict:roktConfig];
#endif

_rokt_log(@"[mParticle-Rokt] selectPlacements called with identifier: %@, attributes count: %lu", identifer, (unsigned long)finalAttributes.count);

[MParticle _setWrapperSdk_internal:MPWrapperSdkReactNative version:@""];
// Create callback implementation
MPRoktEventCallback *callbacks = [[MPRoktEventCallback alloc] init];

__weak __typeof__(self) weakSelf = self;

callbacks.onLoad = ^{
[self.eventManager onRoktCallbackReceived:@"onLoad"];
_rokt_log(@"[mParticle-Rokt] onLoad");
[weakSelf.eventManager onRoktCallbackReceived:@"onLoad"];
};

callbacks.onUnLoad = ^{
[self.eventManager onRoktCallbackReceived:@"onUnLoad"];
RCTLogInfo(@"unloaded");
_rokt_log(@"[mParticle-Rokt] onUnLoad");
[weakSelf.eventManager onRoktCallbackReceived:@"onUnLoad"];
};

callbacks.onShouldShowLoadingIndicator = ^{
[self.eventManager onRoktCallbackReceived:@"onShouldShowLoadingIndicator"];
_rokt_log(@"[mParticle-Rokt] onShouldShowLoadingIndicator");
[weakSelf.eventManager onRoktCallbackReceived:@"onShouldShowLoadingIndicator"];
};

callbacks.onShouldHideLoadingIndicator = ^{
[self.eventManager onRoktCallbackReceived:@"onShouldHideLoadingIndicator"];
_rokt_log(@"[mParticle-Rokt] onShouldHideLoadingIndicator");
[weakSelf.eventManager onRoktCallbackReceived:@"onShouldHideLoadingIndicator"];
};

callbacks.onEmbeddedSizeChange = ^(NSString *placementId, CGFloat height) {
[self.eventManager onWidgetHeightChanges:height placement:placementId];
_rokt_log(@"[mParticle-Rokt] onEmbeddedSizeChange");
[weakSelf.eventManager onWidgetHeightChanges:height placement:placementId];
};

if (self.bridge == nil || self.bridge.uiManager == nil) {
NSLog(@"[mParticle-Rokt] addUIBlock skipped: self.bridge%@ is nil. selectPlacements will not be called. This can occur in New Architecture bridgeless production builds.", self.bridge == nil ? @"" : @".uiManager");
BOOL bridgeNil = (self.bridge == nil);
BOOL uiManagerNil = (self.bridge.uiManager == nil);
_rokt_log(@"[mParticle-Rokt] bridge %@, uiManager %@", bridgeNil ? @"nil" : @"non-nil", uiManagerNil ? @"nil" : @"non-nil");

if (bridgeNil || uiManagerNil) {
_rokt_log(@"[mParticle-Rokt] addUIBlock skipped: self.bridge%@ is nil. selectPlacements will not be called. This can occur in New Architecture bridgeless production builds.", bridgeNil ? @"" : @".uiManager");
} else {
_rokt_log(@"[mParticle-Rokt] queuing addUIBlock for identifier: %@", identifer);
}
[self.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *,UIView *> *viewRegistry) {
NSMutableDictionary *nativePlaceholders = [self getNativePlaceholders:placeholders viewRegistry:viewRegistry];
__strong __typeof__(weakSelf) strongSelf = weakSelf;
_rokt_log(@"[mParticle-Rokt] addUIBlock executing for identifier: %@, viewRegistry count: %lu", identifer, (unsigned long)viewRegistry.count);

[self subscribeViewEvents:identifer];
NSMutableDictionary *nativePlaceholders = strongSelf ? [strongSelf getNativePlaceholders:placeholders viewRegistry:viewRegistry] : [NSMutableDictionary dictionary];

if (strongSelf) {
[strongSelf subscribeViewEvents:identifer];
}

id mpInstance = [MParticle sharedInstance];
id roktKit = mpInstance ? [mpInstance rokt] : nil;
_rokt_log(@"[mParticle-Rokt] MParticle sharedInstance %@, rokt kit %@", mpInstance ? @"non-nil" : @"nil", roktKit ? @"non-nil" : @"nil");
_rokt_log(@"[mParticle-Rokt] calling mParticle Core selectPlacements for: %@", identifer);
[[[MParticle sharedInstance] rokt] selectPlacements:identifer
attributes:finalAttributes
embeddedViews:nativePlaceholders
config:config
callbacks:callbacks];
}];
_rokt_log(@"[mParticle-Rokt] addUIBlock enqueued for identifier: %@", identifer);
}

RCT_EXPORT_METHOD(purchaseFinalized : (NSString *)placementId catalogItemId : (
Expand Down Expand Up @@ -180,6 +233,7 @@ - (MPColorMode)stringToColorMode:(NSString*)colorString
}

- (MPRoktConfig *)buildRoktConfigFromDict:(NSDictionary<NSString *, id> *)configMap {
_rokt_log(@"[mParticle-Rokt] buildRoktConfigFromDict: configMap %@", configMap == nil ? @"nil" : [NSString stringWithFormat:@"non-nil (%lu keys)", (unsigned long)configMap.count]);
MPRoktConfig *config = [[MPRoktConfig alloc] init];
BOOL isConfigEmpty = YES;

Expand Down Expand Up @@ -212,11 +266,13 @@ - (MPRoktConfig *)buildRoktConfigFromDict:(NSDictionary<NSString *, id> *)config
config.cacheDuration = cacheDuration;
}

_rokt_log(@"[mParticle-Rokt] buildRoktConfigFromDict: returning %@", isConfigEmpty ? @"nil" : @"config");
return isConfigEmpty ? nil : config;
}

- (void)subscribeViewEvents:(NSString* _Nonnull) viewName
{
_rokt_log(@"[mParticle-Rokt] subscribeViewEvents for viewName: %@", viewName);
if (self.eventManager == nil) {
self.eventManager = [RoktEventManager allocWithZone: nil];
}
Expand All @@ -227,6 +283,7 @@ - (void)subscribeViewEvents:(NSString* _Nonnull) viewName

- (NSMutableDictionary *)getNativePlaceholders:(NSDictionary *)placeholders viewRegistry:(NSDictionary<NSNumber *, UIView *> *)viewRegistry
{
_rokt_log(@"[mParticle-Rokt] getNativePlaceholders: placeholders %lu, viewRegistry %lu", (unsigned long)placeholders.count, (unsigned long)viewRegistry.count);
NSMutableDictionary *nativePlaceholders = [[NSMutableDictionary alloc]initWithCapacity:placeholders.count];

for(id key in placeholders){
Expand All @@ -248,12 +305,14 @@ - (NSMutableDictionary *)getNativePlaceholders:(NSDictionary *)placeholders view
#endif // RCT_NEW_ARCH_ENABLED
}

_rokt_log(@"[mParticle-Rokt] getNativePlaceholders: resolved %lu native placeholder(s)", (unsigned long)nativePlaceholders.count);
return nativePlaceholders;
}

#ifdef RCT_NEW_ARCH_ENABLED
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:(const facebook::react::ObjCTurboModule::InitParams &)params {
self.bridge = params.instance.bridge;
_rokt_log(@"[mParticle-Rokt] getTurboModule: bridge set to %@", self.bridge == nil ? @"nil" : @"non-nil");
return std::make_shared<facebook::react::NativeMPRoktSpecJSI>(params);
}
#endif // RCT_NEW_ARCH_ENABLED
Expand Down
24 changes: 24 additions & 0 deletions ios/RNMParticle/RoktEventManager.m
Original file line number Diff line number Diff line change
@@ -1,5 +1,23 @@
#import "RoktEventManager.h"
#import <mParticle_Apple_SDK/mParticle_Apple_SDK-Swift.h>
#import <os/log.h>

static os_log_t _rokt_events_os_log(void) {
static os_log_t log;
static dispatch_once_t once;
dispatch_once(&once, ^{
log = os_log_create("com.mparticle.react-native", "rokt-events");
});
return log;
}

static void _rokt_events_log(NSString *format, ...) {
va_list args;
va_start(args, format);
NSString *msg = [[NSString alloc] initWithFormat:format arguments:args];
va_end(args);
os_log_with_type(_rokt_events_os_log(), OS_LOG_TYPE_INFO, "%{public}s", [msg UTF8String]);
}

@implementation RoktEventManager
{
Expand All @@ -13,17 +31,20 @@ + (id)allocWithZone:(NSZone *)zone {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [super allocWithZone:zone];
_rokt_events_log(@"[mParticle-Rokt] RoktEventManager module alloc");
});
return sharedInstance;
}

// Will be called when this module's first listener is added.
-(void)startObserving {
_rokt_events_log(@"[mParticle-Rokt] RoktEventManager startObserving (JS listener added)");
hasListeners = YES;
}

// Will be called when this module's last listener is removed, or on dealloc.
-(void)stopObserving {
_rokt_events_log(@"[mParticle-Rokt] RoktEventManager stopObserving (no JS listeners)");
hasListeners = NO;
}

Expand Down Expand Up @@ -51,13 +72,16 @@ - (void)onFirstPositiveResponse

- (void)onRoktCallbackReceived:(NSString*)eventValue
{
_rokt_events_log(@"[mParticle-Rokt] RoktEventManager onRoktCallbackReceived: %@", eventValue ?: @"(nil)");
if (hasListeners) {
[self sendEventWithName:@"RoktCallback" body:@{@"callbackValue": eventValue}];
}
}

- (void)onRoktEvents:(MPRoktEvent * _Nonnull)event viewName:(NSString * _Nullable)viewName
{
NSString *eventClass = event ? NSStringFromClass([event class]) : @"nil";
_rokt_events_log(@"[mParticle-Rokt] RoktEventManager onRoktEvents: %@ viewName: %@", eventClass, viewName ?: @"(nil)");
if (hasListeners) {
NSString *placementId;
NSString *eventName = @"";
Expand Down
Loading