AI 技术实践者的知识库与工具箱Issue 03 · 2026
活跃2026年6月18日

讯飞广告 SDK · iOS(IFLYADLib)

科大讯飞 ADX 广告 iOS SDK 的对外接入文档 — 开屏 / Banner / 插屏 / 信息流 / 激励视频五类广告,含 CocoaPods、SPM、隐私合规、服务端竞价与完整错误码。

iosad-sdkobjective-ccocoapodsspm
一台 iPhone 显示主屏幕上排布的 App 图标 —— 移动应用即广告 SDK 的投放载体

讯飞广告 SDK(IFLYADLib)是科大讯飞消费者 BG「AI 营销 - ADX」面向 iOS 媒体的广告变现 SDK。一次接入即可投放开屏、Banner、插屏、自渲染信息流、激励视频五类广告,支持 CocoaPods 与 Swift Package Manager,内置隐私清单(PrivacyInfo.xcprivacy)、ATT 合规、服务端竞价(S2S)与 Header Bidding。

本文是面向接入方(媒体开发者)的对外接入文档:从环境要求、安装、隐私合规,到逐类广告的完整调用链与回调、错误码、最佳实践,按行业规范组织,照着做即可跑通。

三个仓库分别是什么

这个项目对外有三套产物,请根据需要选用——接入方通常只需要"公开库"或"定制库"中的一套

对外 · 推荐

公开库 IFLYADLib

标准发行版,五类广告齐全,按广告形式可组合接入。

  • — 类名 IFLYxxxAd
  • — CocoaPods / SPM
  • — 当前 6.0.7

白标定制

定制库 YSIFLYADLib

YS 媒体白标版,命名加前缀、资源内嵌、动态 framework。

  • — 类名 YSIFLYxxxAd
  • — 方法前缀 ysifly_
  • — 无激励视频

内部源码

私有库 IFLYADLibDemo

完整源码 + 示例工程,仅内部使用,不对外分发。

  • — 五类广告示例
  • — 单元测试
  • — 构建脚本

选择建议:常规媒体接入用公开库 IFLYADLib;需要避免与其他广告库符号冲突、或要求资源自包含/动态库交付的,用定制库 YSIFLYADLib。两者 API 形态一致,仅命名与交付方式不同(详见 公开库与定制库差异)。

能力一览

IFLYSplashAd

开屏 Splash

冷/热启动全屏曝光,倒计时、跳过、摇一摇,图片与视频素材。

IFLYBannerAd

Banner

嵌入容器的横幅,宽度自适应,可带关闭按钮。

IFLYInterstitialAd

插屏 Interstitial

半屏 / 全屏弹窗,图片与视频,场景切换时插入。

IFLYNativeFeedAd

信息流 Native Feed

自渲染,媒体侧用原生控件排版,单图 / 三图 / 视频。

IFLYRewardVideoAd

激励视频 Reward

观看完整视频后发放奖励,含发奖回执回调。

S2S / HB

竞价能力

服务端竞价 Token、客户端 Header Bidding 胜负通知。

环境要求

项目要求
iOS 部署版本iOS 13.0 及以上
Xcode14.1 及以上
语言Objective-C 接口(Swift 工程可直接桥接调用)
架构真机 arm64;6.0.1 起(模型 A)含模拟器切片(arm64 / x86_64),可直接在模拟器调试
链接标记公开库为静态 framework,App Target 必须配置 -ObjC;定制库为动态 framework,无需配置

安装 SDK

方式一:CocoaPods(推荐)

公开库 IFLYADLib 支持"按广告形式可组合接入":Core 必选,其余格式按需选用,VideoUI(视频播放容器)由依赖关系自动带入。只接入需要的格式可显著减小包体

pod 'IFLYADLib', '6.0.7'

Podfile — 一行接入全部五类广告

完整 Podfile 示例:

source 'https://cdn.cocoapods.org/'
platform :ios, '13.0'

