Skip to content

Commit

Permalink
feat(ios): sdk methods and banners on new architecture
Browse files Browse the repository at this point in the history
  • Loading branch information
dylancom committed Jul 24, 2023
1 parent 2bd1339 commit 3ee9d02
Show file tree
Hide file tree
Showing 25 changed files with 615 additions and 215 deletions.
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,21 @@ React Native Google Mobile Ads is built with three key principals in mind;
- 📄 **Well documented**
- full reference & installation documentation alongside detailed guides and FAQs

## Migrating to the New Architecture Status (backwards compatible)
This package can be used in both The Old and [The New Architecture](https://reactnative.dev/docs/the-new-architecture/landing-page).
When using The New Architecture, some legacy code will still be used though. See status below:

- **iOS**
- Mobile Ads SDK Methods (Turbo Native Module) 🟢🟢🟢🟢
- Banners (Fabric Native Component) 🟢🟢🟢🟢
- Full Screen Ads (Turbo Native Module) ⚪⚪⚪⚪
- User Messaging Platform (Turbo Native Module) ⚪⚪⚪⚪
- **Android**
- Mobile Ads SDK Methods (Turbo Native Module) ⚪⚪⚪⚪
- Banners (Fabric Native Component) ⚪⚪⚪⚪
- Full Screen Ads (Turbo Native Module) ⚪⚪⚪⚪
- User Messaging Platform (Turbo Native Module) ⚪⚪⚪⚪

## Documentation

- [Installation](https://docs.page/invertase/react-native-google-mobile-ads)
Expand Down
4 changes: 2 additions & 2 deletions RNGoogleMobileAds.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@ Pod::Spec.new do |s|
s.source = { :git => "#{package["repository"]["url"]}.git", :tag => "v#{s.version}" }
s.social_media_url = 'http://twitter.com/invertaseio'
s.ios.deployment_target = "10.0"
s.source_files = 'ios/**/*.{h,m,swift}'
s.source_files = "ios/**/*.{h,m,mm,swift}"
s.weak_frameworks = "AppTrackingTransparency"

# React Native dependencies
s.dependency 'React-Core'
install_modules_dependencies(s)

# Other dependencies
if defined?($RNGoogleUmpSDKVersion)
Expand Down
10 changes: 10 additions & 0 deletions __tests__/banner.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,14 @@ describe('Google Mobile Ads Banner', function () {
"BannerAd: 'size(s)' expected a valid BannerAdSize or custom size string.",
);
});

it('throws if requestOptions is invalid.', function () {
let errorMsg;
try {
render(<BannerAd unitId={MOCK_ID} size={BannerAdSize.BANNER} requestOptions={'options'} />);
} catch (e) {
errorMsg = e.message;
}
expect(errorMsg).toEqual("BannerAd: 'options' expected an object value");
});
});
21 changes: 21 additions & 0 deletions __tests__/googleMobileAds.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import admob, { MaxAdContentRating } from '../src';
import RNGoogleMobileAdsModule from '../src/NativeGoogleMobileAdsModule';

