Руководство по интеграции
Полное руководство по интеграции BidMachine Plus iOS SDK. Примеры кода приведены на Swift и Objective-C — используйте переключатель языка в каждом блоке.
BidMachine Plus iOS SDK
BidMachine Plus предоставляет два пути спроса через один API:
| Режим | Описание |
|---|---|
| AdNetwork | BidMachine работает как источник спроса с хедер-биддингом — самостоятельно или подключённым к сторонней медиации |
| Mediation | BidMachine выступает в роли самой платформы медиации, проводя аукцион по вашему спросу |
Одни и те же классы рекламных юнитов работают в обоих режимах, выбор делается при инициализации:
| Swift | Objective-C |
|---|---|
BannerAd | BMBannerAd |
InterstitialAd | BMInterstitialAd |
RewardedAd | BMRewardedAd |
Эта страница охватывает установку SDK, инициализацию и загрузку рекламы. Доступны два пути установки: автоматизированный через ИИ-агенты для кодинга или ручная настройка с CocoaPods или Swift Package Manager.
Автоматизированная интеграция с ИИ-агентами для кодинга
Бандл BidMachine Plus SDK Agents поставляет переносимый навык интеграции для Claude Code, Codex и Gemini. Установите его для вашей среды выполнения:
- Claude Code
- Gemini CLI
- npx (any agent)
/plugin marketplace add bidmachine/bidmachine-sdk-agents
/plugin install bidmachine-sdk-agents@bidmachine
gemini extensions install https://github.com/bidmachine/bidmachine-sdk-agents
npx skills add bidmachine/bidmachine-sdk-agents
Затем попросите вашего агента интегрировать BidMachine Plus — например:
Integrate BidMachine Plus into my iOS app with interstitial, rewarded, and banner ads.
Встроенный навык управляет интеграцией: добавляет зависимость SDK, инициализирует в режиме AdNetwork или Mediation, подключает рекламные юниты и устанавливает флаги приватности (GDPR, CCPA, ATT), сохраняя любую существующую настройку рекламы.
Ручная установка
Требуется iOS 13.0+ и Xcode 16.4+. Импортируйте модуль как import BidMachinePlus (Swift) или @import BidMachinePlus; (Objective-C).
CocoaPods
Subspec Static используется по умолчанию и автоматически добавляет линкер-флаг -ObjC.
platform :ios, '13.0'
use_frameworks!
target 'YourApp' do
pod 'BidMachinePlus' # default (Static)
# pod 'BidMachinePlus/Static'
# pod 'BidMachinePlus/Dynamic'
end
Swift Package Manager
- File → Add Package Dependencies
- URL:
https://github.com/bidmachine/BidMachinePlus-SPM - Выберите версию
0.0.1или диапазон и добавьте продуктBidMachinePlus - Добавьте
-ObjCв Build Settings → Other Linker Flags
.package(url: "https://github.com/bidmachine/BidMachinePlus-SPM", from: "0.0.1")
// product: .product(name: "BidMachinePlus", package: "BidMachinePlus-SPM")
Настройка Info.plist
Разрешите произвольные загрузки, чтобы рекламные креативы могли отображаться:
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
Вставьте массив SKAdNetworkItems — полный список ID сетей публикуется вместе с SDK.
Если вы собираете IDFA, добавьте описание использования ATT:
<key>NSUserTrackingUsageDescription</key>
<string>$(APP_NAME) needs your advertising identifier to deliver personalized ads.</string>
Инициализация
Вызовите BidMachine.instance(appKey:) один раз при запуске приложения, чтобы получить экземпляр SDK, затем вызовите initialize(config:completion:). Сохраните ссылку на экземпляр — вы будете передавать её в каждый конструктор рекламного юнита. Один и тот же app key всегда возвращает один и тот же экземпляр. Completion вызывается в главной очереди.
BidMachine Plus инициализируется в одном из двух режимов, выбираемом по integrationType, который вы передаёте в билдер.
Режим AdNetwork
BidMachine работает как рекламная сеть с хедер-биддингом. Когда он подключается к сторонней медиации, укажите эту платформу через сеттер медиатора, чтобы он мог конкурировать в вотерфолле.
- Swift
- Objective-C
let sdk = BidMachine.instance(appKey: "YOUR_APP_KEY")
let config = InitializationConfigBuilder(integrationType: .adNetwork)
.with(mediator: MediatorName.admob) // or MediatorName.levelPlay, MediatorName.max, a custom String
.with(loggingEnabled: true) // remove before release
.with(testModeEnabled: true) // remove before release
.build()
sdk.initialize(config: config) { status, error in
if let error { print("init failed: \(error.message)") }
}
@import BidMachinePlus;
BidMachine *sdk = [BidMachine instanceWithAppKey:@"YOUR_APP_KEY"];
BMInitializationConfigBuilder *builder =
[[BMInitializationConfigBuilder alloc] initWithIntegrationType:BMIntegrationTypeAdNetwork];
[builder withMediator:BMMediatorNameAdMob]; // or BMMediatorNameLevelPlay, BMMediatorNameMax, a custom NSString
[builder withLoggingEnabled:YES]; // remove before release
[builder withTestModeEnabled:YES]; // remove before release
BMInitializationConfig *config = [builder build];
[sdk initializeWithConfig:config completion:^(BMInitializationStatus * _Nullable status, BMError * _Nullable error) {
if (error) { NSLog(@"init failed: %@", error.message); }
}];
MediatorName.admob / BMMediatorNameAdMob ("admob"), .levelPlay / BMMediatorNameLevelPlay ("level_play") и .max / BMMediatorNameMax ("max") — это предопределённые строковые константы; сеттер медиатора также принимает любую произвольную строку.
Режим Mediation
BidMachine выступает в роли самой платформы медиации, проводя аукцион по вашему спросу. Передайте тип медиации — стороннего медиатора объявлять не нужно:
- Swift
- Objective-C
let config = InitializationConfigBuilder(integrationType: .mediation)
.with(loggingEnabled: true) // remove before release
.with(testModeEnabled: true) // remove before release
.build()
BidMachine.instance(appKey: "YOUR_APP_KEY").initialize(config: config) { status, error in }
BMInitializationConfigBuilder *builder =
[[BMInitializationConfigBuilder alloc] initWithIntegrationType:BMIntegrationTypeMediation];
[builder withLoggingEnabled:YES]; // remove before release
[builder withTestModeEnabled:YES]; // remove before release
BMInitializationConfig *config = [builder build];
BidMachine *sdk = [BidMachine instanceWithAppKey:@"YOUR_APP_KEY"];
[sdk initializeWithConfig:config completion:^(BMInitializationStatus * _Nullable status, BMError * _Nullable error) {}];
В обоих режимах классы рекламных юнитов предоставляют notifyWin / notifyLoss для сообщения результата раунда вотерфолла — см. Медиация.
Загружаете рекламу под серверную ставку? См. S2S биддинг для
load(bidPayload:).
Интеграция рекламы
Все коллбэки жизненного цикла рекламы передают AdInfo / BMAdInfo, описывающий победившую рекламу:
| Свойство | Тип (Swift / Obj-C) | Описание |
|---|---|---|
placementId | String / NSString * | ID плейсмента из дашборда BidMachine |
price | Double / double | eCPM ÷ 1000 (например, 0.005 = $5 CPM) |
precision | RevenuePrecision | Одно из .estimated, .exact, .unknown |
info | [String: String] / NSDictionary | Метаданные сети. Известные ключи: networkName, dsp, ecpm |
Читайте победившую сеть через adInfo.info["networkName"], а доход на показ — через adInfo.price.
adInfo.precision описывает, насколько надёжна эта цена:
| Swift | Obj-C | Значение |
|---|---|---|
.exact | RevenuePrecisionExact | Цена аукциона в реальном времени — доверяйте при отчётности |
.estimated | RevenuePrecisionEstimated | Оценка по историческим данным — считайте приблизительной |
.unknown | RevenuePrecisionUnknown | Достоверность не может быть определена |
Все коллбэки делегата срабатывают в главном потоке, поэтому вы можете обновлять UI прямо из них. В каждом делегате обязательны только didLoadAd и didFailToLoadAd — остальные необязательны.
Реклама Interstitial
Полноэкранная реклама. Создайте один раз, загрузите, затем показывайте, когда она готова. Перезагружайте из didDismiss, чтобы держать рекламу готовой к следующему показу.
- Swift
- Objective-C
class MainViewController: UIViewController, InterstitialDelegate {
private var interstitialAd: InterstitialAd!
private func createInterstitialAd() {
interstitialAd = InterstitialAd(sdk, placementId: "YOUR_PLACEMENT_ID")
interstitialAd.delegate = self
interstitialAd.load()
}
private func showInterstitialAd() {
if interstitialAd.isLoaded {
interstitialAd.show(from: self)
} else {
print("Interstitial ad not ready yet")
}
}
// InterstitialDelegate
func didLoadAd(adInfo: AdInfo) {
print("Interstitial loaded from \(adInfo.info["networkName"] ?? "?")")
}
func didFailToLoadAd(adInfo: AdInfo?, error: BidMachineError) {
print("Interstitial load failed: \(error.message)")
}
func didShowAd(adInfo: AdInfo) {}
func didFailToShowAd(adInfo: AdInfo?, error: BidMachineError) {
print("Interstitial show failed: \(error.message)")
}
func didClick(adInfo: AdInfo) {}
func didDismiss(adInfo: AdInfo) {
interstitialAd.load() // reload for next show
}
func didExpire(adInfo: AdInfo) {}
func didPayRevenue(adInfo: AdInfo) {
print("Interstitial revenue: \(adInfo.price) from \(adInfo.info["networkName"] ?? "?")")
}
}
@interface MainViewController () <InterstitialDelegate>
@property (nonatomic, strong) BMInterstitialAd *interstitialAd;
@end
@implementation MainViewController
- (void)createInterstitialAd {
self.interstitialAd = [[BMInterstitialAd alloc] init:self.sdk placementId:@"YOUR_PLACEMENT_ID"];
self.interstitialAd.delegate = self;
[self.interstitialAd load];
}
- (void)showInterstitialAd {
if (self.interstitialAd.isLoaded) {
[self.interstitialAd showFrom:self];
} else {
NSLog(@"Interstitial ad not ready yet");
}
}
#pragma mark - InterstitialDelegate
- (void)didLoadAdWithAdInfo:(BMAdInfo *)adInfo {
NSLog(@"Interstitial loaded from %@", adInfo.info[@"networkName"]);
}
- (void)didFailToLoadAdWithAdInfo:(BMAdInfo *)adInfo error:(BMError *)error {
NSLog(@"Interstitial load failed: %@", error.message);
}
- (void)didShowAdWithAdInfo:(BMAdInfo *)adInfo {}
- (void)didFailToShowAdWithAdInfo:(BMAdInfo *)adInfo error:(BMError *)error {
NSLog(@"Interstitial show failed: %@", error.message);
}
- (void)didClickWithAdInfo:(BMAdInfo *)adInfo {}
- (void)didDismissWithAdInfo:(BMAdInfo *)adInfo {
[self.interstitialAd load]; // reload for next show
}
- (void)didExpireWithAdInfo:(BMAdInfo *)adInfo {}
- (void)didPayRevenueWithAdInfo:(BMAdInfo *)adInfo {
NSLog(@"Interstitial revenue: %f from %@", adInfo.price, adInfo.info[@"networkName"]);
}
@end
| Коллбэк | Описание |
|---|---|
didLoadAd | Реклама загружена и готова к показу |
didFailToLoadAd | Реклама не загрузилась |
didShowAd | Реклама отображается на весь экран |
didFailToShowAd | Реклама не отобразилась |
didClick | Пользователь нажал на рекламу |
didDismiss | Пользователь закрыл рекламу |
didExpire | Срок действия рекламы истёк до показа |
didPayRevenue | Зарегистрирован оплачиваемый показ |
У InterstitialAd и RewardedAd нет метода destroy() — чтобы освободить полноэкранную рекламу, отпустите ссылку на неё. (BannerAd предоставляет destroy().)
Реклама Rewarded
Тот же жизненный цикл, что у interstitial, плюс коллбэк didReceiveReward, когда пользователь зарабатывает награду. RewardedDelegate наследуется от InterstitialDelegate.
- Swift
- Objective-C
class MainViewController: UIViewController, RewardedDelegate {
private var rewardedAd: RewardedAd!
private func createRewardedAd() {
rewardedAd = RewardedAd(sdk, placementId: "YOUR_PLACEMENT_ID")
rewardedAd.delegate = self
rewardedAd.load()
}
private func showRewardedAd() {
if rewardedAd.isLoaded {
rewardedAd.show(from: self)
}
}
// RewardedDelegate
func didLoadAd(adInfo: AdInfo) {}
func didReceiveReward(adInfo: AdInfo, reward: Reward?) {
print("Reward earned: \(reward?.amount ?? 0) \(reward?.label ?? "")")
// grant reward to the user
}
func didFailToLoadAd(adInfo: AdInfo?, error: BidMachineError) {
print("Rewarded load failed: \(error.message)")
}
func didDismiss(adInfo: AdInfo) {
rewardedAd.load() // reload for next show
}
func didPayRevenue(adInfo: AdInfo) {
print("Rewarded revenue: \(adInfo.price) from \(adInfo.info["networkName"] ?? "?")")
}
}
@interface MainViewController () <RewardedDelegate>
@property (nonatomic, strong) BMRewardedAd *rewardedAd;
@end
@implementation MainViewController
- (void)createRewardedAd {
self.rewardedAd = [[BMRewardedAd alloc] init:self.sdk placementId:@"YOUR_PLACEMENT_ID"];
self.rewardedAd.delegate = self;
[self.rewardedAd load];
}
- (void)showRewardedAd {
if (self.rewardedAd.isLoaded) {
[self.rewardedAd showFrom:self];
}
}
#pragma mark - RewardedDelegate
- (void)didLoadAdWithAdInfo:(BMAdInfo *)adInfo {}
- (void)didReceiveRewardWithAdInfo:(BMAdInfo *)adInfo reward:(BMReward *)reward {
NSLog(@"Reward earned: %ld %@", (long)reward.amount, reward.label);
// grant reward to the user
}
- (void)didFailToLoadAdWithAdInfo:(BMAdInfo *)adInfo error:(BMError *)error {
NSLog(@"Rewarded load failed: %@", error.message);
}
- (void)didDismissWithAdInfo:(BMAdInfo *)adInfo {
[self.rewardedAd load]; // reload for next show
}
- (void)didPayRevenueWithAdInfo:(BMAdInfo *)adInfo {
NSLog(@"Rewarded revenue: %f from %@", adInfo.price, adInfo.info[@"networkName"]);
}
@end
didReceiveReward добавляется к набору коллбэков interstitial. Объект Reward / BMReward предоставляет label: String и amount: Int.
Баннеры
BannerAd / BMBannerAd — это UIView. Добавьте его в свою разметку или вызовите show(at:), чтобы закрепить его у края экрана. Он одноразовый — он не обновляется автоматически.
| Размер (Swift) | Obj-C | Размеры | Сценарий |
|---|---|---|---|
.banner | BMBannerAdSize.banner | 320 × 50 | Стандартный баннер |
.leaderboard | BMBannerAdSize.leaderboard | 728 × 90 | Планшеты |
.mrec | BMBannerAdSize.mrec | 300 × 250 | Средний прямоугольник |
.adaptive(width:maxHeight:) | +adaptiveWithWidth:maxHeight: | произвольные | Адаптивная разметка |
Используйте BannerAdSize.getMaxAdaptiveHeight(width:) (+getMaxAdaptiveHeightWithWidth: в Obj-C), чтобы вычислить maxHeight для адаптивных баннеров. width и maxHeight имеют тип UInt32.
Ручной баннер
Зарезервируйте хост-UIView, затем прикрепите загруженн ый баннер к нему внутри didLoadAd:
- Swift
- Objective-C
class MainViewController: UIViewController, BannerDelegate {
@IBOutlet private var adContainer: UIView!
private var bannerAd: BannerAd!
private func createBannerAd() {
bannerAd = BannerAd(sdk, placementId: "YOUR_PLACEMENT_ID", size: .banner)
bannerAd.delegate = self
bannerAd.load()
}
deinit { bannerAd?.destroy() }
// BannerDelegate
func didLoadAd(adInfo: AdInfo) {
adContainer.addSubview(bannerAd)
}
func didFailToLoadAd(adInfo: AdInfo?, error: BidMachineError) {
print("Banner load failed: \(error.message)")
}
func didShowAd(adInfo: AdInfo) {}
func didFailToShowAd(adInfo: AdInfo?, error: BidMachineError) {}
func didClick(adInfo: AdInfo) {}
func didExpire(adInfo: AdInfo) {}
func didPayRevenue(adInfo: AdInfo) {
print("Banner revenue: \(adInfo.price) from \(adInfo.info["networkName"] ?? "?")")
}
}
@interface MainViewController () <BannerDelegate>
@property (nonatomic, strong) IBOutlet UIView *adContainer;
@property (nonatomic, strong) BMBannerAd *bannerAd;
@end
@implementation MainViewController
- (void)createBannerAd {
self.bannerAd = [[BMBannerAd alloc] init:self.sdk
placementId:@"YOUR_PLACEMENT_ID"
size:BMBannerAdSize.banner];
self.bannerAd.delegate = self;
[self.bannerAd load];
}
- (void)dealloc { [self.bannerAd destroy]; }
#pragma mark - BannerDelegate
- (void)didLoadAdWithAdInfo:(BMAdInfo *)adInfo {
[self.adContainer addSubview:self.bannerAd];
}
- (void)didFailToLoadAdWithAdInfo:(BMAdInfo *)adInfo error:(BMError *)error {
NSLog(@"Banner load failed: %@", error.message);
}
- (void)didShowAdWithAdInfo:(BMAdInfo *)adInfo {}
- (void)didFailToShowAdWithAdInfo:(BMAdInfo *)adInfo error:(BMError *)error {}
- (void)didClickWithAdInfo:(BMAdInfo *)adInfo {}
- (void)didExpireWithAdInfo:(BMAdInfo *)adInfo {}
- (void)didPayRevenueWithAdInfo:(BMAdInfo *)adInfo {
NSLog(@"Banner revenue: %f from %@", adInfo.price, adInfo.info[@"networkName"]);
}
@end
| Коллбэк | Описание |
|---|---|
didLoadAd | Реклама загружена и готова к добавлению в superview |
didFailToLoadAd | Реклама не загрузилась |
didShowAd | Реклама видна на экране, показ отслежен |
didFailToShowAd | Реклама не отобразилась |
didClick | Пользователь нажал на рекламу |
didExpire | Срок действия рекламы истёк до показа |
didPayRevenue | Зарегистрирован оплачиваемый показ |
Позиционированный баннер
Вызовите show(at:) на загруженном баннере, чтобы закрепить его у одного из краёв ниже. SDK определяет хост-вью внутренне (rootViewController ключевого окна, с фолбэком на верхний презентованный VC) — хост-вью в месте вызова не требуется. Вызовите hide(), чтобы убрать его.
| Позиция | Swift | Obj-C |
|---|---|---|
| Сверху | .horizontalTop | BMBannerPositionHorizontalTop |
| Снизу | .horizontalBottom | BMBannerPositionHorizontalBottom |
| Слева | .verticalLeft | BMBannerPositionVerticalLeft |
| Справа | .verticalRight | BMBannerPositionVerticalRight |
- Swift
- Objective-C
// In didLoadAd:
bannerAd.show(at: .horizontalBottom) // position defaults to .horizontalBottom
// Later:
bannerAd.hide() // detaches the view; show(at:) can re-display it
bannerAd.destroy()
// In didLoadAdWithAdInfo::
[self.bannerAd showAt:BMBannerPositionHorizontalBottom];
// Later:
[self.bannerAd hide]; // detaches the view; showAt: can re-display it
[self.bannerAd destroy];
.verticalLeft / .verticalRight поворачивают баннер на 90° в боковую полосу (только неадаптивные размеры — адаптивные баннеры откатываются к разметке horizontal-bottom). hide() только отсоединяет вью; он не вызывает destroy().
Дополнительные данные паблишера
Прикрепляйте произвольные пары key → value, передаваемые вместе с bid-запросом. API имеет одинаковую форму на двух уровнях:
| Область | Где вызывать | Применяется к |
|---|---|---|
| На весь SDK | sdk.addExtra | Каждому аукциону этого экземпляра |
| На рекламный юнит | <AdUnit>.addExtra | Только аукционам этого плейсмента |
Передача nil в качестве значения удаляет ключ. Во время загрузки extras рекламного юнита накладываются поверх значений на весь SDK — ключи рекламного юнита побеждают при коллизии.
На весь SDK
- Swift
- Objective-C
sdk.addExtra("whale", forKey: "user_segment")
let all: [String: String] = sdk.extras
sdk.addExtra(nil, forKey: "user_segment") // remove
[sdk addExtra:@"whale" forKey:@"user_segment"];
NSDictionary<NSString *, NSString *> *all = sdk.extras;
[sdk addExtra:nil forKey:@"user_segment"]; // remove