Advanced settings
Learn which features you can implement in Feed and how to configure them.
To change or implement placement features, use the SDK's default UI or implement your own classes. The diagram below shows the main Feed features.

Integrate Feed as a child view controller
In addition to the standard present and push integration methods, BuzzAd SDK for iOS supports integrating Feed as a child view controller.

To integrate Feed this way, add the child view controller to your parent view controller.
The following example adds Feed as a child view controller.
import BuzzAdBenefit
final class ContainerViewController: UIViewController {
let buzzAdFeed = BZVBuzzAdFeed { builder in }
override func viewDidLoad() {
super.viewDidLoad()
displayContentViewController(buzzAdFeed.viewController)
}
private func displayContentViewController(_ contentViewController: UIViewController) {
addChild(contentViewController)
contentViewController.view.frame = view.bounds
view.addSubview(contentViewController.view)
contentViewController.didMove(toParent: self)
}
}
@import BuzzAdBenefit;
@interface ContainerViewController ()
@property (nonatomic, strong, readonly) BZVBuzzAdFeed *buzzAdFeed;
@end
@implementation ContainerViewController
- (void)viewDidLoad {
[super viewDidLoad];
_buzzAdFeed = [BZVBuzzAdFeed feedWithBlock:^(BZVBuzzAdFeedBuilder * _Nonnull builder) {}];
[self displayContentViewController:[_buzzAdFeed viewController]];
}
- (void)displayContentViewController:(UIViewController*)contentViewController {
[self addChildViewController:contentViewController];
contentViewController.view.frame = self.view.bounds;
[self.view addSubview:contentViewController.view];
[contentViewController didMoveToParentViewController:self];
}
@end
Ad categories
Feed uses tabs and filters to group ads by type. Users can focus on the types they want to participate in, improving the experience and ad efficiency.
Category settings can be changed without editing code or redeploying your app. Contact your Buzzvil representative to change them.
Ad category tabs and filters
Configure the number of tabs, their names and their ad types. Up to three tabs are supported. The defaults are Ad rewards, which includes all non-shopping ad types, and Shopping rewards, which includes shopping ads.
Tabs can contain impression-based, action-based, video, social media or shopping ads. Each tab can include multiple types, except shopping ads, which cannot share a tab with other types.
Tabs with multiple ad types show filters such as “Click” and “Participate” to narrow the results.

Infinite scrolling
Feed's infinite scroll automatically loads more ads when users reach the end of the current ad list. It is enabled by default. You can enable or disable it without changing your app's code or releasing an update; contact your Buzzvil representative to change the setting.
Add entry points to your app UI
Implement your own views in icons, buttons, banners or other UI elements to create entry points into Feed.
See the diagram below for example UI components. The sample app includes code for several custom entry points.

Prerequisites
- Complete Feed basic setup.
Add an entry point
You can add a Feed entry point to a view controller. Implement a custom view that subclasses BZVFeedEntryView and add it as a subview.
Implement an entry point in code
The following example creates a custom view that subclasses BZVFeedEntryView and adds it as a subview of the view controller.
- Define a subclass of
BZVFeedEntryViewand implement its layout.
import BuzzAdBenefit
final class CustomFeedEntryView: BZVFeedEntryView {
let customSubview1 = UIView(frame: .zero)
let customSubview2 = UIView(frame: .zero)
required init?(coder: NSCoder) {
super.init(coder: coder)
setupView()
}
override init(frame: CGRect) {
super.init(frame: frame)
setupView()
}
private func setupView() {
// subview 초기화 및 LayoutConstraint 설정
...생략...
// 클릭이 가능한 영역을 지정합니다.
clickableViews = [customSubview1, customSubview2]
}
}
@import BuzzAdBenefit;
@interface CustomFeedEntryView: BZVFeedEntryView
@end
@interface CustomFeedEntryView ()
@property (nonatomic, strong, readonly) UIView *customSubview1;
@property (nonatomic, strong, readonly) UIView *customSubview2;
@end
@implementation CustomFeedEntryView
- (instancetype)initWithCoder:(NSCoder *)coder {
if (self = [super initWithCoder:coder]) {
[self setupView];
}
return self;
}
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
[self setupView];
}
return self;
}
- (void)setupView {
// subview 초기화 및 LayoutConstraint 설정
...생략...
// 클릭이 가능한 영역을 지정합니다.
self.clickableViews = @[self.customSubview1, self.customSubview2];
}
@end
- Initialize and use your subclass (
CustomFeedEntryView) ofBZVFeedEntryView.
import UIKit
final class ViewController: UIViewController {
private lazy var customFeedEntryView: CustomFeedEntryView = {
let view = CustomFeedEntryView(frame: .zero)
return view
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(customFeedEntryView)
// LayoutConstraint 설정
...
}
}
@interface ViewController ()
@property (nonatomic, strong, readonly) CustomFeedEntryView *customFeedEntryView;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
_customFeedEntryView = [[CustomFeedEntryView alloc] initWithFrame:CGRectZero];
[self.view addSubview:_customFeedEntryView];
// LayoutConstraint 설정
...
}
@end
Track user interactions
After creating an entry point, add setEntryName to track view impressions and user clicks. To use this feature, share the details of setEntryName with your Buzzvil representative.
import UIKit
final class ViewController: UIViewController {
private lazy var customFeedEntryView: CustomFeedEntryView = {
let view = CustomFeedEntryView(frame: .zero)
// 뷰에 대한 사용자 반응을 추적합니다.
view.setEntryName("your_custom_entry_point_1")
return view
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(customFeedEntryView)
// LayoutConstraint 설정
...
}
}
@interface ViewController ()
@property (nonatomic, strong, readonly) CustomFeedEntryView *customFeedEntryView;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
_customFeedEntryView = [[CustomFeedEntryView alloc] initWithFrame:CGRectZero];
// 뷰에 대한 사용자 반응을 추적합니다.
[_customFeedEntryView setEntryName:@"your_custom_entry_point_1"];
[self.view addSubview:_customFeedEntryView];
// LayoutConstraint 설정
...
}
@end
Display available points
Preview the available points, as shown below, to encourage users who have not yet entered Feed to participate.