describe('Admob', function () {
describe('setRequestConfiguration()', function () {
Expand Down Expand Up @@ -61,6 +62,26 @@ describe('Admob', function () {
});

describe('testDebugMenu', function () {
it('does call native initialize method', () => {
admob().initialize();
expect(RNGoogleMobileAdsModule.initialize).toBeCalledTimes(1);
});

it('does call native setRequestConfiguration method', () => {
admob().setRequestConfiguration({ tagForChildDirectedTreatment: true });
expect(RNGoogleMobileAdsModule.setRequestConfiguration).toBeCalledTimes(1);
});

it('does call native openAdInspector method', () => {
admob().openAdInspector();
expect(RNGoogleMobileAdsModule.openAdInspector).toBeCalledTimes(1);
});

it('does call native openDebugMenu method', () => {
admob().openDebugMenu('12345');
expect(RNGoogleMobileAdsModule.openDebugMenu).toBeCalledTimes(1);
});

it('throws if adUnit is empty', function () {
expect(() => {
admob().openDebugMenu('');
Expand Down
8 changes: 8 additions & 0 deletions android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,13 @@ def jsonMinSdk = packageJson['sdkVersions']['android']['minSdk']
def jsonTargetSdk = packageJson['sdkVersions']['android']['targetSdk']
def jsonCompileSdk = packageJson['sdkVersions']['android']['compileSdk']
def jsonBuildTools = packageJson['sdkVersions']['android']['buildTools']
def isNewArchitectureEnabled() {
return project.hasProperty("newArchEnabled") && project.newArchEnabled == "true"
}

if (isNewArchitectureEnabled()) {
apply plugin: "com.facebook.react"
}

project.ext {
set('react-native', [
Expand Down Expand Up @@ -100,6 +107,7 @@ android {
appJSONGoogleMobileAdsOptimizeInitialization : appJSONGoogleMobileAdsOptimizeInitializationBool,
appJSONGoogleMobileAdsOptimizeAdLoading : appJSONGoogleMobileAdsOptimizeAdLoadingBool
]
buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString()
}
lintOptions {
disable 'GradleCompatible'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package io.invertase.googlemobileads

import com.facebook.react.bridge.WritableMap
import com.facebook.react.uimanager.events.Event

class OnNativeEvent(viewId: Int, private val event: WritableMap) : Event<OnNativeEvent>(viewId) {

override fun getEventName(): String {
return EVENT_NAME
}

override fun getCoalescingKey(): Short = 0

override fun getEventData(): WritableMap? {
return event
}

companion object {
const val EVENT_NAME = "topNative"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,14 @@
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.common.MapBuilder;
import com.facebook.react.uimanager.PixelUtil;
import com.facebook.react.uimanager.SimpleViewManager;
import com.facebook.react.uimanager.ThemedReactContext;
import com.facebook.react.uimanager.UIManagerHelper;
import com.facebook.react.uimanager.annotations.ReactProp;
import com.facebook.react.uimanager.events.RCTEventEmitter;
import com.facebook.react.uimanager.events.EventDispatcher;
import com.facebook.react.views.view.ReactViewGroup;
import com.google.android.gms.ads.AdListener;
import com.google.android.gms.ads.AdRequest;
Expand All @@ -40,12 +40,15 @@
import com.google.android.gms.ads.admanager.AdManagerAdView;
import com.google.android.gms.ads.admanager.AppEventListener;
import io.invertase.googlemobileads.common.ReactNativeAdView;
import io.invertase.googlemobileads.common.SharedUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.json.JSONException;
import org.json.JSONObject;

public class ReactNativeGoogleMobileAdsBannerAdViewManager
extends SimpleViewManager<ReactNativeAdView> {
Expand Down Expand Up @@ -73,7 +76,7 @@ public ReactNativeAdView createViewInstance(@Nonnull ThemedReactContext themedRe
@Override
public Map<String, Object> getExportedCustomDirectEventTypeConstants() {
MapBuilder.Builder<String, Object> builder = MapBuilder.builder();
builder.put("onNativeEvent", MapBuilder.of("registrationName", "onNativeEvent"));
builder.put(OnNativeEvent.EVENT_NAME, MapBuilder.of("registrationName", "onNativeEvent"));
return builder.build();
}

Expand Down Expand Up @@ -104,9 +107,15 @@ public void setUnitId(ReactNativeAdView reactViewGroup, String value) {
}

@ReactProp(name = "request")
public void setRequest(ReactNativeAdView reactViewGroup, ReadableMap value) {
reactViewGroup.setRequest(ReactNativeGoogleMobileAdsCommon.buildAdRequest(value));
reactViewGroup.setPropsChanged(true);
public void setRequest(ReactNativeAdView reactViewGroup, String value) {
try {
JSONObject jsonObject = new JSONObject(value);
WritableMap writableMap = SharedUtils.jsonObjectToWritableMap(jsonObject);
reactViewGroup.setRequest(ReactNativeGoogleMobileAdsCommon.buildAdRequest(writableMap));
reactViewGroup.setPropsChanged(true);
} catch (JSONException e) {
e.printStackTrace();
}
}

@ReactProp(name = "sizes")
Expand Down Expand Up @@ -274,8 +283,11 @@ private void sendEvent(ReactNativeAdView reactViewGroup, String type, WritableMa
event.merge(payload);
}

((ThemedReactContext) reactViewGroup.getContext())
.getJSModule(RCTEventEmitter.class)
.receiveEvent(reactViewGroup.getId(), "onNativeEvent", event);
ThemedReactContext themedReactContext = ((ThemedReactContext) reactViewGroup.getContext());
EventDispatcher eventDispatcher =
UIManagerHelper.getEventDispatcherForReactTag(themedReactContext, reactViewGroup.getId());
if (eventDispatcher != null) {
eventDispatcher.dispatchEvent(new OnNativeEvent(reactViewGroup.getId(), event));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import android.os.Build;
import android.util.Log;
import com.facebook.react.bridge.*;
import com.facebook.react.bridge.WritableNativeMap;
import com.facebook.react.common.LifecycleState;
import com.facebook.react.modules.core.DeviceEventManagerModule;
import java.io.File;
Expand Down Expand Up @@ -239,28 +240,29 @@ public static Boolean hasPackageClass(String packageName, String className) {
}

public static WritableMap jsonObjectToWritableMap(JSONObject jsonObject) throws JSONException {
Iterator<String> iterator = jsonObject.keys();
WritableMap writableMap = Arguments.createMap();
WritableMap map = new WritableNativeMap();

Iterator<String> iterator = jsonObject.keys();
while (iterator.hasNext()) {
String key = iterator.next();
Object value = jsonObject.get(key);
if (value instanceof Float || value instanceof Double) {
writableMap.putDouble(key, jsonObject.getDouble(key));
} else if (value instanceof Number) {
writableMap.putInt(key, jsonObject.getInt(key));
} else if (value instanceof String) {
writableMap.putString(key, jsonObject.getString(key));
} else if (value instanceof JSONObject) {
writableMap.putMap(key, jsonObjectToWritableMap(jsonObject.getJSONObject(key)));
if (value instanceof JSONObject) {
map.putMap(key, jsonObjectToWritableMap((JSONObject) value));
} else if (value instanceof JSONArray) {
writableMap.putArray(key, jsonArrayToWritableArray(jsonObject.getJSONArray(key)));
} else if (value == JSONObject.NULL) {
writableMap.putNull(key);
map.putArray(key, jsonArrayToWritableArray((JSONArray) value));
} else if (value instanceof Boolean) {
map.putBoolean(key, (Boolean) value);
} else if (value instanceof Integer) {
map.putInt(key, (Integer) value);
} else if (value instanceof Double) {
map.putDouble(key, (Double) value);
} else if (value instanceof String) {
map.putString(key, (String) value);
} else {
map.putString(key, value.toString());
}
}

return writableMap;
return map;
}

public static WritableArray jsonArrayToWritableArray(JSONArray jsonArray) throws JSONException {
Expand Down
9 changes: 7 additions & 2 deletions ios/RNGoogleMobileAds/RNGoogleMobileAdsBannerComponent.m
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,13 @@ - (void)setSizes:(NSArray *)sizes {
_propsChanged = true;
}

- (void)setRequest:(NSDictionary *)request {
_request = request;
- (void)setRequest:(NSString *)request {
NSData *jsonData = [request dataUsingEncoding:NSUTF8StringEncoding];
NSError *error = nil;
_request = [NSJSONSerialization JSONObjectWithData:jsonData options:kNilOptions error:&error];
if (error) {
NSLog(@"Error parsing JSON: %@", error.localizedDescription);
}
_propsChanged = true;
}

Expand Down
30 changes: 30 additions & 0 deletions ios/RNGoogleMobileAds/RNGoogleMobileAdsBannerView.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// This guard prevent this file to be compiled in the old architecture.
#ifdef RCT_NEW_ARCH_ENABLED
#import <GoogleMobileAds/GADAppEventDelegate.h>
#import <GoogleMobileAds/GADBannerView.h>
#import <GoogleMobileAds/GADBannerViewDelegate.h>
#import <React/RCTViewComponentView.h>
#import <UIKit/UIKit.h>

#ifndef NativeComponentExampleComponentView_h
#define NativeComponentExampleComponentView_h

NS_ASSUME_NONNULL_BEGIN

@interface RNGoogleMobileAdsBannerView
: RCTViewComponentView <GADBannerViewDelegate, GADAppEventDelegate>

@property GADBannerView *banner;
@property(nonatomic, assign) BOOL requested;

@property(nonatomic, copy) NSArray *sizes;
@property(nonatomic, copy) NSString *unitId;
@property(nonatomic, copy) NSDictionary *request;
@property(nonatomic, copy) NSNumber *manualImpressionsEnabled;

@end

NS_ASSUME_NONNULL_END

#endif /* NativeComponentExampleComponentView_h */
#endif /* RCT_NEW_ARCH_ENABLED */
Loading

0 comments on commit 3ee9d02

Please sign in to comment.