# Native Ad

## 1. 기본 요건

* [<mark style="color:blue;">ADX iOS SDK</mark>](/adx/ios/integrate.md#adxlibrary-cocoapods)를 프로젝트에 추가합니다.
* Native Ad용으로 발급받은 Ad Unit ID를 사용합니다.
* 광고를 요청하기 전에 [<mark style="color:blue;">SDK 초기화</mark>](https://platform-business.gitbook.io/adx/ios/sdk-integration/ad-formats/pages/O5ozkJcMAyY6hEZgu6km#1.-initialize-and-gdpr-content-ui)를 먼저 진행합니다.
  * **SDK 초기화는 앱 실행 시 한 번만 호출**하여 주시고, **광고 요청은 초기화가 완료된 후**에 이뤄져야 합니다.
  * iOS 14 이상 지원하는 경우, [<mark style="color:blue;">ATT(App Tracking Transparency)</mark>](/adx/ios/supporting-ios-14/app-tracking-transparency.md) 권한 요청 완료 후 광고를 요청해주세요.

## 2. 레이아웃 설정

Native Ad의 경우, 광고에 사용 될 레이아웃을 직접 구성해야 합니다. 필수 구성 요소들은 다음과 같습니다.

* **Title** : `UILabel`
* **Main Text** : `UILabel`
* <mark style="color:red;">**Call-To-Action**</mark> <mark style="color:red;">:</mark> <mark style="color:red;">`UIButton`</mark>
* **Icon Image** : `UIImageView`
* **Main Image** : `UIImageView`
* **Privacy Icon** : `UIImageView`
* **Ad Tag** : `UIView (UILabel 또는 UIImageView)`

{% hint style="info" %}
Ad Tag 요소는 "**Ad**" 또는 "**Advertisement**" 문자열을 보여주는 UI 구성 요소를 포함하여, 광고라는 것을 명확하게 사용자에게 인식을 시켜야 합니다. 이 요소가 포함되지 않으면, 일부 광고 네트워크의 광고 송출이 중단될 수 있습니다.
{% endhint %}

이 요소들은 반드시 포함하여 구성하여 주시고, 광고 컨텐츠를 덮는 View가 없어야 합니다. 또한 텍스트 변경, 이미지 변경, 터치 시 액션 변경 등 광고 컨텐츠에 관련된 부분을 가공하거나 변경하지 않도록 주의 부탁드립니다.

{% tabs %}
{% tab title="Objective-C" %}

```objectivec
// 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
```

{% endtab %}

{% tab title="Swift" %}

```swift
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
    }
}
```

{% endtab %}
{% endtabs %}

## 3. 구현

Native Ad는 다음 2가지 방법 중 하나로 광고를 로드할 수 있습니다.

### Case 1: 하나의 View를 사용하는 경우

1. `ADXNativeAdFactory`의 `setRenderingViewClass:renderingViewClass:`메서드를 사용하여 `RenderingViewClass`를 구성합니다.
2. `ADXNativeAdFactoryDelegate` callback을 등록합니다.
3. `ADXNativeAdFactoryDelegate`의 `loadAd`를 호출하여 광고를 로드합니다.
4. 광고 로드가 완료되면 `ADXNativeAdFactoryDelegate`의 `onSuccess:nativeAd:`가 호출됩니다.

{% tabs %}
{% tab title="Objective-C" %}

```objectivec
#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
```

{% endtab %}

{% tab title="Swift" %}

```swift
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
    }
}
```

{% endtab %}
{% endtabs %}

### Case 2: UITableView / UICollectionView에서 AdPlacer를 사용하는 경우

1. `ADXNativeAdFactory`의 `setRenderingViewClass:renderingViewClass:` 메서드를 사용하여 `RenderingViewClass`를 구성합니다.
2. 로드할 광고의 크기를 설정합니다.
   * 광고의 크기는 xib파일에서 사이즈를 지정하시거나 frame으로 세팅해주셔야 합니다.
3. `ADXAdPositioning`을 이용하여 고정 및 반복 위치를 지정합니다.
   * `addFixedIndexPath`: 고정할 광고 위치
   * `enableRepeatingPositionsWithInterval`: 광고 반복 위치
4. `loadAdsForAdUnitID:` 를 호출하여 광고를 로드합니다.

{% tabs %}
{% tab title="Objective-C" %}

```objectivec
#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>"];
}

```

{% endtab %}

{% tab title="Swift" %}

```swift
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
    }
}
```

{% endtab %}
{% endtabs %}

{% hint style="info" %}
Cell의 position 정보가 달라지기 때문에, `UITableView`는 **`ADXTableViewAdPlacer.h`**, `UICollectionView`는 **`ADXCollectionViewAdPlacer.h`** 파일을 참고하시어 대체 메서드를 사용해주시기 바랍니다.
{% endhint %}

## 4. Ad Revenue (paidEventHandler)

광고 노출에 대한 예상 광고 수익을 확인할 수 있습니다.

{% hint style="info" %}

* 아래 예제와 같이' `paidEventHandler`' 를 사용하여 예상되는 eCPM 값을 확인할 수 있습니다.
* 'ADXCollectionViewAdPlacer' 및 'ADXTableViewAdPlacer' 를 사용하는 경우, 이 기능은 지원되지 않습니다.
* 미디에이션 설정 과정에서 수동적으로 설정한 값과 정확한 값이 섞여 있어서 **예상 값으로 사용**하시는 것을 권장드립니다.
* eCPM의 통화(Currency) 단위는 USD입니다.
* 광고 매출 데이터를 MMP와 연동할 수 있습니다. 자세한 사항은 아래 SDK 연동 가이드를 참조해 주십시오.
  * [애드저스트(Adjust)의 AD(X) SDK 연동 가이드](https://dev.adjust.com/ko/sdk/ios/integrations/adx)
  * [에어브릿지(Airbridge)의 AD(X) SDK 연동 가이드](https://help.airbridge.io/ko/developers/ad-x-ad-revenue-integration#sdk-%EC%97%B0%EB%8F%99%ED%95%98%EA%B8%B0-ios)
    {% endhint %}

{% tabs %}
{% tab title="Objective-C" %}

```objectivec
#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
```

{% endtab %}

{% tab title="Swift" %}

```swift
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
    }
}
```

{% endtab %}
{% endtabs %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://platform-business.gitbook.io/adx/ios/sdk-integration/ad-formats/native-ad.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