Follow these steps to show available points in advance.
- Call
[BZVBuzzAdFeed load]to allocate Feed ads. - On success, the
onSuccesscallback is invoked. - See Load and display Feed ads. - In the
onSuccesscallback, callbuzzAdFeed.availableRewardsto retrieve the total available reward amount. - Update your view with that amount.
The following example refreshes Feed and updates the view whenever viewDidLoad runs.
final class ViewController: UIViewController {
override func viewDidLoad() {
...생략...
customFeedEntryView.buzzAdFeed.load {
self.updateFeedEntryView(reward: self.customFeedEntryView.buzzAdFeed.availableRewards)
} onFailure: { _ in
self.updateFeedEntryView(reward: 0)
}
}
private func updateFeedEntryView(reward: Int) {
// 적립 가능한 포인트 안내 문구 설정
}
}
@implementation ViewController
- (void)viewDidLoad {
...생략...
[_customFeedEntryView.buzzAdFeed loadOnSuccess:^{
[self updateFeedEntryWithReward:self->_customFeedEntryView.buzzAdFeed.availableRewards];
} onFailure:^(NSError * _Nonnull error) {
[self updateFeedEntryWithReward:0];
}];
}
- (void)updateFeedEntryWithReward:(NSInteger)reward {
// 적립 가능한 포인트 안내 문구 설정
}
@end

Implement a custom header
You can use the Feed header area as needed—for example, to explain the placement or show the rewards available to earn.

The following example changes the header.
Create a subclass of BZVFeedHeaderViewHolder and implement your custom header view. Register it in headerViewHolderClass of BZVFeedConfig.
final class CustomHeaderViewHolder: BZVFeedHeaderViewHolder {
let headerLabel = UILabel(frame: .zero)
override class func desiredHeight() -> CGFloat {
return 100.0
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setUpView()
}
override init(frame: CGRect) {
super.init(frame: frame)
setUpView()
}
func setUpView() {
addSubview(headerLabel)
// LayoutConstraint 설정
...
}
override func availableRewardDidUpdate(_ reward: Int) {
headerLabel.text = "리워드 \(reward)원"
}
}
@interface CustomFeedHeaderViewHolder : BZVFeedHeaderViewHolder
@end
@interface CustomFeedHeaderViewHolder ()
@property (nonatomic, strong, readonly) UILabel *headerLabel;
@end
@implementation CustomFeedHeaderViewHolder
+ (CGFloat)desiredHeight {
return 100;
}
- (instancetype)initWithCoder:(NSCoder *)coder {
if (self = [super initWithCoder:coder]) {
[self setupView];
}
return self;
}
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
[self setupView];
}
return self;
}
- (void)setupView {
_headerLabel = [[UILabel alloc] initWithFrame:CGRectZero];
[self addSubview:_headerLabel];
// LayoutConstraint 설정
...
}
- (void)availableRewardDidUpdate:(NSInteger)reward {
_headerLabel.text = [NSString stringWithFormat:@"리워드 %ld원", reward];
}
@end
let config = BZVFeedConfig { builder in
builder.unitId = "YOUR_FEED_UNIT_ID"
builder.headerViewHolderClass = CustomHeaderViewHolder.self
}
BZVFeedConfig *config = [BZVFeedConfig configWithBlock:^(BZVFeedConfigBuilder * _Nonnull builder) {
builder.unitId = @"YOUR_FEED_UNIT_ID";
builder.headerViewHolderClass = [CustomFeedHeaderViewHolder class];
}];
Implement a custom base reward notification
Users receive points periodically when entering Feed, even without participating in an ad. Customize the base reward notification to improve the experience.

