// // DirichletMediationUnityBridge.mm // Dirichlet Mediation Unity Bridge for iOS // // // Created by Dirichlet Unity SDK // Copyright © 2025 Dirichlet Inc. All rights reserved. // #import "DirichletMediationUnityBridge.h" #import #import #import // Unity callback interface extern "C" void UnitySendMessage(const char* obj, const char* method, const char* msg); // Constants static NSString * const kUnityCallbackObject = @"DirichletMediationEventReceiver"; static NSString * const kUnityCallbackMethod = @"OnNativeEvent"; static NSString * const kUnityLoadCallbackObject = @"DirichletMediationIOSLoadCallbackReceiver"; static NSString * const kUnityLoadCallbackMethod = @"OnLoadCallback"; static NSString * const kUnityInitCallbackObject = @"DirichletMediationIOSInitCallbackReceiver"; static NSString * const kUnityInitCallbackMethod = @"OnInitCallback"; // Helper to convert C string to NSString static NSString* CreateNSString(const char* cString) { return cString ? [NSString stringWithUTF8String:cString] : @""; } // Helper to convert NSString to C string (caller must free) static char* MakeCString(NSString* nsString) { if (nsString == nil) { return NULL; } const char* utf8String = [nsString UTF8String]; char* cString = (char*)malloc(strlen(utf8String) + 1); strcpy(cString, utf8String); return cString; } // Helper to send event to Unity static void SendEventToUnity(NSString* handleId, NSString* eventName, NSString* adType, NSDictionary* data) { NSMutableDictionary* payload = [NSMutableDictionary dictionary]; payload[@"handle"] = handleId ?: @""; payload[@"eventName"] = eventName ?: @""; payload[@"adType"] = adType ?: @""; if (data) { payload[@"data"] = data; } NSError* error = nil; NSData* jsonData = [NSJSONSerialization dataWithJSONObject:payload options:0 error:&error]; if (jsonData && !error) { NSString* jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; UnitySendMessage([kUnityCallbackObject UTF8String], [kUnityCallbackMethod UTF8String], [jsonString UTF8String]); } } // Helper to send load callback to Unity (separate from ad events) static void SendLoadCallbackToUnity(NSString* handleId, NSString* eventName, NSString* adType, NSDictionary* data) { NSMutableDictionary* payload = [NSMutableDictionary dictionary]; payload[@"handle"] = handleId ?: @""; payload[@"eventName"] = eventName ?: @""; payload[@"adType"] = adType ?: @""; if (data) { payload[@"data"] = data; } NSError* error = nil; NSData* jsonData = [NSJSONSerialization dataWithJSONObject:payload options:0 error:&error]; if (jsonData && !error) { NSString* jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; UnitySendMessage([kUnityLoadCallbackObject UTF8String], [kUnityLoadCallbackMethod UTF8String], [jsonString UTF8String]); } } // Helper to send init callback to Unity (async initialization result) static void SendInitCallbackToUnity(BOOL success, NSError* error, NSString* extraMessage) { NSMutableDictionary* payload = [NSMutableDictionary dictionary]; payload[@"success"] = @(success); NSMutableDictionary* data = [NSMutableDictionary dictionary]; if (error) { data[@"code"] = @(error.code); data[@"message"] = error.localizedDescription ?: @""; if (error.domain) { data[@"domain"] = error.domain; } } else if (extraMessage.length > 0) { data[@"message"] = extraMessage; } if (data.count > 0) { payload[@"data"] = data; } NSError* jsonError = nil; NSData* jsonData = [NSJSONSerialization dataWithJSONObject:payload options:0 error:&jsonError]; if (jsonData && !jsonError) { NSString* jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; UnitySendMessage([kUnityInitCallbackObject UTF8String], [kUnityInitCallbackMethod UTF8String], [jsonString UTF8String]); } } // Parse JSON extras to dictionary static NSDictionary* ParseExtras(const char* extrasJson) { if (!extrasJson || strlen(extrasJson) == 0) { return nil; } NSString* jsonString = CreateNSString(extrasJson); NSData* jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding]; if (!jsonData) { return nil; } NSError* error = nil; NSDictionary* dict = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:&error]; return error ? nil : dict; } static void SendLoadErrorToUnity(NSString* handleId, NSString* adType, NSError* error, NSString* fallbackMessage) { NSInteger code = error ? error.code : -1; NSString* message = error.localizedDescription ?: fallbackMessage ?: @"Unknown error"; NSDictionary* errorData = @{ @"code": @(code), @"message": message }; SendLoadCallbackToUnity(handleId, @"load_error", adType, errorData); } static void SendShowErrorToUnity(NSString* handleId, NSString* adType, NSInteger code, NSString* message) { NSDictionary* errorData = @{ @"code": @(code), @"message": message ?: @"Ad failed to show" }; SendEventToUnity(handleId, @"show_error", adType ?: @"unknown", errorData); } static UIViewController* TopViewController(UIViewController* rootViewController) { UIViewController* top = rootViewController; while (top.presentedViewController) { top = top.presentedViewController; } if ([top isKindOfClass:[UINavigationController class]]) { return TopViewController(((UINavigationController*)top).visibleViewController); } if ([top isKindOfClass:[UITabBarController class]]) { return TopViewController(((UITabBarController*)top).selectedViewController); } return top; } static UIViewController* CurrentRootViewController(void) { UIWindow* keyWindow = nil; if (@available(iOS 13.0, *)) { for (UIScene* scene in [UIApplication sharedApplication].connectedScenes) { if (scene.activationState != UISceneActivationStateForegroundActive || ![scene isKindOfClass:[UIWindowScene class]]) { continue; } UIWindowScene* windowScene = (UIWindowScene*)scene; for (UIWindow* window in windowScene.windows) { if (window.isKeyWindow) { keyWindow = window; break; } } if (keyWindow) { break; } } } if (!keyWindow) { keyWindow = [UIApplication sharedApplication].keyWindow; } return TopViewController(keyWindow.rootViewController); } static NSString* AdTypeForObject(id ad) { if ([ad isKindOfClass:[DRMRewardVideoAd class]]) { return @"reward_video"; } if ([ad isKindOfClass:[DRMInterstitialAd class]]) { return @"interstitial"; } if ([ad isKindOfClass:[DRMBannerAd class]]) { return @"banner"; } if ([ad isKindOfClass:[DRMSplashAd class]]) { return @"splash"; } return @"unknown"; } #pragma mark - Ad Instance Manager @interface DirichletMediationInstanceManager : NSObject @property (nonatomic, strong) NSMutableDictionary* adInstances; @property (nonatomic, strong) dispatch_queue_t syncQueue; + (instancetype)shared; - (void)storeAd:(id)ad forHandle:(NSString*)handleId; - (id)adForHandle:(NSString*)handleId; - (void)removeAdForHandle:(NSString*)handleId; - (NSString*)generateHandle; @end @implementation DirichletMediationInstanceManager + (instancetype)shared { static DirichletMediationInstanceManager* instance = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ instance = [[DirichletMediationInstanceManager alloc] init]; }); return instance; } - (instancetype)init { if (self = [super init]) { _adInstances = [NSMutableDictionary dictionary]; _syncQueue = dispatch_queue_create("com.dirichlet.mediation.unity.admanager", DISPATCH_QUEUE_SERIAL); } return self; } - (void)storeAd:(id)ad forHandle:(NSString*)handleId { dispatch_sync(self.syncQueue, ^{ self.adInstances[handleId] = ad; }); } - (id)adForHandle:(NSString*)handleId { __block id ad = nil; dispatch_sync(self.syncQueue, ^{ ad = self.adInstances[handleId]; }); return ad; } - (void)removeAdForHandle:(NSString*)handleId { dispatch_sync(self.syncQueue, ^{ [self.adInstances removeObjectForKey:handleId]; }); } - (NSString*)generateHandle { return [[NSUUID UUID] UUIDString]; } @end #pragma mark - Ad Delegates // Reward Video Ad Delegate @interface DirichletMediationUnityRewardVideoAdDelegate : NSObject @property (nonatomic, strong) NSString* handleId; @end @implementation DirichletMediationUnityRewardVideoAdDelegate - (void)rewardVideoAdDidShow:(DRMRewardVideoAd *)rewardVideoAd { SendEventToUnity(self.handleId, @"show", @"reward_video", nil); } - (void)rewardVideoAdDidFailToShow:(DRMRewardVideoAd *)rewardVideoAd withError:(NSError *)error { NSDictionary* data = @{ @"code": @(error.code), @"message": error.localizedDescription ?: @"Unknown error" }; SendEventToUnity(self.handleId, @"show_error", @"reward_video", data); } - (void)rewardVideoAdDidClick:(DRMRewardVideoAd *)rewardVideoAd { SendEventToUnity(self.handleId, @"click", @"reward_video", nil); } - (void)rewardVideoAdDidClose:(DRMRewardVideoAd *)rewardVideoAd { SendEventToUnity(self.handleId, @"close", @"reward_video", nil); } - (void)rewardVideoAdDidRewardUser:(DRMRewardVideoAd *)rewardVideoAd { NSDictionary* data = @{ @"rewardVerify": @(YES), @"rewardAmount": @(0), @"rewardName": @"", @"code": @(0), @"message": @"" }; SendEventToUnity(self.handleId, @"reward", @"reward_video", data); } @end // Interstitial Ad Delegate @interface DirichletMediationUnityInterstitialAdDelegate : NSObject @property (nonatomic, strong) NSString* handleId; @end @implementation DirichletMediationUnityInterstitialAdDelegate - (void)interstitialAdDidShow:(DRMInterstitialAd *)interstitialAd { SendEventToUnity(self.handleId, @"show", @"interstitial", nil); } - (void)interstitialAdDidFailToShow:(DRMInterstitialAd *)interstitialAd withError:(NSError *)error { NSDictionary* data = @{ @"code": @(error.code), @"message": error.localizedDescription ?: @"Unknown error" }; SendEventToUnity(self.handleId, @"show_error", @"interstitial", data); } - (void)interstitialAdDidClick:(DRMInterstitialAd *)interstitialAd { SendEventToUnity(self.handleId, @"click", @"interstitial", nil); } - (void)interstitialAdDidClose:(DRMInterstitialAd *)interstitialAd { SendEventToUnity(self.handleId, @"close", @"interstitial", nil); } @end // Banner Ad Delegate @interface DirichletMediationUnityBannerAdDelegate : NSObject @property (nonatomic, strong) NSString* handleId; @end @implementation DirichletMediationUnityBannerAdDelegate - (void)bannerAdDidShow:(DRMBannerAd *)bannerAd { SendEventToUnity(self.handleId, @"show", @"banner", nil); } - (void)bannerAdDidFailToShow:(DRMBannerAd *)bannerAd withError:(NSError *)error { NSDictionary* data = @{ @"code": @(error.code), @"message": error.localizedDescription ?: @"Unknown error" }; SendEventToUnity(self.handleId, @"show_error", @"banner", data); } - (void)bannerAdDidClick:(DRMBannerAd *)bannerAd { SendEventToUnity(self.handleId, @"click", @"banner", nil); } - (void)bannerAdDidClose:(DRMBannerAd *)bannerAd { SendEventToUnity(self.handleId, @"close", @"banner", nil); } @end // Splash Ad Delegate @interface DirichletMediationUnitySplashAdDelegate : NSObject @property (nonatomic, strong) NSString* handleId; @end @implementation DirichletMediationUnitySplashAdDelegate - (void)splashAdDidShow:(DRMSplashAd *)splashAd { SendEventToUnity(self.handleId, @"show", @"splash", nil); } - (void)splashAdDidFailToShow:(DRMSplashAd *)splashAd withError:(NSError *)error { NSDictionary* data = @{ @"code": @(error.code), @"message": error.localizedDescription ?: @"Unknown error" }; SendEventToUnity(self.handleId, @"show_error", @"splash", data); } - (void)splashAdDidClick:(DRMSplashAd *)splashAd { SendEventToUnity(self.handleId, @"click", @"splash", nil); } - (void)splashAdDidClose:(DRMSplashAd *)splashAd { SendEventToUnity(self.handleId, @"close", @"splash", nil); } @end #pragma mark - Bridge Implementation extern "C" { bool DirichletMediationUnityBridge_Initialize( const char* mediaId, const char* mediaKey, bool enableLog, const char* mediaName, const char* gameChannel, bool shakeEnabled, bool allowIDFAAccess, const char* aTags ) { NSString* nsMediaId = CreateNSString(mediaId); NSString* nsMediaKey = CreateNSString(mediaKey); NSLog(@"[DirichletMediationUnityBridge] Initialize called with mediaId=%@, mediaKey=%@, enableLog=%d", nsMediaId, nsMediaKey, enableLog); if (nsMediaId.length == 0 || nsMediaKey.length == 0) { NSLog(@"[DirichletMediationUnityBridge] Initialize failed: mediaId and mediaKey are required"); return false; } // Check if SDK is already initialized if ([DirichletMediation isInitialized]) { NSLog(@"[DirichletMediationUnityBridge] SDK already initialized"); SendInitCallbackToUnity(YES, nil, @"already_initialized"); return true; } DRMSDKConfig* config = [DRMSDKConfig configWithMediaId:nsMediaId mediaKey:nsMediaKey]; if (!config) { NSLog(@"[DirichletMediationUnityBridge] Failed to create SDK config"); return false; } config.isDebug = enableLog; config.shakeEnabled = shakeEnabled; config.allowIDFAAccess = allowIDFAAccess; if (mediaName && strlen(mediaName) > 0) { config.mediaName = CreateNSString(mediaName); } if (gameChannel && strlen(gameChannel) > 0) { config.gameChannel = CreateNSString(gameChannel); } if (aTags && strlen(aTags) > 0) { config.aTags = CreateNSString(aTags); } NSLog(@"[DirichletMediationUnityBridge] Starting SDK initialization (async callback)..."); NSLog(@"[DirichletMediationUnityBridge] Config details - mediaId:%@, mediaKey:%@, mediaName:%@, gameChannel:%@", config.mediaId, config.mediaKey, config.mediaName, config.gameChannel); // Use async callback pattern (aligned with Ad Unity implementation) void (^startBlock)(void) = ^{ [DirichletMediation startWithConfig:config completion:^(BOOL success, NSError * _Nullable error) { if (error) { NSLog(@"[DirichletMediationUnityBridge] SDK init callback - success:%d, error:%@ (code:%ld, domain:%@)", success, error.localizedDescription, (long)error.code, error.domain); } else { NSLog(@"[DirichletMediationUnityBridge] SDK init callback - success:%d", success); } SendInitCallbackToUnity(success, error, success ? @"ios_mediation_bridge" : nil); }]; }; if ([NSThread isMainThread]) { startBlock(); } else { dispatch_async(dispatch_get_main_queue(), startBlock); } return true; } void DirichletMediationUnityBridge_RequestPermissionIfNeeded(void) { NSLog(@"[DirichletMediationUnityBridge] RequestPermissionIfNeeded called"); if (@available(iOS 14.0, *)) { void (^requestBlock)(void) = ^{ ATTrackingManagerAuthorizationStatus status = [ATTrackingManager trackingAuthorizationStatus]; if (status != ATTrackingManagerAuthorizationStatusNotDetermined) { NSLog(@"[DirichletMediationUnityBridge] ATT status already determined: %lu", (unsigned long)status); return; } [ATTrackingManager requestTrackingAuthorizationWithCompletionHandler:^(ATTrackingManagerAuthorizationStatus status) { NSLog(@"[DirichletMediationUnityBridge] ATT request completed with status: %lu", (unsigned long)status); }]; }; if ([NSThread isMainThread]) { requestBlock(); } else { dispatch_async(dispatch_get_main_queue(), requestBlock); } } else { NSLog(@"[DirichletMediationUnityBridge] ATT is not required below iOS 14"); } } const char* DirichletMediationUnityBridge_GetSdkVersion(void) { static char* versionCString = NULL; if (versionCString == NULL) { versionCString = MakeCString([DirichletMediation sdkVersion]); } return versionCString; } const char* DirichletMediationUnityBridge_LoadRewardVideoAd(long long spaceId, const char* extras) { NSString* handleId = [[DirichletMediationInstanceManager shared] generateHandle]; NSDictionary* extrasDict = ParseExtras(extras); // Create load request DRMAdLoadRequest* request = [[DRMAdLoadRequest alloc] initWithSpaceId:[NSString stringWithFormat:@"%lld", spaceId]]; // Apply extras if provided (matching Unity C# ToBridgePayload keys) if (extrasDict[@"user_id"]) { request.rewardUserId = extrasDict[@"user_id"]; } if (extrasDict[@"extra1"]) { request.rewardExtra = extrasDict[@"extra1"]; } if (extrasDict[@"reward_name"]) { request.rewardName = extrasDict[@"reward_name"]; } if (extrasDict[@"reward_amount"]) { request.rewardAmount = [extrasDict[@"reward_amount"] integerValue]; } if (extrasDict[@"mina_id"]) { request.minaId = [NSString stringWithFormat:@"%@", extrasDict[@"mina_id"]]; } [DRMRewardVideoAd loadWithRequest:request completion:^(NSArray * _Nullable ads, NSError * _Nullable error) { if (ads && ads.count > 0) { DRMRewardVideoAd* ad = ads.firstObject; DirichletMediationUnityRewardVideoAdDelegate* delegate = [[DirichletMediationUnityRewardVideoAdDelegate alloc] init]; delegate.handleId = handleId; ad.delegate = delegate; [[DirichletMediationInstanceManager shared] storeAd:ad forHandle:handleId]; [[DirichletMediationInstanceManager shared] storeAd:delegate forHandle:[handleId stringByAppendingString:@"_delegate"]]; NSLog(@"[DirichletMediationUnityBridge] RewardVideoAd loaded: %@", handleId); SendLoadCallbackToUnity(handleId, @"load_success", @"reward_video", nil); } else { NSLog(@"[DirichletMediationUnityBridge] RewardVideoAd load failed: %@", error.localizedDescription); SendLoadErrorToUnity(handleId, @"reward_video", error, @"RewardVideoAd load failed"); } }]; return MakeCString(handleId); } const char* DirichletMediationUnityBridge_LoadInterstitialAd(long long spaceId, const char* extras) { NSString* handleId = [[DirichletMediationInstanceManager shared] generateHandle]; NSDictionary* extrasDict = ParseExtras(extras); // Create load request DRMAdLoadRequest* request = [[DRMAdLoadRequest alloc] initWithSpaceId:[NSString stringWithFormat:@"%lld", spaceId]]; // Apply extras if provided (matching Unity C# ToBridgePayload keys) if (extrasDict[@"mina_id"]) { request.minaId = [NSString stringWithFormat:@"%@", extrasDict[@"mina_id"]]; } [DRMInterstitialAd loadWithRequest:request completion:^(NSArray * _Nullable ads, NSError * _Nullable error) { if (ads && ads.count > 0) { DRMInterstitialAd* ad = ads.firstObject; DirichletMediationUnityInterstitialAdDelegate* delegate = [[DirichletMediationUnityInterstitialAdDelegate alloc] init]; delegate.handleId = handleId; ad.delegate = delegate; [[DirichletMediationInstanceManager shared] storeAd:ad forHandle:handleId]; [[DirichletMediationInstanceManager shared] storeAd:delegate forHandle:[handleId stringByAppendingString:@"_delegate"]]; NSLog(@"[DirichletMediationUnityBridge] InterstitialAd loaded: %@", handleId); SendLoadCallbackToUnity(handleId, @"load_success", @"interstitial", nil); } else { NSLog(@"[DirichletMediationUnityBridge] InterstitialAd load failed: %@", error.localizedDescription); SendLoadErrorToUnity(handleId, @"interstitial", error, @"InterstitialAd load failed"); } }]; return MakeCString(handleId); } const char* DirichletMediationUnityBridge_LoadBannerAd(long long spaceId, const char* extras) { NSString* handleId = [[DirichletMediationInstanceManager shared] generateHandle]; NSDictionary* extrasDict = ParseExtras(extras); // Create load request DRMAdLoadRequest* request = [[DRMAdLoadRequest alloc] initWithSpaceId:[NSString stringWithFormat:@"%lld", spaceId]]; // Apply extras if provided (matching Unity C# ToBridgePayload keys) if (extrasDict[@"mina_id"]) { request.minaId = [NSString stringWithFormat:@"%@", extrasDict[@"mina_id"]]; } // Set ad size for Banner (CSJ/GDT adapters need this) NSNumber* width = extrasDict[@"express_width"]; NSNumber* height = extrasDict[@"express_height"]; if (width || height) { CGFloat w = width ? [width floatValue] : 0; CGFloat h = height ? [height floatValue] : 0; request.adSize = CGSizeMake(w, h); } [DRMBannerAd loadWithRequest:request completion:^(NSArray * _Nullable ads, NSError * _Nullable error) { if (ads && ads.count > 0) { DRMBannerAd* ad = ads.firstObject; DirichletMediationUnityBannerAdDelegate* delegate = [[DirichletMediationUnityBannerAdDelegate alloc] init]; delegate.handleId = handleId; ad.delegate = delegate; [[DirichletMediationInstanceManager shared] storeAd:ad forHandle:handleId]; [[DirichletMediationInstanceManager shared] storeAd:delegate forHandle:[handleId stringByAppendingString:@"_delegate"]]; NSLog(@"[DirichletMediationUnityBridge] BannerAd loaded: %@", handleId); SendLoadCallbackToUnity(handleId, @"load_success", @"banner", nil); } else { NSLog(@"[DirichletMediationUnityBridge] BannerAd load failed: %@", error.localizedDescription); SendLoadErrorToUnity(handleId, @"banner", error, @"BannerAd load failed"); } }]; return MakeCString(handleId); } const char* DirichletMediationUnityBridge_LoadSplashAd(long long spaceId, const char* extras) { NSString* handleId = [[DirichletMediationInstanceManager shared] generateHandle]; NSDictionary* extrasDict = ParseExtras(extras); // Create load request DRMAdLoadRequest* request = [[DRMAdLoadRequest alloc] initWithSpaceId:[NSString stringWithFormat:@"%lld", spaceId]]; // Apply extras if provided (matching Unity C# ToBridgePayload keys) if (extrasDict[@"mina_id"]) { request.minaId = [NSString stringWithFormat:@"%@", extrasDict[@"mina_id"]]; } // Set ad size for Splash (CSJ adapter needs this) NSNumber* width = extrasDict[@"express_width"]; NSNumber* height = extrasDict[@"express_height"]; if (width || height) { CGFloat w = width ? [width floatValue] : 0; CGFloat h = height ? [height floatValue] : 0; request.adSize = CGSizeMake(w, h); } [DRMSplashAd loadWithRequest:request completion:^(NSArray * _Nullable ads, NSError * _Nullable error) { if (ads && ads.count > 0) { DRMSplashAd* ad = ads.firstObject; DirichletMediationUnitySplashAdDelegate* delegate = [[DirichletMediationUnitySplashAdDelegate alloc] init]; delegate.handleId = handleId; ad.delegate = delegate; [[DirichletMediationInstanceManager shared] storeAd:ad forHandle:handleId]; [[DirichletMediationInstanceManager shared] storeAd:delegate forHandle:[handleId stringByAppendingString:@"_delegate"]]; NSLog(@"[DirichletMediationUnityBridge] SplashAd loaded: %@", handleId); SendLoadCallbackToUnity(handleId, @"load_success", @"splash", nil); } else { NSLog(@"[DirichletMediationUnityBridge] SplashAd load failed: %@", error.localizedDescription); SendLoadErrorToUnity(handleId, @"splash", error, @"SplashAd load failed"); } }]; return MakeCString(handleId); } bool DirichletMediationUnityBridge_ShowAd(const char* handleId, const char* extras) { NSString* nsHandleId = CreateNSString(handleId); id ad = [[DirichletMediationInstanceManager shared] adForHandle:nsHandleId]; if (!ad) { NSLog(@"[DirichletMediationUnityBridge] ShowAd failed: Ad not found for handle %@", nsHandleId); return false; } __block BOOL didStartShow = NO; NSString* adType = AdTypeForObject(ad); NSDictionary* extrasDict = ParseExtras(extras); void (^showBlock)(void) = ^{ UIViewController* rootVC = CurrentRootViewController(); if (!rootVC) { NSLog(@"[DirichletMediationUnityBridge] ShowAd failed: Root view controller not found"); SendShowErrorToUnity(nsHandleId, adType, -2, @"Root view controller not found"); return; } if ([ad isKindOfClass:[DRMRewardVideoAd class]]) { DRMRewardVideoAd* rewardAd = (DRMRewardVideoAd*)ad; if ([rewardAd isReady]) { [rewardAd showFromViewController:rootVC]; didStartShow = YES; NSLog(@"[DirichletMediationUnityBridge] Showing reward video ad: %@", nsHandleId); } else { SendShowErrorToUnity(nsHandleId, @"reward_video", -3, @"Reward video ad is not ready"); } } else if ([ad isKindOfClass:[DRMInterstitialAd class]]) { DRMInterstitialAd* interstitialAd = (DRMInterstitialAd*)ad; if ([interstitialAd isReady]) { [interstitialAd showFromViewController:rootVC]; didStartShow = YES; NSLog(@"[DirichletMediationUnityBridge] Showing interstitial ad: %@", nsHandleId); } else { SendShowErrorToUnity(nsHandleId, @"interstitial", -3, @"Interstitial ad is not ready"); } } else if ([ad isKindOfClass:[DRMBannerAd class]]) { DRMBannerAd* bannerAd = (DRMBannerAd*)ad; UIView* bannerView = bannerAd.view; if (bannerView) { [bannerView removeFromSuperview]; [rootVC.view addSubview:bannerView]; bannerView.translatesAutoresizingMaskIntoConstraints = NO; NSInteger baseline = [extrasDict[@"banner_baseline"] integerValue]; CGFloat offset = extrasDict[@"banner_offset"] ? [extrasDict[@"banner_offset"] floatValue] : 0; NSLayoutYAxisAnchor* verticalAnchor = baseline == 0 ? rootVC.view.safeAreaLayoutGuide.topAnchor : rootVC.view.safeAreaLayoutGuide.bottomAnchor; NSLayoutConstraint* verticalConstraint = baseline == 0 ? [bannerView.topAnchor constraintEqualToAnchor:verticalAnchor constant:offset] : [bannerView.bottomAnchor constraintEqualToAnchor:verticalAnchor constant:-offset]; [NSLayoutConstraint activateConstraints:@[ [bannerView.centerXAnchor constraintEqualToAnchor:rootVC.view.centerXAnchor], [bannerView.widthAnchor constraintLessThanOrEqualToAnchor:rootVC.view.widthAnchor], verticalConstraint, [bannerView.heightAnchor constraintEqualToConstant:bannerAd.size.height > 0 ? bannerAd.size.height : 50] ]]; didStartShow = YES; NSLog(@"[DirichletMediationUnityBridge] Showing banner ad: %@", nsHandleId); } else { NSLog(@"[DirichletMediationUnityBridge] Banner ad view not available: %@", nsHandleId); SendShowErrorToUnity(nsHandleId, @"banner", -4, @"Banner ad view not available"); } } else if ([ad isKindOfClass:[DRMSplashAd class]]) { DRMSplashAd* splashAd = (DRMSplashAd*)ad; if ([splashAd isReady]) { [splashAd showFromViewController:rootVC]; didStartShow = YES; NSLog(@"[DirichletMediationUnityBridge] Showing splash ad: %@", nsHandleId); } else { SendShowErrorToUnity(nsHandleId, @"splash", -3, @"Splash ad is not ready"); } } else { NSLog(@"[DirichletMediationUnityBridge] ShowAd failed: Ad not ready or unknown type"); SendShowErrorToUnity(nsHandleId, @"unknown", -5, @"Unknown ad type"); } }; if ([NSThread isMainThread]) { showBlock(); } else { dispatch_sync(dispatch_get_main_queue(), showBlock); } return didStartShow; } void DirichletMediationUnityBridge_DestroyAd(const char* handleId) { NSString* nsHandleId = CreateNSString(handleId); NSLog(@"[DirichletMediationUnityBridge] Destroying ad: %@", nsHandleId); id ad = [[DirichletMediationInstanceManager shared] adForHandle:nsHandleId]; if ([ad isKindOfClass:[DRMBannerAd class]]) { DRMBannerAd* bannerAd = (DRMBannerAd*)ad; if (bannerAd.view.superview) { dispatch_async(dispatch_get_main_queue(), ^{ [bannerAd.view removeFromSuperview]; }); } } // Remove ad instance [[DirichletMediationInstanceManager shared] removeAdForHandle:nsHandleId]; // Remove delegate NSString* delegateKey = [nsHandleId stringByAppendingString:@"_delegate"]; [[DirichletMediationInstanceManager shared] removeAdForHandle:delegateKey]; } bool DirichletMediationUnityBridge_IsAdValid(const char* handleId) { NSString* nsHandleId = CreateNSString(handleId); id ad = [[DirichletMediationInstanceManager shared] adForHandle:nsHandleId]; if (!ad) { NSLog(@"[DirichletMediationUnityBridge] IsAdValid: Ad not found for handle %@", nsHandleId); return false; } if ([ad isKindOfClass:[DRMRewardVideoAd class]]) { DRMRewardVideoAd* rewardAd = (DRMRewardVideoAd*)ad; return [rewardAd isReady]; } else if ([ad isKindOfClass:[DRMInterstitialAd class]]) { DRMInterstitialAd* interstitialAd = (DRMInterstitialAd*)ad; return [interstitialAd isReady]; } else if ([ad isKindOfClass:[DRMBannerAd class]]) { // Banner ads don't have isReady, assume valid if loaded return true; } else if ([ad isKindOfClass:[DRMSplashAd class]]) { DRMSplashAd* splashAd = (DRMSplashAd*)ad; return [splashAd isReady]; } return false; } } // extern "C"