Native Ad
1. 기본 요건
ADX iOS SDK를 프로젝트에 추가합니다.
Native Ad용으로 발급받은 Ad Unit ID를 사용합니다.
광고를 요청하기 전에 SDK 초기화를 먼저 진행합니다.
SDK 초기화는 앱 실행 시 한 번만 호출하여 주시고, 광고 요청은 초기화가 완료된 후에 이뤄져야 합니다.
iOS 14 이상 지원하는 경우, ATT(App Tracking Transparency) 권한 요청 완료 후 광고를 요청해주세요.
2. 레이아웃 설정
Native Ad의 경우, 광고에 사용 될 레이아웃을 직접 구성해야 합니다. 필수 구성 요소들은 다음과 같습니다.
Title :
UILabel
Main Text :
UILabel
Call-To-Action :
UIButton
Icon Image :
UIImageView
Main Image :
UIImageView
Privacy Icon :
UIImageView
Ad Tag :
UIView (UILabel 또는 UIImageView)
Ad Tag 요소는 "Ad" 또는 "Advertisement" 문자열을 보여주는 UI 구성 요소를 포함하여, 광고라는 것을 명확하게 사용자에게 인식을 시켜야 합니다. 이 요소가 포함되지 않으면, 일부 광고 네트워크의 광고 송출이 중단될 수 있습니다.
2.1.0 버전부터 Native Ad의 CTA (Call-To-Action)가 UILabel
에서 UIButton
으로 변경
되었습니다.
이 요소들은 반드시 포함하여 구성하여 주시고, 광고 컨텐츠를 덮는 View가 없어야 합니다. 또한 텍스트 변경, 이미지 변경, 터치 시 액션 변경 등 광고 컨텐츠에 관련된 부분을 가공하거나 변경하지 않도록 주의 부탁드립니다.
// NativeAdView.h
#import <ADXLibrary/ADXNativeAdRendering.h>
@interface NativeAdView : UIView <ADXNativeAdRendering>
@property (nonatomic, weak) IBOutlet UILabel *titleLabel;
@property (nonatomic, weak) IBOutlet UILabel *mainTextLabel;
@property (nonatomic, weak) IBOutlet UIButton *callToActionButton;
@property (nonatomic, weak) IBOutlet UIImageView *iconImageView;
@property (nonatomic, weak) IBOutlet UIImageView *mainImageView;
@property (nonatomic, weak) IBOutlet UIImageView *privacyInformationIconImageView;
@end
// NativeAdView.m
@implementation NativeAdView
- (void)layoutSubviews {
[super layoutSubviews];
}
- (UILabel *)nativeMainTextLabel {
return self.mainTextLabel;
}
- (UILabel *)nativeTitleTextLabel {
return self.titleLabel;
}
- (UIButton *)nativeCallToActionButton {
return self.callToActionButton;
}
- (UILabel *)nativeSponsoredByCompanyTextLabel {
return self.sponsoredByLabel;
}
- (UIImageView *)nativeIconImageView {
return self.iconImageView;
}
- (UIImageView *)nativeMainImageView {
return self.mainImageView;
}
- (UIImageView *)nativePrivacyInformationIconImageView {
return self.privacyInformationIconImageView;
}
@end
import UIKit
import ADXLibrary
class NativeAdView: UIView {
@IBOutlet var contentsView: UIView!
@IBOutlet var titleLabel: UILabel!
@IBOutlet var mainTextLabel: UILabel!
@IBOutlet var callToActionButton: UIButton!
@IBOutlet var iconImageView: UIImageView!
@IBOutlet var privacyInformationIconImageView: UIImageView!
@IBOutlet var mainImageView: UIImageView!
convenience init() {
self.init(frame: .zero)
initView()
}
override init(frame: CGRect) {
super.init(frame: frame)
initView()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
initView()
}
func initView() {
Bundle.main.loadNibNamed("NativeAdView", owner: self, options: nil)
addSubview(contentsView)
contentsView.frame = self.bounds
contentsView.autoresizingMask = [.flexibleHeight, .flexibleWidth]
}
}
extension NativeAdView: ADXNativeAdRendering {
func nativeMainTextLabel() -> UILabel {
return self.mainTextLabel
}
func nativeTitleTextLabel() -> UILabel {
return self.titleLabel
}
func nativeCallToActionButton() -> UIButton {
return self.callToActionButton
}
func nativeIconImageView() -> UIImageView {
return self.iconImageView
}
func nativeMainImageView() -> UIImageView {
return self.mainImageView
}
func nativePrivacyInformationIconImageView() -> UIImageView {
return self.privacyInformationIconImageView
}
}
3. 구현
Native Ad는 다음 2가지 방법 중 하나로 광고를 로드할 수 있습니다.
Case 1: 하나의 View를 사용하는 경우
ADXNativeAdFactory
의setRenderingViewClass:renderingViewClass:
메서드를 사용하여RenderingViewClass
를 구성합니다.ADXNativeAdFactoryDelegate
callback을 등록합니다.ADXNativeAdFactoryDelegate
의loadAd
를 호출하여 광고를 로드합니다.광고 로드가 완료되면
ADXNativeAdFactoryDelegate
의onSuccess:nativeAd:
가 호출됩니다.
#import "NativeAdViewController"
#import "NativeAdView.h"
#import <ADXLibrary/ADXNativeAdFactory.h>
@interface NativeAdViewController () <ADXNativeAdFactoryDelegate, ADXNativeAdDelegate>
@property (strong) ADXNativeAd *nativeAd;
@end
@implementation NativeAdViewController
- (void)viewDidLoad {
[super viewDidLoad];
[[ADXNativeAdFactory sharedInstance]
setRenderingViewClass:@"<ADX_NATIVE_AD_UNIT_ID>"
renderingViewClass:[NativeAdView class]];
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[[ADXNativeAdFactory sharedInstance] addDelegate:self];
[[ADXNativeAdFactory sharedInstance] loadAd:@"<ADX_NATIVE_AD_UNIT_ID>"];
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[[ADXNativeAdFactory sharedInstance] removeDelegate:self];
}
#pragma mark - ADXNativeAdFactoryDelegate
- (void)onSuccess:(NSString *)adUnitId nativeAd:(ADXNativeAd *)nativeAd {
NSLog(@"onSuccess : %@", adUnitId);
if([adUnitId isEqualToString:ADX_NATIVE_AD_UNIT_ID]) {
self.nativeAd = nativeAd;
self.nativeAd.delegate = self;
UIView *nativeAdView = [[ADXNativeAdFactory sharedInstance]
getNativeAdView:@"<ADX_NATIVE_AD_UNIT_ID>"];
nativeAdView.frame = CGRectMake(0.0,
100.0,
320.0,
300.0);
[self.view addSubview:nativeAdView];
}
}
- (void)onFailure:(NSString *)adUnitId {
NSLog(@"onFailure : %@", adUnitId);
}
#pragma mark - ADXNativeAdDelegate
- (UIViewController *)viewControllerForPresentingModalView {
return self;
}
@end
import UIKit
import ADXLibrary
class NativeAdViewController: UIViewController {
var nativeAd: ADXNativeAd?
override func viewDidLoad() {
super.viewDidLoad()
ADXNativeAdFactory.sharedInstance().setRenderingViewClass(
"<ADX_NATIVE_AD_UNIT_ID>",
renderingViewClass: NativeAdView.self)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
ADXNativeAdFactory.sharedInstance().add(self)
ADXNativeAdFactory.sharedInstance().loadAd("<ADX_NATIVE_AD_UNIT_ID>")
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
ADXNativeAdFactory.sharedInstance().remove(self)
}
}
extension NativeAdViewController: ADXNativeAdFactoryDelegate, ADXNativeAdDelegate {
// MARK: - ADXNativeAdFactoryDelegate
public func onSuccess(_ adUnitId: String!, nativeAd: ADXNativeAd!) {
if (adUnitId == ADX_NATIVE_AD_UNIT_ID) {
self.nativeAd = nativeAd
self.nativeAd?.delegate = self
let nativeAdView = ADXNativeAdFactory.sharedInstance()
.getNativeAdView("<ADX_NATIVE_AD_UNIT_ID>")
nativeAdView?.frame = CGRect(
x: (UIScreen.main.bounds.width - 300.0)/2,
y: 100.0,
width: 300.0,
height: 270.0)
self.view.addSubview(nativeAdView!)
} else {
print("fail to load")
}
}
public func onFailure(_ adUnitId: String!) {
print("onFailure :", adUnitId!)
}
// MARK: - ADXNativeAdDelegate
func viewControllerForPresentingModalView() -> UIViewController {
return self
}
}
Case 2: UITableView / UICollectionView에서 AdPlacer를 사용하는 경우
ADXNativeAdFactory
의setRenderingViewClass:renderingViewClass:
메서드를 사용하여RenderingViewClass
를 구성합니다.로드할 광고의 크기를 설정합니다.
광고의 크기는 xib파일에서 사이즈를 지정하시거나 frame으로 세팅해주셔야 합니다.
ADXAdPositioning
을 이용하여 고정 및 반복 위치를 지정합니다.addFixedIndexPath
: 고정할 광고 위치enableRepeatingPositionsWithInterval
: 광고 반복 위치
loadAdsForAdUnitID:
를 호출하여 광고를 로드합니다.
#import "NativeAdPlacerViewController.h"
#import "NativeAdView.h"
#import <ADXLibrary/ADXNativeAdFactory.h>
@interface NativeAdPlacerViewController () <UICollectionViewDataSource, UICollectionViewDelegate, ADXCollectionViewAdPlacerDelegate>
@property (weak) IBOutlet UICollectionView *colltionView;
@property (strong) ADXCollectionViewAdPlacer *placer;
@end
@implementation NativeAdPlacerViewController
- (void)viewDidLoad {
[super viewDidLoad];
[[ADXNativeAdFactory sharedInstance]
setRenderingViewClass:@"<ADX_NATIVE_AD_UNIT_ID>"
renderingViewClass:[NativeAdView class]];
// 광고 고정 및 반복 위치 지정
ADXAdPositioning *adPositioning = [ADXAdPositioning positioning];
[adPositioning addFixedIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]];
[adPositioning enableRepeatingPositionsWithInterval:3];
self.placer = [[ADXNativeAdFactory sharedInstance]
getCollectionViewAdPlacer:@"<ADX_NATIVE_AD_UNIT_ID>"
collectionView:self.colltionView
viewController:self
adPositioning:adPositioning];
[self.placer loadAdsForAdUnitID:@"<ADX_NATIVE_AD_UNIT_ID>"];
}
import UIKit
import ADXLibrary
class NativeAdPlacerViewController: UIViewController {
@IBOutlet var collectionView: UICollectionView!
var placer : ADXCollectionViewAdPlacer!
override func viewDidLoad() {
super.viewDidLoad()
ADXNativeAdFactory.sharedInstance().setRenderingViewClass(
"<ADX_NATIVE_AD_UNIT_ID>",
renderingViewClass: NativeAdView.self)
let adPositioning = ADXAdPositioning()
adPositioning.addFixedIndexPath(IndexPath(row: 0, section: 0))
adPositioning.enableRepeatingPositions(withInterval: 3)
self.placer = ADXNativeAdFactory.sharedInstance()
.getCollectionViewAdPlacer(
"<ADX_NATIVE_AD_UNIT_ID>",
collectionView: collectionView,
viewController: self,
adPositioning: adPositioning
)
self.placer.loadAds(forAdUnitID: "<ADX_NATIVE_AD_UNIT_ID>")
}
}
extension NativeAdPlacerViewController: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 60;
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell : UICollectionViewCell = collectionView.adx_dequeueReusableCell(withReuseIdentifier: "CustomCell", for: indexPath) as! UICollectionViewCell
return cell
}
}
Cell의 position 정보가 달라지기 때문에, UITableView
는 ADXTableViewAdPlacer.h
, UICollectionView
는 ADXCollectionViewAdPlacer.h
파일을 참고하시어 대체 메서드를 사용해주시기 바랍니다.
4. Ad Revenue (paidEventHandler)
광고 노출에 대한 예상 광고 수익을 확인할 수 있습니다.
이 기능은 iOS ADX SDK 2.6.2 이상 버전을 사용하시는 것이 권장됩니다.
아래 예제와 같이'
paidEventHandler
' 를 사용하여 예상되는 eCPM 값을 확인할 수 있습니다.'ADXCollectionViewAdPlacer' 및 'ADXTableViewAdPlacer' 를 사용하는 경우, 이 기능은 지원되지 않습니다.
미디에이션 설정 과정에서 수동적으로 설정한 값과 정확한 값이 섞여 있어서 예상 값으로 사용하시는 것을 권장드립니다.
eCPM의 통화(Currency) 단위는 USD입니다.
AdJust의 광고 매출 데이터 연동을 지원합니다. 상세한 내용은 AdJust의 AD(X) SDK 연동 가이드를 확인 부탁드립니다.
#import <UIKit/UIKit.h>
#import <ADXLibrary/ADXNativeAdFactory.h>
#import "NativeAdViewController.h"
#import "NativeAdView.h"
#import <FirebaseAnalytics/FirebaseAnalytics.h>
#import <AppsFlyerAdRevenue/AppsFlyerAdRevenue.h>
@interface NativeAdViewController () <ADXNativeAdFactoryDelegate, ADXNativeAdDelegate>
@property (nonatomic, strong) ADXNativeAd *nativeAd;
@end
@implementation NativeAdViewController
- (void)viewDidLoad {
[super viewDidLoad];
[[ADXNativeAdFactory sharedInstance]
setRenderingViewClass:@"<ADX_NATIVE_AD_UNIT_ID>"
renderingViewClass:[NativeAdView class]];
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[[ADXNativeAdFactory sharedInstance] addDelegate:self];
[[ADXNativeAdFactory sharedInstance] loadAd:@"<ADX_NATIVE_AD_UNIT_ID>"];
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[[ADXNativeAdFactory sharedInstance] removeDelegate:self];
}
- (void)handleAdRevenue:(NSNumber *)revenue {
// 1) Firebase Analytics
[FIRAnalytics logEventWithName:kFIREventAdImpression
parameters: @{
kFIRParameterAdPlatform: @"AD(X)",
kFIRParameterAdFormat: @"NativeAd",
kFIRParameterAdUnitName: @"ADX Native Ad",
kFIRParameterCurrency: @"USD",
kFIRParameterValue: revenune
}];
// 2) AppsFlyer
NSDictionary * adRevenueParams = @{
@"AdUnitName" : @"ADX Native Ad",
@"AdType" : @"NativeAd",
};
AppsFlyerAdRevenue * appsFlyerAdRevenue = [AppsFlyerAdRevenue shared];
[appsFlyerAdRevenue
logAdRevenueWithMonetizationNetwork:@"AD(X)"
mediationNetwork:AppsFlyerAdRevenueMediationNetworkTypeCustom
eventRevenue:revenune
revenueCurrency:@"USD"
additionalParameters:adRevenueParams];
}
#pragma mark - ADXNativeAdFactoryDelegate
- (void)onSuccess:(NSString *)adUnitId nativeAd:(ADXNativeAd *)nativeAd {
NSLog(@"onSuccess : %@", adUnitId);
if([adUnitId isEqualToString:@"<ADX_NATIVE_AD_UNIT_ID>"]) {
self.nativeAd = nativeAd;
self.nativeAd.delegate = self;
__weak typeof(self) weakSelf = self;
self.nativeAd.paidEventHandler = ^(double eCPM) {
__strong typeof(self) strongSelf = weakSelf;
if(!strongSelf) { return; }
NSNumber * revenue = [NSNumber numberWithDouble:eCPM/1000];
[strongSelf handleAdRevenue:revenue];
};
UIView *nativeAdView = [[ADXNativeAdFactory sharedInstance]
getNativeAdView:@"<ADX_NATIVE_AD_UNIT_ID>"];
nativeAdView.frame = CGRectMake(0.0,
100.0,
320.0,
300.0);
[self.view addSubview:nativeAdView];
}
}
- (void)onFailure:(NSString *)adUnitId {
NSLog(@"onFailure : %@", adUnitId);
}
#pragma mark - ADXNativeAdDelegate
- (UIViewController *)viewControllerForPresentingModalView {
return self;
}
@end
import UIKit
import ADXLibrary
import FirebaseAnalytics
import AppsFlyerAdRevenue
class NativeAdViewController: UIViewController {
var nativeAd: ADXNativeAd?
override func viewDidLoad() {
super.viewDidLoad()
ADXNativeAdFactory.sharedInstance().setRenderingViewClass(
"<ADX_NATIVE_AD_UNIT_ID>",
renderingViewClass: NativeAdView.self)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
ADXNativeAdFactory.sharedInstance().add(self)
ADXNativeAdFactory.sharedInstance().loadAd("<ADX_NATIVE_AD_UNIT_ID>")
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
ADXNativeAdFactory.sharedInstance().remove(self)
}
func handleAdRevenue(revenue:NSNumber) {
// 1) Firebase Analytics
Analytics.logEvent(
AnalyticsEventAdImpression,
parameters: [
AnalyticsParameterAdPlatform: "AD(X)",
AnalyticsParameterAdUnitName: "ADX Native Ad",
AnalyticsParameterAdFormat: "NativeAd",
AnalyticsParameterValue: revenune,
AnalyticsParameterCurrency: "USD",
])
// 2) AppsFlyer
let adRevenueParams:[AnyHashable: Any] = [
"AdUnitName" : "ADX Native Ad",
"AdType" : "NativeAd",
]
AppsFlyerAdRevenue.shared().logAdRevenue(
monetizationNetwork: "AD(X)",
mediationNetwork: .custom,
eventRevenue: revenune,
revenueCurrency: "USD",
additionalParameters: adRevenueParams)
}
}
extension NativeAdViewController: ADXNativeAdFactoryDelegate,
ADXNativeAdDelegate
{
// MARK: - ADXNativeAdFactoryDelegate
public func onSuccess(_ adUnitId: String!, nativeAd: ADXNativeAd!) {
if (adUnitId == "<ADX_NATIVE_AD_UNIT_ID>") {
self.nativeAd = nativeAd
self.nativeAd?.delegate = self
self.nativeAd?.paidEventHandler = { [weak self] eCPM in
let revenue:NSNumber = (eCPM / 1000) as NSNumber
self?.handleAdRevenue(revenue: revenue)
}
let nativeAdView = ADXNativeAdFactory.sharedInstance()
.getNativeAdView("<ADX_NATIVE_AD_UNIT_ID>")
nativeAdView?.frame = CGRect(
x: (UIScreen.main.bounds.width - 300.0)/2,
y: 100.0,
width: 300.0,
height: 270.0)
self.view.addSubview(nativeAdView!)
} else {
print("fail to load")
}
}
public func onFailure(_ adUnitId: String!) {
print("onFailure :", adUnitId!)
}
// MARK: - ADXNativeAdDelegate
func viewControllerForPresentingModalView() -> UIViewController {
return self
}
}
Last updated