The following example implements a custom base reward notification.
- Create a class that subclasses
BZVFeedBaseRewardViewHolder.
final class CustomFeedBaseRewardViewHolder: BZVFeedBaseRewardViewHolder {
let baseRewardLabel = UILabel(frame: .zero)
override class func desiredHeight() -> CGFloat {
return 100.0
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setUpView()
}
override init(frame: CGRect) {
super.init(frame: frame)
setUpView()
}
func setUpView() {
addSubview(baseRewardLabel)
// LayoutConstraint 설정
...
}
override func didUpdateBaseReward(_ baseReward: Int) {
baseRewardLabel.text = "Received \(baseReward) points"
}
}
@interface CustomFeedBaseRewardViewHolder : BZVFeedBaseRewardViewHolder
@end
@interface CustomFeedBaseRewardViewHolder ()
@property (nonatomic, strong, readonly) UILabel *baseRewardLabel;
@end
@implementation CustomFeedBaseRewardViewHolder
+ (CGFloat)desiredHeight {
return 100;
}
- (instancetype)initWithCoder:(NSCoder *)coder {
if (self = [super initWithCoder:coder]) {
[self setupView];
}
return self;
}
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
[self setupView];
}
return self;
}
- (void)setupView {
_baseRewardLabel = [[UILabel alloc] initWithFrame:CGRectZero];
[self addSubview:_baseRewardLabel];
// LayoutConstraint 설정
...
}
- (void)didUpdateBaseReward:(NSInteger)baseReward {
_baseRewardLabel.text = [NSString stringWithFormat:@"Received %@ points", @(baseReward)];
}
@end
- Set the
CustomFeedBaseRewardViewHoldercreated in the previous step onBZVFeedConfig.
let config = BZVFeedConfig { builder in
builder.unitId = "YOUR_FEED_UNIT_ID"
builder.baseRewardViewHolderClass = CustomFeedBaseRewardViewHolder.self
}
BZVFeedConfig *config = [BZVFeedConfig configWithBlock:^(BZVFeedConfigBuilder * _Nonnull builder) {
builder.unitId = @"YOUR_FEED_UNIT_ID";
builder.baseRewardViewHolderClass = [CustomFeedBaseRewardViewHolder class];
}];
Implement a custom ad design
BuzzAd SDK for iOS provides standard ads and shopping reward ads.
- Standard ads: All ad types other than shopping ads.
- Shopping reward ads: Users who make a purchase through an ad earn points equivalent to a percentage of the purchase amount.

