> ## Documentation Index
> Fetch the complete documentation index at: https://docs.adrop.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Banner Ads

> Guide to implementing banner ads in iOS apps.

## Overview

Banner ads are rectangular ads displayed in a portion of the screen. They can be used in both UIKit and SwiftUI.

### Key Features

* Can be fixed at the top, bottom, or middle of the screen
* Supports both image and video ads
* Supports both UIKit and SwiftUI
* Ad event handling through delegates

<Note>
  Use test unit ID in development environment: `PUBLIC_TEST_UNIT_ID_320_100`
</Note>

***

## UIKit Implementation

In UIKit environment, implement banner ads using the `AdropBanner` class.

### Basic Implementation

```swift theme={null}
import UIKit
import AdropAds

class ViewController: UIViewController {
    private var banner: AdropBanner?

    override func viewDidLoad() {
        super.viewDidLoad()

        // 1. Create banner instance
        banner = AdropBanner(unitId: "YOUR_UNIT_ID")

        // 2. Set delegate
        banner?.delegate = self

        // 3. Add to view hierarchy
        if let bannerView = banner {
            view.addSubview(bannerView)

            // 4. Setup Auto Layout
            bannerView.translatesAutoresizingMaskIntoConstraints = false
            NSLayoutConstraint.activate([
                bannerView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
                bannerView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
                bannerView.widthAnchor.constraint(equalToConstant: 320),
                bannerView.heightAnchor.constraint(equalToConstant: 100)
            ])
        }

        // 5. Load ad
        banner?.load()
    }

    deinit {
        // 6. Remove banner before memory deallocation
        banner?.removeFromSuperview()
        banner = nil
    }
}

// MARK: - AdropBannerDelegate
extension ViewController: AdropBannerDelegate {
    func onAdReceived(_ banner: AdropBanner) {
        print("Banner ad received successfully")
    }

    func onAdFailedToReceive(_ banner: AdropBanner, _ errorCode: AdropErrorCode) {
        print("Banner ad failed to receive: \(errorCode)")
    }

    func onAdImpression(_ banner: AdropBanner) {
        print("Banner ad impression")
    }

    func onAdClicked(_ banner: AdropBanner) {
        print("Banner ad clicked")
    }
}
```

### AdropBanner Initialization

<ParamField body="unitId" type="String" required>
  Unit ID created in Ad Control Console
</ParamField>

```swift theme={null}
let banner = AdropBanner(unitId: "YOUR_UNIT_ID")
```

### Load Ad

Call the `load()` method to request an ad after adding the banner to the screen.

```swift theme={null}
banner?.load()
```

<Warning>
  Call `load()` when the banner is visible on screen. Loading when not visible may result in inaccurate impression measurements.
</Warning>

### Set Context ID

