From 921eac06a440eecff1e36add401f14f323d2e45b Mon Sep 17 00:00:00 2001 From: Elliot Hesp Date: Wed, 7 Jun 2017 12:37:32 +0100 Subject: [PATCH] [admob][ios] Finish up Interstitial with events + AdRequest handling --- docs/modules/admob.md | 23 ++++- ios/RNFirebase/RNFirebaseAdMob.h | 6 +- ios/RNFirebase/RNFirebaseAdMob.m | 93 ++++++++++++++++++-- ios/RNFirebase/RNFirebaseAdMobInterstitial.h | 4 +- ios/RNFirebase/RNFirebaseAdMobInterstitial.m | 44 +++++++-- lib/modules/admob/Interstitial.js | 13 ++- 6 files changed, 159 insertions(+), 24 deletions(-) diff --git a/docs/modules/admob.md b/docs/modules/admob.md index 38c9d354..f56b856d 100644 --- a/docs/modules/admob.md +++ b/docs/modules/admob.md @@ -60,6 +60,8 @@ An interstitial is a full screen advert which creates a new activity on top of R allowing the developer to choose when to display them they're not available as a component. Instead they're controlled via method calls. +A single interstitial instance can only be shown once. If you want to display another, create a new one. + To request an interstitial from AdMob, the `loadAd` method must be called with an instance of `AdRequest` (see below for full API): #### Methods @@ -147,6 +149,8 @@ A rewarded video allows you to display a video to a user, whereby they're able t and receive nothing. For example, when a user completes a level on your gaming app, show them a video which will give them in-game credit. +A single rewarded video instance can only be shown once. If you want to display another, create a new one. + ?> It's recommended you begin loading the video as soon as possible. To request an Rewarded Video from AdMob, the `loadAd` method must be called with an instance of `AdRequest` (see below for full API): @@ -203,8 +207,8 @@ in production. ##### build() Builds the current request for AdMob to handle. -##### addTestDevice() -Sets the current device as a test device. +##### addTestDevice(device?: string) +Sets a device ID as a test device. If no device string is passed, a default emulator id is passed. ##### addKeyword(keyword: `string`) Add a new keyword to relate the advert to. @@ -224,7 +228,7 @@ Sets the request agent string to identify the ad request's origin. Third party l ##### setContentUrl(url: `string`) Sets the content URL for targeting purposes. -##### setIsDesignedForFamilies(forFamilies: `boolean`) +##### [android] setIsDesignedForFamilies(forFamilies: `boolean`) If you set this value to true, you indicate that your app requires that the ad request should return a Designed for Families-compliant ad. If you set this value to false, you indicate that your app does not require that the ad request should return a Designed for Families-compliant ad. @@ -301,7 +305,7 @@ Called if the opened advert causes the user to leave the application, for exampl Called when the user returns back to the application after closing an advert. ##### onAdFailedToLoad(error: `Error`) -Called when an advert fails to load. Returns a JavaScript Error with one of the following error codes: +Called when an advert fails to load. Returns a JavaScript Error with one of the following error codes. | code | message | | --------------------------------- | ------------------------------------------------------------------------------------------------------------ | @@ -309,6 +313,7 @@ Called when an advert fails to load. Returns a JavaScript Error with one of the | admob/error-code-invalid-request | The ad request was invalid; for instance, the ad unit ID was incorrect. | | admob/error-code-network-error | The ad request was unsuccessful due to network connectivity. | | admob/error-code-no-fill | The ad request was successful, but no ad was returned due to lack of ad inventory. | +| admob/os-version-too-low | The current device’s OS is below the minimum required version. | ##### [NativeExpress] onVideoEnd() Called when a the video has ended. @@ -340,3 +345,13 @@ Used to build a request object to pass into AdMob requests. ### VideoOptions Used to build an options object for how videos should be handled with adverts containing a video. + +### EventTypes +Returns all of the available advert event types. + +### RewardedVideoEventTypes +Returns the extra event types for Rewarded Videos. + +### NativeExpressEventTypes +Returns the extra event types for Native Express adverts. + diff --git a/ios/RNFirebase/RNFirebaseAdMob.h b/ios/RNFirebase/RNFirebaseAdMob.h index 35b61387..d23cd11b 100644 --- a/ios/RNFirebase/RNFirebaseAdMob.h +++ b/ios/RNFirebase/RNFirebaseAdMob.h @@ -11,9 +11,11 @@ #import "GoogleMobileAds/GADAdDelegate.h" -@interface RNFirebaseAdMob : RCTEventEmitter { -} +@interface RNFirebaseAdMob : RCTEventEmitter @property NSMutableDictionary *interstitials; + +- (GADRequest *)buildRequest:(NSDictionary *)request; +- (NSDictionary *)errorCodeToDictionary:(NSError *)error; @end #else diff --git a/ios/RNFirebase/RNFirebaseAdMob.m b/ios/RNFirebase/RNFirebaseAdMob.m index 0ee0cac8..7d9c4b7c 100644 --- a/ios/RNFirebase/RNFirebaseAdMob.m +++ b/ios/RNFirebase/RNFirebaseAdMob.m @@ -3,6 +3,10 @@ #import "RNFirebaseAdMobInterstitial.h" +NSString *MALE = @"male"; +NSString *FEMALE = @"female"; +NSString *UNKNOWN = @"unknown"; + @implementation RNFirebaseAdMob RCT_EXPORT_MODULE(); @@ -18,27 +22,32 @@ RCT_EXPORT_MODULE(); return dispatch_get_main_queue(); } -RCT_EXPORT_METHOD(initialize:(NSString *)appId) { +RCT_EXPORT_METHOD(initialize: + (NSString *) appId) { [GADMobileAds configureWithApplicationID:appId]; } -RCT_EXPORT_METHOD(interstitialShowAd:(NSString *)adUnit) { - RNFirebaseAdMobInterstitial * interstitial = [self getOrCreateInterstitial:adUnit]; +RCT_EXPORT_METHOD(interstitialShowAd: + (NSString *) adUnit) { + RNFirebaseAdMobInterstitial *interstitial = [self getOrCreateInterstitial:adUnit]; [interstitial showAd]; } -RCT_EXPORT_METHOD(interstitialLoadAd:(NSString *)adUnit request:(NSDictionary *) request) { - RNFirebaseAdMobInterstitial * interstitial = [self getOrCreateInterstitial:adUnit]; - [interstitial loadAd]; +RCT_EXPORT_METHOD(interstitialLoadAd: + (NSString *) adUnit + request: + (NSDictionary *) request) { + RNFirebaseAdMobInterstitial *interstitial = [self getOrCreateInterstitial:adUnit]; + [interstitial loadAd:request]; } -- (RNFirebaseAdMobInterstitial *) getOrCreateInterstitial:(NSString *)adUnit { +- (RNFirebaseAdMobInterstitial *)getOrCreateInterstitial:(NSString *)adUnit { if (_interstitials[adUnit]) { return _interstitials[adUnit]; } - _interstitials[adUnit] = [[RNFirebaseAdMobInterstitial alloc] initWithProps:adUnit delegate:self]; + _interstitials[adUnit] = [[RNFirebaseAdMobInterstitial alloc] initWithProps:adUnit delegate:self]; return _interstitials[adUnit]; } @@ -46,4 +55,72 @@ RCT_EXPORT_METHOD(interstitialLoadAd:(NSString *)adUnit request:(NSDictionary *) return @[ADMOB_INTERSTITIAL_EVENT, ADMOB_REWARDED_VIDEO_EVENT]; } +- (GADRequest *)buildRequest:(NSDictionary *)request { + GADRequest *builder = [GADRequest request]; + + if (request[@"tagForChildDirectedTreatment"]) [builder tagForChildDirectedTreatment:(BOOL) request[@"tagForChildDirectedTreatment"]]; + if (request[@"contentUrl"]) builder.contentURL = request[@"contentUrl"]; + if (request[@"requestAgent"]) builder.requestAgent = request[@"requestAgent"]; + if (request[@"keywords"]) builder.keywords = request[@"keywords"]; + if (request[@"testDevices"]) builder.testDevices = request[@"testDevices"]; + + if (request[@"gender"]) { + NSString *gender = [request[@"gender"] stringValue]; + + if (gender == MALE) { + builder.gender = kGADGenderMale; + } else if (gender == FEMALE) { + builder.gender = kGADGenderFemale; + } else if (gender == UNKNOWN) { + builder.gender = kGADGenderUnknown; + } + } + + return builder; +} + +- (NSDictionary *)errorCodeToDictionary:(NSError *)error { + NSString *code; + NSString *message; + + switch (error.code) { + default: + case kGADErrorServerError: + case kGADErrorInternalError: + case kGADErrorInvalidArgument: + case kGADErrorMediationDataError: + case kGADErrorMediationAdapterError: + code = @"admob/error-code-internal-error "; + message = @"Something happened internally; for instance, an invalid response was received from the ad server."; + break; + case kGADErrorMediationInvalidAdSize: + code = @"admob/error-code-invalid-request"; + message = @"The ad requested has an invalid size."; + break; + case kGADErrorInvalidRequest: + case kGADErrorReceivedInvalidResponse: + code = @"admob/error-code-invalid-request"; + message = @"The ad request was invalid; for instance, the ad unit ID was incorrect."; + break; + case kGADErrorNoFill: + case kGADErrorMediationNoFill: + code = @"admob/error-code-no-fill"; + message = @"The ad request was successful, but no ad was returned due to lack of ad inventory."; + break; + case kGADErrorNetworkError: + code = @"admob/error-code-network-error"; + message = @"The ad request was unsuccessful due to network connectivity."; + break; + case kGADErrorOSVersionTooLow: + code = @"admob/os-version-too-low"; + message = @"The current device’s OS is below the minimum required version."; + break; + } + + return @{ + @"code": code, + @"message": message, + }; +} + @end diff --git a/ios/RNFirebase/RNFirebaseAdMobInterstitial.h b/ios/RNFirebase/RNFirebaseAdMobInterstitial.h index 512e0b03..912d459f 100644 --- a/ios/RNFirebase/RNFirebaseAdMobInterstitial.h +++ b/ios/RNFirebase/RNFirebaseAdMobInterstitial.h @@ -2,6 +2,7 @@ #define RNFirebaseAdMobInterstitial_h #import +#import "RNFirebaseEvents.h" #if __has_include() @@ -18,7 +19,8 @@ - (id)initWithProps:(NSString *)adUnit delegate:(RNFirebaseAdMob *)delegate; - (void)showAd; -- (void)loadAd; +- (void)loadAd:(NSDictionary *)request; +- (void)sendJSEvent:(NSString *)type payload:(nullable NSDictionary *)payload; @end diff --git a/ios/RNFirebase/RNFirebaseAdMobInterstitial.m b/ios/RNFirebase/RNFirebaseAdMobInterstitial.m index f0747458..1d3da60c 100644 --- a/ios/RNFirebase/RNFirebaseAdMobInterstitial.m +++ b/ios/RNFirebase/RNFirebaseAdMobInterstitial.m @@ -1,6 +1,5 @@ #import "RNFirebaseAdMobInterstitial.h" - @implementation RNFirebaseAdMobInterstitial - (id)initWithProps:(NSString *)adUnit delegate:(RNFirebaseAdMob *)delegate { @@ -15,17 +14,50 @@ - (GADInterstitial *)createInterstitial { GADInterstitial *interstitial = [[GADInterstitial alloc] initWithAdUnitID:_adUnitID]; - interstitial.delegate = _delegate; + interstitial.delegate = self; return interstitial; } -- (void) loadAd { - // todo build request - [_interstitial loadRequest:[GADRequest request]]; +- (void)loadAd:(NSDictionary *)request { + [_interstitial loadRequest:[_delegate buildRequest:request]]; } - (void)showAd { - [_interstitial presentFromRootViewController:[UIApplication sharedApplication].delegate.window.rootViewController]; + if (_interstitial.isReady) { + [_interstitial presentFromRootViewController:[UIApplication sharedApplication].delegate.window.rootViewController]; + } +} + +- (void)sendJSEvent:(NSString *)type payload:(NSDictionary *)payload { + if (payload == nil) { + payload = @{}; + } + + [_delegate sendEventWithName:ADMOB_INTERSTITIAL_EVENT body:@{ + @"type": type, + @"adUnit": _adUnitID, + @"payload": payload + }]; +} + +- (void)interstitialDidReceiveAd:(nonnull GADInterstitial *)ad { + [self sendJSEvent:@"onAdLoaded" payload:@{}]; +} + +- (void)interstitial:(GADInterstitial *)ad didFailToReceiveAdWithError:(GADRequestError *)error { + [self sendJSEvent:@"onAdFailedToLoad" payload:[_delegate errorCodeToDictionary:error]]; +} + +- (void)interstitialWillPresentScreen:(GADInterstitial *)ad { + [self sendJSEvent:@"onAdOpened" payload:@{}]; +} + +- (void)interstitialDidDismissScreen:(GADInterstitial *)ad { + [self sendJSEvent:@"onAdClosed" payload:@{}]; +} + +- (void)interstitialWillLeaveApplication:(GADInterstitial *)ad { + [self sendJSEvent:@"onAdLeftApplication" payload:@{}]; } @end diff --git a/lib/modules/admob/Interstitial.js b/lib/modules/admob/Interstitial.js index 46bfc3ae..f072b96b 100644 --- a/lib/modules/admob/Interstitial.js +++ b/lib/modules/admob/Interstitial.js @@ -1,8 +1,9 @@ import { NativeModules } from 'react-native'; import { statics } from './'; +import AdRequest from './AdRequest'; import { nativeToJSError } from '../../utils'; -const FirebaseAdMob = NativeModules.RNFirebaseAdmob; +const FirebaseAdMob = NativeModules.RNFirebaseAdMob; export default class Interstitial { @@ -43,8 +44,14 @@ export default class Interstitial { * @param request * @returns {*} */ - loadAd(request: Object) { - return FirebaseAdMob.interstitialLoadAd(this.adUnit, request); + loadAd(request?: AdRequest) { + let adRequest = request; + + if (!adRequest || !Object.keys(adRequest)) { + adRequest = new AdRequest().addTestDevice().build(); + } + + return FirebaseAdMob.interstitialLoadAd(this.adUnit, adRequest); } /**