Both types contain multiple elements that describe the ad. See the table below for the elements and UI of standard ads and shopping reward ads.
| Ad type | Component | Description |
|---|---|---|
| Shared: standard and shopping | Ad title (required) | The ad title. A maximum of 10 characters is recommended. You may truncate part of the text with an ellipsis as needed, regardless of length. |
| Ad creative (required) |
| |
| Ad description (required) | A detailed ad description. A maximum of 40 characters is recommended. You may truncate part of the text with an ellipsis as needed, regardless of length. | |
| Advertiser icon (required) |
| |
| CTA button (required) |
✏️ Note See Implement a custom CTA button to change the design. | |
| Ad disclosure (recommended) | Text identifying the content as advertising, such as 광고, ad, 스폰서 or Sponsored. | |
| Shopping rewards | OriginalPrice View (recommended) | Displays the original product price. |
| Price View (recommended) | Displays the discounted product price. | |
| DiscountRate View (recommended) | Displays the discount rate. Calculate it from the original and discounted prices. |
Implement a custom standard ad design
Follow these steps to customize standard ads.
- Implement a subclass of
BZVFeedAdViewHolderfor standard ads.
- Add
BZVNativeAdView,BZVMediaViewandBZVDefaultCtaViewas subviews. - In
renderAd, useBZVNativeAdViewBinderto bind ad data (NativeAd) to theCustomFeedAdViewHolderyou created.
import UIKit
import BuzzAdBenefit
final class CustomFeedAdViewHolder: BZVFeedAdViewHolder {
let nativeAdView = BZVNativeAdView(frame: .zero)
let mediaView = BZVMediaView(frame: .zero)
let iconImageView = UIImageView(frame: .zero)
let titleLabel = UILabel(frame: .zero)
let descriptionLabel = UILabel(frame: .zero)
let ctaView = BZVDefaultCtaView(frame: .zero)
lazy var viewBinder = BZVNativeAdViewBinder { builder in
builder.nativeAdView = self.nativeAdView
builder.mediaView = self.mediaView
builder.iconImageView = self.iconImageView
builder.titleLabel = self.titleLabel
builder.descriptionLabel = self.descriptionLabel
builder.ctaView = self.ctaView
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setUpView()
}
override init(frame: CGRect) {
super.init(frame: frame)
setUpView()
}
func setUpView() {
addSubview(nativeAdView)
nativeAdView.addSubview(mediaView)
nativeAdView.addSubview(iconImageView)
nativeAdView.addSubview(titleLabel)
nativeAdView.addSubview(descriptionLabel)
nativeAdView.addSubview(ctaView)
// LayoutConstraint 설정
...
}
override func renderAd(_ ad: BZVNativeAd) {
viewBinder.bind(with: ad)
}
}
@import UIKit;
@import BuzzAdBenefit;
@interface CustomFeedAdViewHolder : BZVFeedAdViewHolder
@end
@interface CustomFeedAdViewHolder ()
@property (nonatomic, strong, readonly) BZVNativeAdView *nativeAdView;
@property (nonatomic, strong, readonly) BZVMediaView *mediaView;
@property (nonatomic, strong, readonly) UIImageView *iconImageView;
@property (nonatomic, strong, readonly) UILabel *titleLabel;
@property (nonatomic, strong, readonly) UILabel *descriptionLabel;
@property (nonatomic, strong, readonly) BZVDefaultCtaView *ctaView;
@property (nonatomic, strong, readonly) BZVNativeAdViewBinder *viewBinder;
@end
@implementation CustomFeedAdViewHolder
- (instancetype)initWithCoder:(NSCoder *)coder {
if (self = [super initWithCoder:coder]) {
[self setupView];
}
return self;
}
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
[self setupView];
}
return self;
}
- (void)setupView {
_nativeAdView = [[BZVNativeAdView alloc] initWithFrame:CGRectZero];
[self addSubview:_nativeAdView];
_mediaView = [[BZVMediaView alloc] initWithFrame:CGRectZero];
[_nativeAdView addSubview:_mediaView];
_iconImageView = [[UIImageView alloc] initWithFrame:CGRectZero];
[_nativeAdView addSubview:_iconImageView];
_titleLabel = [[UILabel alloc] initWithFrame:CGRectZero];
[_nativeAdView addSubview:_titleLabel];
_descriptionLabel = [[UILabel alloc] initWithFrame:CGRectZero];
[_nativeAdView addSubview:_descriptionLabel];
_ctaView = [[BZVDefaultCtaView alloc] initWithFrame:CGRectZero];
[_nativeAdView addSubview:_ctaView];
_viewBinder = [BZVNativeAdViewBinder viewBinderWithBlock:^(BZVNativeAdViewBinderBuilder * _Nonnull builder) {
builder.nativeAdView = self.nativeAdView;
builder.mediaView = self.mediaView;
builder.iconImageView = self.iconImageView;
builder.titleLabel = self.titleLabel;
builder.descriptionLabel = self.descriptionLabel;
builder.ctaView = self.ctaView;
}];
// LayoutConstraint 설정
...
}
- (void)renderAd:(BZVNativeAd *)ad {
[_viewBinder bindWithNativeAd:ad];
}
@end
Optionally receive ad callbacks through BZVNativeAdEventDelegate.
import UIKit
import BuzzAdBenefit
final class CustomFeedAdViewHolder: BZVFeedAdViewHolder, BZVNativeAdEventDelegate {
override func renderAd(_ ad: BZVNativeAd) {
viewBinder.bind(with: ad)
ad.delegate = self
}
// MARK: BZVNativeAdEventDelegate
func didImpress(_ nativeAd: BZVNativeAd) {
}
func didClick(_ nativeAd: BZVNativeAd) {
}
func didRequestReward(for nativeAd: BZVNativeAd) {
}
func didReward(for nativeAd: BZVNativeAd, with result: BZVRewardResult) {
}
func didParticipateAd(_ nativeAd: BZVNativeAd) {
}
}
// 부가 기능: 광고 콜백 이벤트 수신 기능을 추가합니다.
@interface CustomFeedAdViewHolder () <BZVNativeAdEventDelegate>
@end
@implementation CustomFeedAdViewHolder
- (void)renderAd:(BZVNativeAd *)ad {
[_viewBinder bindWithNativeAd:ad];
ad.delegate = self;
}
#pragma mark - BZVNativeAdEventDelegate
- (void)didImpressAd:(BZVNativeAd *)nativeAd {
}
- (void)didClickAd:(BZVNativeAd *)nativeAd {
}
- (void)didRequestRewardForAd:(BZVNativeAd *)nativeAd {
}
- (void)didRewardForAd:(BZVNativeAd *)nativeAd withResult:(BZVRewardResult)result {
}
- (void)didParticipateAd:(BZVNativeAd *)nativeAd {
}
@end
- Set your
CustomFeedAdViewHolderas the ad ViewHolder inBZVFeedConfig.
let config = BZVFeedConfig { builder in
builder.unitId = "YOUR_FEED_UNIT_ID"
builder.adViewHolderClass = CustomFeedAdViewHolder.self
}
BZVFeedConfig *config = [BZVFeedConfig configWithBlock:^(BZVFeedConfigBuilder * _Nonnull builder) {
builder.unitId = @"YOUR_FEED_UNIT_ID";
builder.adViewHolderClass = [CustomFeedAdViewHolder class];
}];
Implement a custom shopping ad design
Follow these steps to customize shopping ads.
- Implement a subclass of
BZVFeedAdViewHolderfollowing Implement a custom standard ad design.
- Add
priceLabel,originalPriceLabelanddiscountPercentageLabelto the standard ad layout.
import UIKit
import BuzzAdBenefit
final class CustomFeedCpsAdViewHolder: BZVFeedAdViewHolder {
let nativeAdView = BZVNativeAdView(frame: .zero)
let mediaView = BZVMediaView(frame: .zero)
let iconImageView = UIImageView(frame: .zero)
let titleLabel = UILabel(frame: .zero)
let descriptionLabel = UILabel(frame: .zero)
let ctaView = BZVDefaultCtaView(frame: .zero)
let priceLabel = UILabel(frame: .zero)
let originalPriceLabel = UILabel(frame: .zero)
let discountRateLabel = UILabel(frame: .zero)
lazy var viewBinder = BZVNativeAdViewBinder { builder in
builder.nativeAdView = self.nativeAdView
builder.mediaView = self.mediaView
builder.iconImageView = self.iconImageView
builder.titleLabel = self.titleLabel
builder.descriptionLabel = self.descriptionLabel
builder.ctaView = self.ctaView
// 부가 기능: 텍스트를 클릭할 수 있도록 설정합니다.
builder.clickableViews = [
self.mediaView,
self.ctaView,
self.priceLabel,
self.originalPriceLabel,
self.discountRateLabel,
]
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setUpView()
}
override init(frame: CGRect) {
super.init(frame: frame)
setUpView()
}
private func setUpView() {
// 광고 레이아웃 컴포넌트를 생성합니다.
...생략...
nativeAdView.addSubview(priceLabel)
nativeAdView.addSubview(originalPriceLabel)
nativeAdView.addSubview(discountRateLabel)
// LayoutConstraint 설정
...생략...
}
override func renderAd(_ ad: BZVNativeAd) {
viewBinder.bind(with: ad)
let product = ad.product
if product.discountedPrice != 0 {
// 할인이 있는 쇼핑 광고
priceLabel.text = NSNumber(value: product.discountedPrice).formattedString()
originalPriceLabel.text = NSNumber(value: product.price).formattedString()
let discountRate = (1 - product.discountedPrice / product.price) * 100
discountRateLabel.text = String(format: "%d%%", Int(discountRate))
} else {
// 할인이 없는 쇼핑 광고
priceLabel.text = NSNumber(value: product.discountedPrice).formattedString()
originalPriceLabel.text = NSNumber(value: product.price).formattedString()
discountRateLabel.text = "0%%"
}
}
}
extension NSNumber {
func formattedString() -> String? {
let numberFormatter = NumberFormatter()
numberFormatter.numberStyle = .decimal
return numberFormatter.string(from: self)
}
}
@import UIKit;
@import BuzzAdBenefit;
@interface CustomFeedCpsAdViewHolder : BZVFeedAdViewHolder
@end
@interface CustomFeedCpsAdViewHolder ()
@property (nonatomic, strong, readonly) BZVNativeAdView *nativeAdView;
@property (nonatomic, strong, readonly) BZVMediaView *mediaView;
@property (nonatomic, strong, readonly) UIImageView *iconImageView;
@property (nonatomic, strong, readonly) UILabel *titleLabel;
@property (nonatomic, strong, readonly) UILabel *descriptionLabel;
@property (nonatomic, strong, readonly) BZVDefaultCtaView *ctaView;
@property (nonatomic, strong, readonly) UILabel *priceLabel;
@property (nonatomic, strong, readonly) UILabel *originalPriceLabel;
@property (nonatomic, strong, readonly) UILabel *discountRateLabel;
@property (nonatomic, strong, readonly) BZVNativeAdViewBinder *viewBinder;
@end
@implementation CustomFeedCpsAdViewHolder
- (instancetype)initWithCoder:(NSCoder *)coder {
if (self = [super initWithCoder:coder]) {
[self setupView];
}
return self;
}
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
[self setupView];
}
return self;
}
- (void)setupView {
// 광고 레이아웃 컴포넌트를 생성합니다.
...생략...
_priceLabel = [[UILabel alloc] initWithFrame:CGRectZero];
[_nativeAdView addSubview:_priceLabel];
_originalPriceLabel = [[UILabel alloc] initWithFrame:CGRectZero];
[_nativeAdView addSubview:_originalPriceLabel];
_discountRateLabel = [[UILabel alloc] initWithFrame:CGRectZero];
[_nativeAdView addSubview:_discountRateLabel];
_viewBinder = [BZVNativeAdViewBinder viewBinderWithBlock:^(BZVNativeAdViewBinderBuilder * _Nonnull builder) {
builder.nativeAdView = self.nativeAdView;
builder.mediaView = self.mediaView;
builder.titleLabel = self.titleLabel;
builder.ctaView = self.ctaView;
// 부가 기능: 뷰를 클릭할 수 있도록 설정합니다.
builder.clickableViews = @[
self.mediaView,
self.priceLabel,
self.originalPriceLabel,
self.discountRateLabel,
self.ctaView,
];
}];
// LayoutConstraint 설정
...생략...
}
- (void)renderAd:(BZVNativeAd *)ad {
[_viewBinder bindWithNativeAd:ad];
BZVNativeAdProduct *product = ad.product;
if (product.discountedPrice != 0) {
// 할인이 있는 쇼핑 광고
_priceLabel.text = [self formattedStringFromNumber:@(product.discountedPrice)];
_originalPriceLabel.text = [self formattedStringFromNumber:@(product.price)];
CGFloat discountRate = (1 - product.discountedPrice / product.price) * 100;
_discountRateLabel.text = [NSString stringWithFormat:@"%.f%%", discountRate];
} else {
// 할인이 없는 쇼핑 광고
_priceLabel.text = [self formattedStringFromNumber:@(product.price)];
_originalPriceLabel.text = [self formattedStringFromNumber:@(product.price)];
_discountRateLabel.hidden = YES;
}
}
- (NSString *)formattedStringFromNumber:(NSNumber *)number {
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
formatter.numberStyle = NSNumberFormatterDecimalStyle;
return [formatter stringFromNumber:number];
}
@end
- Set the
CustomFeedAdViewHolderfrom the previous step as the shopping ad ViewHolder inBZVFeedConfig.
let config = BZVFeedConfig { builder in
builder.unitId = "YOUR_FEED_UNIT_ID"
builder.cpsAdViewHolderClass = CustomCpsAdViewHolder.self
}
BZVFeedConfig *config = [BZVFeedConfig configWithBlock:^(BZVFeedConfigBuilder * _Nonnull builder) {
builder.unitId = @"YOUR_FEED_UNIT_ID";
builder.cpsAdViewHolderClass = [CustomCpsAdViewHolder class];
}];
Implement a custom CTA button
You can implement your own CTA button instead of using the default UI provided by BuzzAd SDK for iOS.