You can set a Context ID for [contextual targeting](/sdk/ios/targeting#contextual-targeting).

```swift theme={null}
// contextId is read-only — set via initializer
let banner = AdropBanner(unitId: "YOUR_UNIT_ID", contextId: "article_123")
```

***

## SwiftUI Implementation

In SwiftUI environment, implement banner ads using `AdropBannerRepresented`.

### Basic Implementation

```swift theme={null}
import SwiftUI
import AdropAds

struct ContentView: View {
    @State private var represented = AdropBannerRepresented(unitId: "YOUR_UNIT_ID")

    var body: some View {
        VStack {
            Spacer()

            Text("Main Content")

            Spacer()

            // Banner ad
            represented
                .frame(width: 320, height: 100)
                .onAppear {
                    represented.banner.load()
                }
        }
    }
}
```

### Delegate Handling

You can handle ad events through delegates.

```swift theme={null}
import SwiftUI
import AdropAds

struct ContentView: View {
    @StateObject private var bannerDelegate = BannerDelegate()
    @State private var represented = AdropBannerRepresented(unitId: "YOUR_UNIT_ID")

    var body: some View {
        VStack {
            Spacer()

            if bannerDelegate.isLoaded {
                Text("Ad loaded successfully")
            }

            represented
                .frame(width: 320, height: 100)
                .onAppear {
                    represented.delegate = bannerDelegate
                    represented.banner.load()
                }
        }
    }
}

// Delegate implementation
class BannerDelegate: NSObject, ObservableObject, AdropBannerDelegate {
    @Published var isLoaded = false

    func onAdReceived(_ banner: AdropBanner) {
        print("Banner ad received successfully")
        isLoaded = true
    }

    func onAdFailedToReceive(_ banner: AdropBanner, _ errorCode: AdropErrorCode) {
        print("Banner ad failed to receive: \(errorCode)")
        isLoaded = false
    }

    func onAdImpression(_ banner: AdropBanner) {
        print("Banner ad impression")
    }

    func onAdClicked(_ banner: AdropBanner) {
        print("Banner ad clicked")
    }
}
```

### Set Context ID

```swift theme={null}
AdropBannerRepresented(
    unitId: "YOUR_UNIT_ID",
    contextId: "article_123"
)
.frame(width: 320, height: 100)
```

***

## Objective-C Implementation

How to implement banner ads in Objective-C environment.

### Basic Implementation

```objc theme={null}
@import AdropAds;

@interface ViewController () <AdropBannerDelegate>
@property (nonatomic, strong) AdropBanner *banner;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    // 1. Create banner instance
    self.banner = [[AdropBanner alloc] initWithUnitId:@"YOUR_UNIT_ID"];

    // 2. Set delegate
    self.banner.delegate = self;

    // 3. Add to view hierarchy
    [self.view addSubview:self.banner];

    // 4. Setup Auto Layout
    self.banner.translatesAutoresizingMaskIntoConstraints = NO;
    [NSLayoutConstraint activateConstraints:@[
        [self.banner.centerXAnchor constraintEqualToAnchor:self.view.centerXAnchor],
        [self.banner.bottomAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.bottomAnchor],
        [self.banner.widthAnchor constraintEqualToConstant:320],
        [self.banner.heightAnchor constraintEqualToConstant:100]
    ]];

    // 5. Load ad
    [self.banner load];
}

- (void)dealloc {
    [self.banner removeFromSuperview];
    self.banner = nil;
}

#pragma mark - AdropBannerDelegate

- (void)onAdReceived:(AdropBanner *)banner {
    NSLog(@"Banner ad received successfully");
}

- (void)onAdFailedToReceive:(AdropBanner *)banner :(AdropErrorCode)errorCode {
    NSLog(@"Banner ad failed to receive: %ld", (long)errorCode);
}

- (void)onAdImpression:(AdropBanner *)banner {
    NSLog(@"Banner ad impression");
}

- (void)onAdClicked:(AdropBanner *)banner {
    NSLog(@"Banner ad clicked");
}

@end
```

### Set Context ID

```objc theme={null}
// contextId is read-only — set via initializer
AdropBanner *banner = [[AdropBanner alloc] initWithUnitId:@"YOUR_UNIT_ID" contextId:@"article_123"];
```

***

## Delegate Methods

The `AdropBannerDelegate` protocol handles ad lifecycle events.

### onAdReceived (Required)

Called when ad is received successfully.

```swift theme={null}
func onAdReceived(_ banner: AdropBanner) {
    print("Banner ad received successfully")
    // Handle ad loading indicator, etc.
}
```

### onAdFailedToReceive (Required)

Called when ad fails to load.

```swift theme={null}
func onAdFailedToReceive(_ banner: AdropBanner, _ errorCode: AdropErrorCode) {
    print("Banner ad failed to receive: \(errorCode)")
    // Handle errors and display alternative content
}
```

<ParamField body="errorCode" type="AdropErrorCode">
  Error code indicating the type of error. See [Reference](/sdk/ios/reference#error-codes) for details.
</ParamField>

### onAdImpression (Optional)

Called when ad is displayed on screen.

```swift theme={null}
func onAdImpression(_ banner: AdropBanner) {
    print("Banner ad impression")
    // Handle impression analytics logging, etc.
}
```

### onAdClicked (Optional)

Called when user clicks on the ad.

```swift theme={null}
func onAdClicked(_ banner: AdropBanner) {
    print("Banner ad clicked")
    // Handle click analytics logging, etc.
}
```

### onAdVideoStart (Optional)

Called when a video ad starts playing.

```swift theme={null}
func onAdVideoStart(_ banner: AdropBanner) {
    print("Banner video started")
    // Handle video start analytics logging, etc.
}
```

### onAdVideoEnd (Optional)

Called when a video ad finishes playing.

```swift theme={null}
func onAdVideoEnd(_ banner: AdropBanner) {
    print("Banner video ended")
    // Handle video end analytics logging, etc.
}
```

***

## Closure Callbacks

As an alternative to delegates, you can use closure-based callbacks.

```swift theme={null}
let banner = AdropBanner(unitId: "YOUR_UNIT_ID")

banner.onAdReceived = { banner in
    print("Banner ad received")
}

banner.onAdFailedToReceive = { banner, errorCode in
    print("Banner ad failed: \(errorCode)")
}

banner.onAdImpression = { banner in
    print("Banner ad impression")
}

banner.onAdClicked = { banner in
    print("Banner ad clicked")
}

banner.onAdVideoStart = { banner in
    print("Banner video started")
}

banner.onAdVideoEnd = { banner in
    print("Banner video ended")
}
```

<Note>
  If both a delegate and closures are set, both will be called.
</Note>

***

## Ad Sizes

Banner ads require view size to match the size set in the unit.

### Common Banner Sizes

| Size       | Use Case     |
| ---------- | ------------ |
| 320 x 50   | Small banner |
| 320 x 100  | Large banner |
| 16:9 ratio | Video banner |

### UIKit

```swift theme={null}
bannerView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
    bannerView.widthAnchor.constraint(equalToConstant: 320),
    bannerView.heightAnchor.constraint(equalToConstant: 100)
])
```

### SwiftUI

```swift theme={null}
represented
    .frame(width: 320, height: 100)
    .onAppear {
        represented.banner.load()
    }
```

***

## Best Practices

### 1. Memory Management

Remove the banner when the view controller is deallocated.

```swift theme={null}
deinit {
    banner?.removeFromSuperview()
    banner = nil
}
```

### 2. Screen Visibility

Load ads when the banner is visible on screen.

```swift theme={null}
override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    banner?.load()
}
```

### 3. Reuse

Call `load()` again to reuse the same banner instance.

```swift theme={null}
func refreshAd() {
    banner?.load()
}
```

### 4. Error Handling

Implement appropriate error handling when ad loading fails.

```swift theme={null}
func onAdFailedToReceive(_ banner: AdropBanner, _ errorCode: AdropErrorCode) {
    switch errorCode {
    case .ERROR_CODE_NETWORK:
        print("Network error: Check connection")
    case .ERROR_CODE_AD_NO_FILL:
        print("No available ads")
    default:
        print("Ad load failed: \(errorCode)")
    }

    // Hide ad area or display fallback content
    bannerContainerView.isHidden = true

    // Or show your own promotional banner
    showPromotionBanner()
}
```

***

## Test Unit IDs

Use the following test unit IDs during development and testing.

| Ad Type             | Test Unit ID                            | Size       |
| ------------------- | --------------------------------------- | ---------- |
| Banner (320x50)     | `PUBLIC_TEST_UNIT_ID_320_50`            | 320 x 50   |
| Banner (320x100)    | `PUBLIC_TEST_UNIT_ID_320_100`           | 320 x 100  |
| Carousel Banner     | `PUBLIC_TEST_UNIT_ID_CAROUSEL`          | Variable   |
| Banner Video (16:9) | `PUBLIC_TEST_UNIT_ID_BANNER_VIDEO_16_9` | 16:9 ratio |
| Banner Video (9:16) | `PUBLIC_TEST_UNIT_ID_BANNER_VIDEO_9_16` | 9:16 ratio |

### Usage Example

<CodeGroup>
  ```swift Swift theme={null}
  let banner = AdropBanner(unitId: AdropUnitId.PUBLIC_TEST_UNIT_ID_320_100)
  ```

  ```objc Objective-C theme={null}
  AdropBanner *banner = [[AdropBanner alloc] initWithUnitId:PUBLIC_TEST_UNIT_ID_320_100];
  ```
</CodeGroup>

***

## Video Playback Control

For video banner ads, you can manually control video playback using `play()` and `pause()`.

### play()

Resumes video playback. Call this when the banner becomes visible again — for example, after a popup is dismissed or the app returns from the background.

<CodeGroup>
  ```swift Swift theme={null}
  banner?.play()
  ```

  ```objc Objective-C theme={null}
  [self.banner play];
  ```
</CodeGroup>

### pause()

Pauses video playback. Call this when the banner is hidden by a popup or the app enters the background.

<CodeGroup>
  ```swift Swift theme={null}
  banner?.pause()
  ```

  ```objc Objective-C theme={null}
  [self.banner pause];
  ```
</CodeGroup>

### Example: Background/Foreground Handling

```swift theme={null}
override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    banner?.play()
}

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    banner?.pause()
}
```

<Note>
  `play()` and `pause()` only affect video banner ads. They have no effect on image banners.
</Note>

***

## Custom Click Handling

You can take control of ad click behavior by using `useCustomClick`. When enabled, the SDK will not automatically open the destination URL on click, and you can handle it yourself.

### useCustomClick

```swift theme={null}
let banner = AdropBanner(unitId: "YOUR_UNIT_ID")
banner.useCustomClick = true
banner.delegate = self
banner.load()
```

When `useCustomClick` is `true`, the SDK will not open the destination URL automatically when the ad is clicked. The `onAdClicked` delegate callback will still fire, and you can use the `open()` method or handle navigation yourself.

### open()

Opens the destination URL of the ad. Can optionally pass a custom URL.

```swift theme={null}
// Open the ad's default destination URL
banner?.open()

// Open a custom URL
banner?.open("https://example.com")

// Open using in-app browser
banner?.open(nil, useInAppBrowser: true)
```

***

## Batch Loading (loads)

Use `AdropBanner.loads(...)` to request multiple banner ads in a single batched call. This is useful for building carousels, paginated feeds, or prefetching a pool of ads.

### Signature

<CodeGroup>
  ```swift Swift theme={null}
  AdropBanner.loads(
      unitId: String,
      contextId: String = "",
      delegate: AdropBannerDelegate
  )
  ```

  ```objc Objective-C theme={null}
  [AdropBanner loadsWithUnitId:@"YOUR_UNIT_ID"
                     contextId:@""
                      delegate:delegate];
  ```
</CodeGroup>

### Usage

<CodeGroup>
  ```swift Swift theme={null}
  class ViewController: UIViewController {
      private var banners: [AdropBanner] = []

      override func viewDidLoad() {
          super.viewDidLoad()
          AdropBanner.loads(unitId: "YOUR_UNIT_ID", delegate: self)
      }
  }

  extension ViewController: AdropBannerDelegate {
      // Required single-load callbacks — unused on the batch path.
      func onAdReceived(_ banner: AdropBanner) { /* batch path */ }
      func onAdFailedToReceive(_ banner: AdropBanner, _ errorCode: AdropErrorCode) {}

      // Batch success — deliver up to 5 ready-to-use banners.
      func onAdsReceived(_ banners: [AdropBanner]) {
          self.banners = banners
          banners.forEach { banner in
              // Attach to your view hierarchy (UIScrollView, UIPageViewController, ...)
              stackView.addArrangedSubview(banner)
          }
      }

      // Batch failure
      func onAdsFailedToReceive(_ errorCode: AdropErrorCode) {
          print("Batch load failed: \(errorCode)")
      }

      // Click / impression / video callbacks still fire per instance.
      func onAdClicked(_ banner: AdropBanner) {
          print("Banner clicked")
      }

      func onAdImpression(_ banner: AdropBanner) {
          print("Banner impression")
      }
  }
  ```

  ```objc Objective-C theme={null}
  @interface ViewController () <AdropBannerDelegate>
  @property (nonatomic, strong) NSArray<AdropBanner *> *banners;
  @end

  @implementation ViewController

  - (void)viewDidLoad {
      [super viewDidLoad];
      [AdropBanner loadsWithUnitId:@"YOUR_UNIT_ID" contextId:@"" delegate:self];
  }

  #pragma mark - AdropBannerDelegate

  - (void)onAdReceived:(AdropBanner *)banner { /* batch path */ }
  - (void)onAdFailedToReceive:(AdropBanner *)banner :(AdropErrorCode)errorCode {}

  - (void)onAdsReceived:(NSArray<AdropBanner *> *)banners {
      self.banners = banners;
      for (AdropBanner *banner in banners) {
          [self.stackView addArrangedSubview:banner];
      }
  }

  - (void)onAdsFailedToReceive:(AdropErrorCode)errorCode {
      NSLog(@"Batch load failed: %ld", (long)errorCode);
  }

  - (void)onAdClicked:(AdropBanner *)banner {
      NSLog(@"Banner clicked");
  }

  - (void)onAdImpression:(AdropBanner *)banner {
      NSLog(@"Banner impression");
  }

  @end
  ```
</CodeGroup>

### Delegate Methods

| Method                                                               | When it fires                                                                                  |
| -------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
| `onAdsReceived(_:)`                                                  | Batch delivered. Each `AdropBanner` already has the delegate attached and ad data applied.     |
| `onAdsFailedToReceive(_:)`                                           | Request rejected, network failure, or no fillable ads were returned (`ERROR_CODE_AD_NO_FILL`). |
| `onAdClicked` / `onAdImpression` / `onAdVideoStart` / `onAdVideoEnd` | Fire on each returned banner just like a single `load()`.                                      |

<Warning>
  The singular `onAdReceived(_:)` / `onAdFailedToReceive(_:_:)` callbacks are **not** invoked on the `loads` path. Use `onAdsReceived(_:)` / `onAdsFailedToReceive(_:)` as the sole batch signals.
</Warning>

### Constants

<ParamField body="AdropBanner.maxLoadsBatch" type="Int" default="5">
  Upper bound on ads returned from a single `loads(...)` call.
</ParamField>

### Constraints

* **Maximum 5 ads per call.** If the server returns more, only the first 5 are delivered.
* **Backfill is not applied.** If no direct ads fill, `onAdsFailedToReceive` is called with `ERROR_CODE_AD_NO_FILL` regardless of backfill configuration.
* `onAdsReceived` fires as soon as ad data is applied, matching the single `load()` contract — the creative paints shortly after you add the banner to a view hierarchy.
* Hold strong references to the returned banners (e.g., store them on the view controller). If they deallocate before being attached, they're silently dropped.

***

## Backfill Ads

When backfill ads are enabled, backfill ads are automatically loaded when direct ads are unavailable. Use the `isBackfilled` property to check if the ad is a backfill ad.

<CodeGroup>
  ```swift Swift theme={null}
  class ViewController: UIViewController, AdropBannerDelegate {
      private var banner: AdropBanner?

      func onAdReceived(_ banner: AdropBanner) {
          if banner.isBackfilled {
              print("Backfill ad loaded")
          } else {
              print("Direct ad loaded")
          }
      }

      func onAdFailedToReceive(_ banner: AdropBanner, _ errorCode: AdropErrorCode) {
          switch errorCode {
          case .ERROR_CODE_AD_NO_FILL:
              print("No direct ad, requesting backfill...")
          case .ERROR_CODE_AD_BACKFILL_NO_FILL:
              print("No backfill ad available")
              // Hide ad area
              banner.isHidden = true
          default:
              print("Ad load failed: \(errorCode)")
          }
      }
  }
  ```

  ```objc Objective-C theme={null}
  @interface ViewController () <AdropBannerDelegate>
  @property (nonatomic, strong) AdropBanner *banner;
  @end

  @implementation ViewController

  - (void)onAdReceived:(AdropBanner *)banner {
      if (banner.isBackfilled) {
          NSLog(@"Backfill ad loaded");
      } else {
          NSLog(@"Direct ad loaded");
      }
  }

  - (void)onAdFailedToReceive:(AdropBanner *)banner :(AdropErrorCode)errorCode {
      if (errorCode == AdropErrorCodeAdNoFill) {
          NSLog(@"No direct ad, requesting backfill...");
      } else if (errorCode == AdropErrorCodeBackfillNoFill) {
          NSLog(@"No backfill ad available");
          // Hide ad area
          banner.hidden = YES;
      } else {
          NSLog(@"Ad load failed: %ld", (long)errorCode);
      }
  }

  @end
  ```
</CodeGroup>

<Note>
  To use backfill ads, add the `AdropAds-Backfill` dependency. See [Getting Started](/sdk/ios/overview).
</Note>

***

## Next Steps

<CardGroup cols={2}>
  <Card title="Native Ads" href="/sdk/ios/native">
    Implementing customizable native ads
  </Card>

  <Card title="Interstitial Ads" href="/sdk/ios/interstitial">
    Implementing full-screen interstitial ads
  </Card>

  <Card title="Targeting Settings" href="/sdk/ios/targeting">
    Setting up user attributes and contextual targeting
  </Card>

  <Card title="Reference" href="/sdk/ios/reference">
    Classes, delegates, error codes reference
  </Card>
</CardGroup>