target 'YourApp' do
  use_frameworks!

  # 全量:五类广告 + VideoUI + 内置资源
  pod 'IFLYADLib', '6.0.7'

  # 或按需组合(Core 隐式必选;Splash / Interstitial / Reward 会自动带入 VideoUI)
  # pod 'IFLYADLib/Splash', '6.0.7'
  # pod 'IFLYADLib/Banner', '6.0.7'
  # pod 'IFLYADLib/NativeFeed', '6.0.7'
end
pod install --repo-update

可选 subspec:Core(必选)、BannerSplashInterstitialNativeFeedRewardFull(默认,等于全选)。

方式二:Swift Package Manager

在 Xcode File › Add Package Dependencies… 中输入仓库地址,选择版本 6.0.7,按需勾选 product:

https://github.com/LJMcarryu/IFLYADLib_iOS

可选 product 与 subspec 同名:Core(必选)、BannerSplashInterstitialNativeFeedRewardFull

仅公开库

必配 -ObjC

在 App Target › Build Settings › Other Linker Flags 加 -ObjC,否则静态库的 category / +load 被裁剪,运行期异常。

注意

SPM 不带资源

binaryTarget 无法随包分发 resource_bundles。用到内置素材的格式(开屏/插屏/激励 UI)经 SPM 接入需自行从 Release 合并包提取 .bundle,或改用 CocoaPods。

PrivacyInfo

隐私清单

CocoaPods 随 Core 资源自动带入 PrivacyInfo.xcprivacy;SPM 需手动添加到 App Target。

导入头文件

统一使用伞头即可(6.0.3 起伞头用 __has_include 守卫,全量与按格式部分安装都能正常编译,自动只导入已安装格式的入口类):

#import <IFLYADLib/IFLYADLib.h>

隐私与合规

App Tracking Transparency(ATT)

iOS 14+ 读取 IDFA 前需请求用户授权。建议在 applicationDidBecomeActive: 或首个前台时机发起:

#import <AppTrackingTransparency/AppTrackingTransparency.h>

- (void)applicationDidBecomeActive:(UIApplication *)application {
    if (@available(iOS 14, *)) {
        if (ATTrackingManager.trackingAuthorizationStatus == ATTrackingManagerAuthorizationStatusNotDetermined) {
            [ATTrackingManager requestTrackingAuthorizationWithCompletionHandler:^(ATTrackingManagerAuthorizationStatus status) {
                NSLog(@"ATT status: %ld", (long)status);
            }];
        }
    }
}

Info.plist 需补充用途说明:

<key>NSUserTrackingUsageDescription</key>
<string>用于向你展示更相关的广告内容</string>

未授权时 IDFA 读取应返回空,由 SDK 走非个性化链路。媒体侧可把 IDFA 通过 IFLYAdRequestConfig.idfa 传入(见下一节)。

个性化与日志开关

// 媒体侧个性化推荐开关(记录媒体侧状态)
[IFLYAdConfig setPersonalizedEnabled:YES];

// SDK 日志:Debug 默认开启,正式上线前建议关闭
[IFLYAdConfig setLogEnabled:NO];

隐私清单要点

SDK 已内置 PrivacyInfo.xcprivacy:声明 NSPrivacyTracking = YES,追踪域名包含 voiceads.cn 等;采集数据类型涵盖 DeviceID、ProductInteraction、AdvertisingData(用户关联、用于追踪)与 OtherDiagnosticData;并声明了 UserDefaults(CA92.1)、文件时间戳(C617.1)、系统启动时间(35F9.1)、磁盘可用空间(E174.1)等 Required Reason API。提审前请确认隐私清单已随包合入。

初始化与请求配置

全局初始化

AppDelegate 启动时完成全局配置(无需 appId/secret,广告位粒度由 adUnitId 区分):

#import <IFLYADLib/IFLYADLib.h>

- (BOOL)application:(UIApplication *)application
    didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    [IFLYAdConfig setPersonalizedEnabled:YES];
    [IFLYAdConfig setLogEnabled:NO];   // 正式环境关闭
    return YES;
}

统一请求配置 IFLYAdRequestConfig

每次加载广告可传入一个 IFLYAdRequestConfig,用于竞价底价、超时、设备标识等。常用字段:

字段类型说明
requestIdNSString *请求 ID,不设则 SDK 自动生成
settleTypeNSNumber *交易方式:0 固定价,1 RTB
bidFloorNSNumber *竞价底价,单位 元 / 千次展示(CPM)
interactStatusNSNumber *互动开关:1 开启,2 关闭
appName / appVersionNSString *App 名称 / 版本号
requestTimeoutNSNumber *请求超时(秒)
idfaNSString *媒体侧传入的 IDFA(ATT 授权后)
caidListNSArray<NSDictionary *> *媒体侧传入的 CAID 列表
userAgentNSString *自定义 UA
deepLinkDisabledNSNumber *是否禁用 DeepLink 外跳

封装一个工具方法复用:

+ (IFLYAdRequestConfig *)sharedRequestConfig {
    IFLYAdRequestConfig *config = [[IFLYAdRequestConfig alloc] init];
    config.settleType = @1;          // RTB
    config.bidFloor = @0.01;         // 底价 0.01 元/千次
    config.interactStatus = @1;      // 开启互动
    config.requestTimeout = @5;      // 5 秒超时
    config.appName = NSBundle.mainBundle.infoDictionary[@"CFBundleDisplayName"];
    config.appVersion = NSBundle.mainBundle.infoDictionary[@"CFBundleShortVersionString"];
    config.idfa = [self currentIDFAStringOrNil];   // ATT 未授权返回 nil
    return config;
}

通用接入范式

五类广告共享同一套生命周期:创建实例 → 设置 delegate → 发起加载 → 收到就绪回调后展示 → 用完销毁。理解这条主线,任意一种广告都能照搬。

01

创建实例并设置 delegate

用广告位 ID 初始化,delegate 必须在 loadAd 之前设置;并设置承载落地页跳转的 currentViewController。

IFLYBannerAd *ad = [[IFLYBannerAd alloc] initWithAdUnitId:adUnitId]; ad.delegate = self; ad.currentViewController = self;
02

发起加载

传入统一请求配置;SDK 会回调 didLoad(响应解析成功)、didReady(主素材就绪)。

[ad loadAdWithRequestConfig:[MyAds sharedRequestConfig]];
03

就绪后展示

在 didReady 回调中、确认 isAdValid 后再展示。不同广告展示入口不同(showInView: / showAdFromRootViewController:config: / bindAdWithViewBinder:error:)。

if ([ad isAdValid]) { [ad showInView:self.container]; }
04

监听交互与失败

通过 delegate 感知曝光 didExpose、点击 didClick、关闭 didClose 与统一失败出口 didFailWithError:。

05

销毁

用完先把 delegate 置 nil 再 destroy;广告对象为一次性消费,不能复用。

ad.delegate = nil; [ad destroy];

生命周期状态 IFLYAdStateIdle → Loading → Loaded → Ready → Showing → Exposed → Closed → Destroyed,异常进入 Failed。其中信息流(NativeFeed)没有 Ready 阶段,Loaded 之后直接进入绑定(bind),其余四类才经过 Ready(即 didReady)。

通用约束(适用于全部广告类型):

  • 强持有广告对象:用属性持有,loadAd 后不要让对象被提前释放。
  • delegate 为弱引用:在 dealloc / 退出页面前把 delegate = nil,再 destroy,避免野指针回调。
  • didReady 后展示didLoad 仅表示响应解析成功,素材未必下载完;didReady(或对应"就绪"回调)才可展示。
  • 一次性消费:广告已展示 / 已关闭 / 已销毁后不可再次 loadAd,需新建实例。
  • 替换真实广告位:示例中的 adUnitId 仅供演示,上线前替换为平台分配的正式广告位。

下面逐类给出完整调用链与关键回调。

开屏广告 Splash

开屏不走 present,而是直接加到 window 上展示,带倒计时、跳过与可选的"摇一摇/扭一扭"互动。

@interface SplashVC () <IFLYSplashAdDelegate>
@property (nonatomic, strong) IFLYSplashAd *splashAd;
@end