To implement your own CTA button, create a subclass of UIView that conforms to the CtaViewProtocol protocol.
The following example creates a CTA View class using UIStackView.
import UIKit
import BuzzAdBenefit
final class CustomCtaView: UIStackView, BZVCtaViewProtocol {
let rewardImageView = UIImageView(frame: .zero)
let rewardLabel = UILabel(frame: .zero)
let ctaLabel = UILabel(frame: .zero)
required init(coder: NSCoder) {
super.init(coder: coder)
setUpView()
}
override init(frame: CGRect) {
super.init(frame: frame)
setUpView()
}
private func setUpView() {
addArrangedSubview(rewardImageView)
addArrangedSubview(rewardLabel)
addArrangedSubview(ctaLabel)
}
// MARK: BZVCtaViewProtocol
func renderRewardNotAvailableViewState(withCtaText ctaText: String) {
// 리워드가 없는 광고에 대한 CTA 뷰 레이아웃을 정의합니다.
rewardImageView.isHidden = true
rewardLabel.isHidden = true
ctaLabel.text = ctaText
}
func renderRewardAvailableViewState(withCtaText ctaText: String, reward: Int) {
// 리워드가 있는 광고에 대한 CTA 뷰 레이아웃을 정의합니다.
rewardImageView.isHidden = false
rewardLabel.isHidden = false
rewardImageView.image = UIImage(named: "YOUR_REWARD_IMAGE")
rewardLabel.text = "+\(reward)"
ctaLabel.text = ctaText
}
func renderParticipatingViewState(withCtaText ctaText: String) {
// 참여 확인 중인 광고에 대한 CTA 뷰 레이아웃을 정의합니다.
rewardImageView.isHidden = true
rewardLabel.isHidden = true
ctaLabel.text = "YOUR_PARTICIPATING_TEXT"
}
func renderParticipatedViewState(withCtaText ctaText: String) {
// 참여 완료한 광고에 대한 CTA 뷰 레이아웃을 정의합니다.
rewardImageView.isHidden = false
rewardLabel.isHidden = true
rewardImageView.image = UIImage(named: "YOUR_PARTICIPATED_IMAGE")
ctaLabel.text = "YOUR_PARTICIPATED_TEXT"
}
}
@import UIKit;
@import BuzzAdBenefit;
@interface CustomCtaView : UIStackView <BZVCtaViewProtocol>
@end
@interface CustomCtaView ()
@property (nonatomic, strong, readonly) UIImageView *rewardImageView;
@property (nonatomic, strong, readonly) UILabel *rewardLabel;
@property (nonatomic, strong, readonly) UILabel *ctaLabel;
@end
@implementation CustomCtaView
- (instancetype)initWithCoder:(NSCoder *)coder {
if (self = [super initWithCoder:coder]) {
[self setupView];
}
return self;
}
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
[self setupView];
}
return self;
}
- (void)setupView {
_rewardImageView = [[UIImageView alloc] initWithFrame:CGRectZero];
[self addArrangedSubview:_rewardImageView];
_rewardLabel = [[UILabel alloc] initWithFrame:CGRectZero];
[self addArrangedSubview:_rewardLabel];
_ctaLabel = [[UILabel alloc] initWithFrame:CGRectZero];
[self addArrangedSubview:_ctaLabel];
}
#pragma mark - BZVCtaViewProtocol
- (void)renderRewardNotAvailableViewStateWithCtaText:(NSString *)ctaText {
// 리워드가 없는 광고에 대한 CTA 뷰 레이아웃을 정의합니다.
[_rewardImageView setHidden:YES];
[_rewardLabel setHidden:YES];
[_ctaLabel setText:ctaText];
}
- (void)renderRewardAvailableViewStateWithCtaText:(NSString *)ctaText reward:(NSInteger)reward {
// 리워드가 있는 광고에 대한 CTA 뷰 레이아웃을 정의합니다.
[_rewardImageView setHidden:NO];
[_rewardLabel setHidden:NO];
[_rewardImageView setImage:[UIImage imageNamed:@"YOUR_REWARD_IMAGE"]];
[_rewardLabel setText:[NSString stringWithFormat:@"+%ld", reward]];
[_ctaLabel setText:ctaText];
}
- (void)renderParticipatingViewStateWithCtaText:(NSString *)ctaText {
// 참여 확인 중인 광고에 대한 CTA 뷰 레이아웃을 정의합니다.
[_rewardImageView setHidden:YES];
[_rewardLabel setHidden:YES];
[_ctaLabel setText:@"YOUR_PARTICIPATING_TEXT"];
}
- (void)renderParticipatedViewStateWithCtaText:(NSString *)ctaText {
// 참여 완료한 광고에 대한 CTA 뷰 레이아웃을 정의합니다.
[_rewardImageView setHidden:NO];
[_rewardLabel setHidden:YES];
[_rewardImageView setImage:[UIImage imageNamed:@"YOUR_PARTICIPATED_IMAGE"]];
[_ctaLabel setText:@"YOUR_PARTICIPATED_TEXT"];
}
@end
Implement a custom no-ads screen
If no ads are available when Feed opens, a no-ads UI appears. You can replace it with your own design.

The following example customizes the no-ads message.
Create a class that subclasses BZVFeedErrorViewHolder, then assign it to the errorViewHolderClass property of BZVFeedConfig. Optionally, override updateViewWithError to update the UI based on the error.
import UIKit
import BuzzAdBenefit
final class CustomFeedErrorViewHolder: BZVFeedErrorViewHolder {
let imageView = UIImageView(image: UIImage(named: "YOUR_ERROR_IMAGE_NAME"))
required init?(coder: NSCoder) {
super.init(coder: coder)
setupView()
}
override init(frame: CGRect) {
super.init(frame: frame)
setupView()
}
private func setupView() {
addSubview(imageView)
// LayoutConstraint 설정
...
}
override func updateViewWithError(_ error: Error) {
// error에 따라 UI를 업데이트 할 수 있는 코드 작성
}
}
let config = BZVFeedConfig { builder in
builder.unitId = "YOUR_FEED_UNIT_ID"
builder.errorViewHolderClass = CustomFeedErrorViewHolder.self
}
@import BuzzAdBenefit;
@interface CustomFeedErrorViewHolder : BZVFeedErrorViewHolder
@end
@interface CustomFeedErrorViewHolder ()
@property (nonatomic, strong, readonly) UIImageView *imageView;
@end
@implementation CustomFeedErrorViewHolder
- (instancetype)initWithCoder:(NSCoder *)coder {
if (self = [super initWithCoder:coder]) {
[self setupView];
}
return self;
}
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
[self setupView];
}
return self;
}
- (void)setupView {
UIImage *image = [UIImage imageNamed:@"YOUR_ERROR_IMAGE_NAME"];
_imageView = [[UIImageView alloc] initWithImage:image];
[self addSubview:_imageView];
// LayoutConstraint 설정
...
}
- (void)updateViewWithError:(NSError *)error {
// error에 따라 UI를 업데이트 할 수 있는 코드 작성
}
@end
BZVFeedConfig *config = [BZVFeedConfig configWithBlock:^(BZVFeedConfigBuilder * _Nonnull builder) {
builder.unitId = @"YOUR_FEED_UNIT_ID";
builder.headerViewHolderClass = [CustomFeedErrorViewHolder class];
}];
Tracking authorization
BuzzAd SDK for iOS provides features for requesting App Tracking Transparency authorization on iOS 14 and later. Feed can display a dialog and a banner to help users grant tracking permission.
Display the tracking authorization dialog when Feed opens
When enabled, the dialog appears when users on iOS 14 or later enter Feed without having granted tracking permission.