@implementation SplashVC

- (void)loadAd {
    IFLYSplashAd *ad = [[IFLYSplashAd alloc] initWithAdUnitId:adUnitId];
    ad.delegate = self;
    ad.currentViewController = self;
    self.splashAd = ad;
    [ad loadAdWithRequestConfig:[MyAds sharedRequestConfig]];
}

// 主素材就绪,可以展示
- (void)splashAdDidReady:(IFLYSplashAd *)ad {
    if (![ad isAdValid]) return;

    IFLYSplashAdConfig *config = [[IFLYSplashAdConfig alloc] init];
    config.traceDuration = 5;                 // 倒计时秒数(3–5,默认 5)
    config.mediumBottomView = [self logoView]; // 底部自定义 Logo 区
    config.muteOnStart = YES;                  // 视频起播静音
    config.showNoAds = NO;                     // 是否显示"免除广告"按钮
    [ad showAdFromRootViewController:self config:config];
}

- (void)splashAdDidExpose:(IFLYSplashAd *)ad { /* 曝光计费成立 */ }
- (void)splashAdDidClick:(IFLYSplashAd *)ad { /* 点击 */ }
- (void)splashAdDidSkip:(IFLYSplashAd *)ad  { /* 用户跳过 */ }
- (void)splashAdDidClose:(IFLYSplashAd *)ad { [self enterApp]; }   // 关闭后进主界面
- (void)splashAd:(IFLYSplashAd *)ad didFailWithError:(IFLYAdError *)error {
    NSLog(@"开屏失败 %ld %@", (long)error.errorCode, error.errorDescription);
    [self enterApp];
}

- (void)dealloc {
    self.splashAd.delegate = nil;
    [self.splashAd destroy];
}
@end

IFLYSplashAdConfig 关键属性traceDuration(倒计时 3–5s)、mediumBottomView(底部自定义视图)、customWindow(自定义承载 window)、headingInteractionEnabled(摇一摇/扭一扭)、showNoAds("免除广告"按钮)、muteOnStart / muteButtonHidden

主要回调(IFLYSplashAdDelegate

回调时机
splashAdDidLoad:响应解析成功(可读 adData 做竞价决策)
splashAdDidReady:主素材就绪,可展示
splashAdDidShow: / splashAdDidExpose:已展示 / 有效曝光
splashAdDidClick:用户点击
splashAdDidSkip: / splashAdDidTapNoAds:跳过 / 点击"免除广告"
splashAdDidClose:倒计时结束或关闭(务必在此进入主界面)
视频事件splashAdDidStartPlay: / splashAdDidPausePlay: / splashAdDidResumePlay: / splashAdDidPlayFinish: / splashAd:didFailToPlayWithError:
splashAd:didFailWithError:加载或展示失败(统一出口,也应进入主界面)

Banner 展示到媒体提供的容器中,容器宽度需 > 0,高度为 0 时按素材自适应。

@interface BannerVC () <IFLYBannerAdDelegate>
@property (nonatomic, strong) IFLYBannerAd *bannerAd;
@end

@implementation BannerVC

- (void)loadAd {
    IFLYBannerAd *ad = [[IFLYBannerAd alloc] initWithAdUnitId:adUnitId];
    ad.delegate = self;
    ad.currentViewController = self;
    ad.closeButtonVisible = YES;     // 右上角关闭按钮
    self.bannerAd = ad;
    [ad loadAdWithRequestConfig:[MyAds sharedRequestConfig]];
}

- (void)bannerAdDidReady:(IFLYBannerAd *)ad {
    if ([ad isAdValid]) {
        [ad showInView:self.bannerContainer];   // 展示到容器
    }
}

- (void)bannerAdDidClose:(IFLYBannerAd *)ad { [self.bannerContainer removeFromSuperview]; }
- (void)bannerAd:(IFLYBannerAd *)ad didFailWithError:(IFLYAdError *)error {
    NSLog(@"Banner 失败 %ld %@", (long)error.errorCode, error.errorDescription);
}

- (void)dealloc {
    self.bannerAd.delegate = nil;
    [self.bannerAd destroy];
}
@end

主要回调(IFLYBannerAdDelegatebannerAdDidLoad:bannerAdDidReady:(可 showInView:)、bannerAdDidExpose:bannerAdDidClick:bannerAdDidClose:(用户关闭)、bannerAd:didFailWithError:

插屏广告 Interstitial

插屏以 present 方式弹出,支持半屏 / 全屏两种样式,图片与视频素材。

@interface InterstitialVC () <IFLYInterstitialAdDelegate>
@property (nonatomic, strong) IFLYInterstitialAd *interstitialAd;
@end

@implementation InterstitialVC

- (void)loadAd {
    IFLYInterstitialAd *ad = [[IFLYInterstitialAd alloc] initWithAdUnitId:adUnitId];
    ad.delegate = self;
    ad.currentViewController = self;
    self.interstitialAd = ad;
    [ad loadAdWithRequestConfig:[MyAds sharedRequestConfig]];
}

- (void)interstitialAdDidReady:(IFLYInterstitialAd *)ad {
    if (![ad isAdValid]) return;

    IFLYInterstitialAdConfig *config = [[IFLYInterstitialAdConfig alloc] init];
    config.presentationStyle = IFLYInterstitialPresentationStyleHalfScreen; // 或 FullScreen
    config.muteOnStart = YES;
    [ad showAdFromRootViewController:self config:config];
}

- (void)interstitialAdDidClose:(IFLYInterstitialAd *)ad { /* 关闭 */ }
- (void)interstitialAd:(IFLYInterstitialAd *)ad didFailWithError:(IFLYAdError *)error {
    NSLog(@"插屏失败 %ld %@", (long)error.errorCode, error.errorDescription);
}

- (void)dealloc {
    self.interstitialAd.delegate = nil;
    [self.interstitialAd destroy];
}
@end

展示样式枚举

typedef NS_ENUM(NSInteger, IFLYInterstitialPresentationStyle) {
    IFLYInterstitialPresentationStyleHalfScreen = 0,  // 半屏(默认)
    IFLYInterstitialPresentationStyleFullScreen = 1,  // 全屏
};

主要回调(IFLYInterstitialAdDelegateinterstitialAdDidLoad:interstitialAdDidReady:interstitialAdDidShow:interstitialAdDidRender: / didFailToRenderWithError:interstitialAdDidExpose:interstitialAdDidClick:、视频事件(interstitialAdDidStartPlay: / interstitialAdDidPlayFinish: / interstitialAd:didFailToPlayWithError: 等)、interstitialAdDidClose:interstitialAd:didFailWithError:

信息流广告 Native Feed

信息流是自渲染:SDK 只回传素材数据(adData),由媒体侧用自己的原生控件排版,再通过 IFLYNativeFeedAdViewBinder 把容器、点击区、关闭按钮、视频承载视图交还给 SDK 完成曝光与点击监测。SDK 不会向你的容器添加任何 UI。

@interface NativeVC () <IFLYNativeFeedAdDelegate>
@property (nonatomic, strong) IFLYNativeFeedAd *nativeAd;
@end

@implementation NativeVC

- (void)loadAd {
    IFLYNativeFeedAd *ad = [[IFLYNativeFeedAd alloc] initWithAdUnitId:adUnitId];
    ad.delegate = self;
    ad.currentViewController = self;
    ad.muteOnStart = YES;
    self.nativeAd = ad;
    [ad loadAdWithRequestConfig:[MyAds sharedRequestConfig]];
}

// 1) 请求成功:读取素材数据,用媒体侧控件自行渲染
- (void)nativeFeedAdDidLoad:(IFLYNativeFeedAd *)ad {
    IFLYNativeFeedAdData *data = ad.adData;
    self.titleLabel.text = data.title;
    self.descLabel.text  = data.desc ?: data.content;
    [self.ctaButton setTitle:(data.actionText ?: @"查看详情") forState:UIControlStateNormal];
    // 按 data.materialType 渲染单图 / 三图 / 视频;图片地址见 data.imageURLs,视频见 data.videoURL

    // 2) 渲染完成后绑定给 SDK
    IFLYNativeFeedAdViewBinder *binder = [[IFLYNativeFeedAdViewBinder alloc] init];
    binder.containerView = self.adContainer;                                  // 必填
    binder.renderViews   = @[self.mediaContainer, self.titleLabel, self.descLabel, self.ctaButton];
    binder.clickViews    = @[self.mediaContainer, self.ctaButton];           // 可点击区
    binder.closeView     = self.closeButton;
    binder.videoView     = ad.hasVideoTemplate ? self.mediaContainer : nil;  // 视频素材必填

    IFLYAdError *error = nil;
    if (![ad bindAdWithViewBinder:binder error:&error]) {
        NSLog(@"信息流绑定失败 %ld %@", (long)error.errorCode, error.errorDescription);
    }
}

- (void)nativeFeedAdDidRender:(IFLYNativeFeedAd *)ad {
    if (ad.hasVideoTemplate) { [ad startPlay]; }
}

- (void)nativeFeedAdDidClose:(IFLYNativeFeedAd *)ad { /* 用户关闭 */ }

// 列表 cell 复用前务必解绑
- (void)prepareForReuse {
    [self.nativeAd unbindAd];
}

- (void)dealloc {
    self.nativeAd.delegate = nil;
    [self.nativeAd destroy];
}
@end

素材类型枚举

typedef NS_ENUM(NSInteger, IFLYNativeFeedAdMaterialType) {
    IFLYNativeFeedAdMaterialTypeUnknown     = 0,
    IFLYNativeFeedAdMaterialTypeSingleImage = 1,  // 单图
    IFLYNativeFeedAdMaterialTypeThreeImages = 2,  // 三图
    IFLYNativeFeedAdMaterialTypeVideo       = 3,  // 视频
};

IFLYNativeFeedAdViewBinder 关键字段containerView(必填)、renderViewsclickViewscloseViewvideoView(视频素材必填),以及语义视图 titleView / descView / iconView / imageView / adSourceView / ctaView

IFLYNativeFeedAdData 常用字段materialTypetitledesccontentactionTexticonURLimageURLsvideoURLvideoCoverURLvideoDurationecpm,并提供 isMaterialComplete 校验素材是否满足渲染要求。

主要回调(IFLYNativeFeedAdDelegatenativeFeedAdDidLoad:(自渲染)、nativeFeedAdDidRender:(绑定成功)/ didFailToRenderWithError:nativeFeedAdDidExpose:nativeFeedAdDidClick:、视频事件、nativeFeedAdDidClose:nativeFeedAd:didFailWithError:

复用要点:列表场景在 cell 复用前必须调用 unbindAd 清理绑定状态;绑定为一次性消费,unbindAd 不会把对象复位为可重新绑定。

激励视频广告 Reward

用户观看完整视频后发放奖励。发奖以 didRewardEffective: 回调为准didClose: 仅表示广告关闭,不等同于发奖。

@interface RewardVC () <IFLYRewardVideoAdDelegate>
@property (nonatomic, strong) IFLYRewardVideoAd *rewardAd;
@end

@implementation RewardVC

- (void)loadAd {
    IFLYRewardVideoAd *ad = [[IFLYRewardVideoAd alloc] initWithAdUnitId:adUnitId];
    ad.delegate = self;
    ad.currentViewController = self;
    self.rewardAd = ad;
    [ad loadAdWithRequestConfig:[MyAds sharedRequestConfig]];
}

- (void)rewardVideoAdDidReady:(IFLYRewardVideoAd *)ad {
    if (![ad isAdValid]) return;
    IFLYRewardVideoAdConfig *config = [[IFLYRewardVideoAdConfig alloc] init];
    config.muteOnStart = YES;
    [ad showAdFromRootViewController:self config:config];
}

// 激励发放生效 —— 在这里给用户发奖
- (void)rewardVideoAd:(IFLYRewardVideoAd *)ad didRewardEffective:(NSDictionary *)info {
    [self grantRewardToUser];
}

- (void)rewardVideoAdDidClose:(IFLYRewardVideoAd *)ad { /* 关闭,不在此发奖 */ }
- (void)rewardVideoAd:(IFLYRewardVideoAd *)ad didFailWithError:(IFLYAdError *)error {
    NSLog(@"激励失败 %ld %@", (long)error.errorCode, error.errorDescription);
}

- (void)dealloc {
    self.rewardAd.delegate = nil;
    [self.rewardAd destroy];
}
@end

主要回调(IFLYRewardVideoAdDelegaterewardVideoAdDidLoad:rewardVideoAdDidReady:rewardVideoAdDidShow:rewardVideoAdDidExpose:rewardVideoAdDidClick:、视频事件、rewardVideoAd:didRewardEffective:(发奖准绳)rewardVideoAdDidClose:rewardVideoAd:didFailWithError:

服务端竞价与 Header Bidding

客户端获取 SDK 竞价 Token(S2S)

NSError *error = nil;
NSString *sdkToken = [IFLYAdSDK getSdkTokenWithAdUnitId:@"YOUR_AD_UNIT_ID" error:&error];
if (!sdkToken) {
    NSLog(@"获取 SDK Token 失败: %@", error);
}
// 将 sdkToken 上送你的服务端,向 ADX 发起服务端竞价,拿到 rspToken 后回传客户端

用服务端竞价结果加载(所有广告类型通用)

[splashAd       loadAdWithServerBiddingToken:rspToken];
[bannerAd       loadAdWithServerBiddingToken:rspToken];
[interstitialAd loadAdWithServerBiddingToken:rspToken];
[nativeAd       loadAdWithServerBiddingToken:rspToken];
[rewardAd       loadAdWithServerBiddingToken:rspToken];

Header Bidding 胜负通知

if (ad.bidInfo.winNoticeAvailable) {
    [ad sendBidResultWithType:IFLYAdBidResultTypeWin reason:@"win"];
} else {
    [ad sendBidResultWithType:IFLYAdBidResultTypeLoseBidLower reason:@"loss"];
}

竞价结果类型 IFLYAdBidResultTypeWin = 100LoseBidLower = 101LoseCreativePending = 102LoseCreativeRejected = 103LosePriorityLower = 104Error = 105Timeout = 106

错误码

所有失败统一通过各广告的 ...didFailWithError: 回调返回 IFLYAdError,读取 error.errorCodeerror.errorDescription。错误码按段划分:

服务端(70xxx)

含义
70200请求成功
70204无填充(No Fill)
70400无效广告位
70401 / 70404S2S Token 为空 / 无效或过期
70406S2S Load 响应解码失败
70403超过日请求次数上限
70500服务端内部错误

通用客户端(71xxx)

含义
71001未知错误
71002无效请求
71003网络连接错误
71004权限未授予
71005广告位 ID 为空
71006请求超时
71007 / 71008ViewController 为空 / 父视图为空

各广告专属段:激励视频 712xx、Banner 713xx、插屏 714xx、信息流 715xx、开屏 716xx(例如 71203 激励未就绪、71308 Banner 未就绪、71406 插屏未就绪、71505 信息流未就绪、71603 开屏未就绪;以及各类"已展示/已过期/已销毁/素材无效"等细分码)。

公开库与定制库差异

定制库 YSIFLYADLib 与公开库 API 形态一致,但做了白标改造,主要差异如下:

维度公开库 IFLYADLib定制库 YSIFLYADLib
当前版本6.0.76.0.7
类名IFLYSplashAdYSIFLYSplashAd …(加 YS 前缀)
公开方法loadAdWithRequestConfig: / showInView: / destroyysifly_loadAdWithRequestConfig: / ysifly_showInView: / ysifly_destroy(加 ysifly_ 前缀)
delegate 方法splashAdDidReady:ysifly_splashAdDidReady: …(加 ysifly_ 前缀)
错误类型IFLYAdErrorYSIFLYAdError
伞头#import <IFLYADLib/IFLYADLib.h>#import <YSIFLYADLib/YSIFLYADLib.h>
广告类型开屏 / Banner / 插屏 / 信息流 / 激励视频开屏 / Banner / 插屏 / 信息流(无激励视频
交付形态静态 framework,-ObjC动态 framework,无需 -ObjC
资源随 Core 资源带入资源(YSAdvSDK.bundle)内嵌于 framework

定制库安装(CocoaPods 经 raw podspec 直连):

source 'https://cdn.cocoapods.org/'
platform :ios, '13.0'

target 'YourApp' do
  use_frameworks!
  pod 'YSIFLYADLib', :podspec => 'https://raw.githubusercontent.com/LJMcarryu/YSIFLYADLib_iOS/6.0.7/YSIFLYADLib.podspec'
end

SPM:https://github.com/LJMcarryu/YSIFLYADLib_iOS.git,版本 6.0.7,product YSIFLYADLib

调用对照(以开屏为例):

// 公开库(方法名无前缀,与前文主线一致:统一传 IFLYAdRequestConfig)
IFLYSplashAd *ad = [[IFLYSplashAd alloc] initWithAdUnitId:@"xxx"];
[ad loadAdWithRequestConfig:[MyAds sharedRequestConfig]];
// …didReady 后:[ad showAdFromRootViewController:self config:config];

// 定制库(类名加 YS 前缀、方法加 ysifly_ 前缀,调用形态与公开库一致)
YSIFLYSplashAd *ad = [[YSIFLYSplashAd alloc] initWithAdUnitId:@"xxx"];
[ad ysifly_loadAdWithRequestConfig:[MyAds sharedRequestConfig]];
// …didReady 后:[ad ysifly_showAdFromRootViewController:self config:config];

最佳实践

  • 提前加载、就绪展示:开屏/激励等强曝光场景建议提前发起 loadAd,在 didReady 后再展示,缩短用户等待。
  • 每次展示新建实例:广告对象一次性消费,展示/关闭后请重新 initWithAdUnitId: 创建。
  • 正确释放:退出页面或 dealloc 时先 delegate = nildestroy;信息流 cell 复用前 unbindAd
  • 失败要兜底:开屏/插屏在 didFailWithError:didClose: 都要保证业务流程继续(如进入主界面)。
  • 激励发奖只认回执:仅在 didRewardEffective: 发奖,避免重复或漏发。
  • 上线前收尾:关闭 setLogEnabled:、替换正式广告位、确认隐私清单与 ATT 文案已就绪。

常见问题

版本与支持

版本要点
6.0.7(推荐)服务端竞价 S2S 正式环境域名切换为 sdk-adx.voiceads.cn(内部默认值,接入方不可见/不配置,灰度地址不变);隐私清单 NSPrivacyTrackingDomains 新增该域;公开 API 与各格式行为不变,二进制因地址改动重建
6.0.6SDK 内部日志精简(约 322→181 条)+ 日志输出彻底去 IFLY 字眼;公开 API 与各格式行为不变
6.0.5资源加载器跨域兜底修复(按格式分包域内缺图)+ SDK 版本号常量对齐发版号
6.0.4运行期日志前缀去品牌 [IFLYAd][AdSDK](合规去名);公开 API 与 Full 行为不变
6.0.3伞头 __has_include 守卫,部分安装也可直接用伞头;二进制与 6.0.2 一致
6.0.2模型 A 补齐 PrivacyInfo.xcprivacy,CocoaPods 随 Core 资源带入
6.0.1首次推出"按广告形式可组合接入",各模块独立 xcframework
6.0.0API 大版本:公开五类广告入口与统一请求配置 IFLYAdRequestConfig

从 6.0.0 升级到 6.0.7:业务 API 无变化,可改为按广告形式组合接入;CocoaPods 直接升级版本号即可,无需改业务代码。

Turing.

AI 技术实践者的知识库与工具箱

Colophon

A magazine of practical notes from inside the AI work — written, edited & published by Jimmy Liu.

Elsewhere

© 2026 Jimmy Liu · 版权所有

Bodoni Moda · Albert Sans · Imagery from Unsplash