The following example displays the tracking authorization dialog when the user opens Feed.
let config = BZVFeedConfig { builder in
builder.unitId = "YOUR_FEED_UNIT_ID"
builder.shouldShowAppTrackingTransparencyDialog = true
}
BZVFeedConfig *config = [BZVFeedConfig configWithBlock:^(BZVFeedConfigBuilder * _Nonnull builder) {
builder.unitId = @"YOUR_FEED_UNIT_ID";
builder.shouldShowAppTrackingTransparencyDialog = YES;
}];
Add a tracking authorization information banner
You can display a banner at the top of Feed explaining opportunities to earn more rewards and encouraging users to allow tracking. It appears only for users who have denied permission.

The following example adds the tracking authorization information banner.
let config = BZVFeedConfig { builder in
builder.unitId = "YOUR_FEED_UNIT_ID"
builder.shouldShowAppTrackingTransparencyGuideBanner = true
}
BZVFeedConfig *config = [BZVFeedConfig configWithBlock:^(BZVFeedConfigBuilder * _Nonnull builder) {
builder.unitId = @"YOUR_FEED_UNIT_ID";
builder.shouldShowAppTrackingTransparencyGuideBanner = YES;
}];
You can also add a property to the Info.plist file to change the URL opened when the banner is clicked.
<key>BuzzAdAppTrackingTransparencyLandingURL</key>
<string>YOUR_ATT_DESCRIPTION_URL</string>
Check the ad type
The BuzzAd Android SDK provides APIs to identify ad types so you can offer different rewards or experiences for impression-based, action-based and other ads. See Check the ad type in Frequently asked questions.
UI for consent to share personal information with third parties
BuzzAd SDK for iOS provides a UI for obtaining consent to share personal information.
The previous function has been deprecated. For BuzzAd iOS v3.21.x and later, we recommend removing the code below. The setting can be controlled remotely without this code.
BuzzAdBenefit.privacyPolicyManagerPrivacyPolicyManager.presentConsentForm()PrivacyPolicyManager.grantConsent()PrivacyPolicyManager.revokeConsent()PrivacyPolicyManager.isConsentGranted()
