EmAdsSDK 1.0.7

EmAdsSDK 1.0.7

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 维护。



EmAdsSDK 1.0.7

  • esell2016

历史版本,iOS EmSDK 更新日志

2024-05-15 SDK 1.0.0 1、初始版本,EmAds.framework 1.0.0,支持iOS 13+(含iOS 13)手动导入,完善Objective-C和Swift 的引入代码说明;

2024-05-20 SDK 1.0.0 1、版本修改,EmAds.framework 1.0.0, 版本号不改变,支持iOS 9+(含iOS 9)手动导入并通过编译,不crash,但仅支持iOS 13+ 才能初始化成功并展示广告,优化LOG输出; 2、增加iOS 13-,调用SDK初始化方法时,返回错误code 20988;

2024-05-21 SDK 1.0.0 1、修复EmAds.framework 1.0.0 iOS 12 运行crash 的问题

2024-05-22 SDK 1.0.1 1、优化SDK, 快手渠道的Intersititial广告声音无法关闭的问题 2、将其它渠道视频广告能关闭的声音关闭掉 「 快手的只有插屏Interstitial可以关,其它的也关不掉 百度联盟 的都不能关闭 优量汇的,NativeExpress RewardVideo FullScreenVideo Interstitial 可以关闭, Banner& Splash忽略 穿山甲的,都没有设置静音的功能 」

2024-06-03 SDK 1.0.2 1、重构瀑布流规则代码; 2、增加竞价规则代码;

2024-06-05 SDK 1.0.3 1、EmCore.framework,EmAds.framework代码内部处理

2024-06-16 SDK 1.0.3 1、尝试增加使用Cocoapods自动集成(暂未支持成功)

使用Cocoapods自动集成(暂未支持成功,推荐手动)

在Podfile 中增加

pod 'EmAdsSDK', '~> 1.0.3'

在info.plist 增加

SKAdNetworkItems - Array
item0 - Dictionary
    <key>SKAdNetworkIdentifier</key>
    <string>r3y5dwb26t.skadnetwork</string>
item1 - Dictionary
    <key>SKAdNetworkIdentifier</key>
    <string>238da6jt44.skadnetwork</string>
item2 - Dictionary
    <key>SKAdNetworkIdentifier</key>
    <string>22mmun2rn5.skadnetwork</string>


App Transport Security Settings - Dictionary
<key>NSAllowsArbitraryLoads</key>
<true/>

Privacy - Tracking Usage Description 对应值 "APP需要使用您的IDFA跟踪展示广告信息"

代码集成见【三、代码集成】

手动集成EmAds.framework

接入前提: 若您的项目已经集成百度联盟,穿山甲,优量汇,快手联盟的SDK, 请先移除;

一、库文件说明(目前仅支持真机)

baidumobadsdk.bundle—— 百度联盟的资源文件
CSJAdSDK.bundle —— 穿山甲联盟的资源文件
EmAds.framework —— EmAdsSDK
EmCore.framework —— EmAdsSDK内部引擎&基础代码
KSAdSDK.xcframework —— 快手联盟的SDK
README —— 接入文档

注意: EmAds.framework 版本支持iOS 9+ (含iOS 9)编译通过,但所有广告位均只支持iOS 13 + (含iOS 13),  针对 < iOS 13 系统,执行初始化代码时会提示初始化不成功,抛出20988错误码。

二、手动导入SDK

1、将EmSDK 1.0.0文件夹(README文件可以不需要)拖进Xcode Project工程, 勾选copy items if needed

2、选中目标Target, 切换到General选项卡, 滑动到Frameworks, Libraries, and Embedded Content栏, 将EmAds.framework,EmCore.framework,KSAdSDK.xcframework的Embed属性设置为Embed & Sign

3、选中目标Target,在其info 配置中添加配置

SKAdNetworkItems - Array
item0 - Dictionary
    <key>SKAdNetworkIdentifier</key>
    <string>r3y5dwb26t.skadnetwork</string>
item1 - Dictionary
    <key>SKAdNetworkIdentifier</key>
    <string>238da6jt44.skadnetwork</string>
item2 - Dictionary
    <key>SKAdNetworkIdentifier</key>
    <string>22mmun2rn5.skadnetwork</string>


App Transport Security Settings - Dictionary
<key>NSAllowsArbitraryLoads</key>
<true/>

Privacy - Tracking Usage Description 对应值 "APP需要使用您的IDFA跟踪展示广告信息"

(4、导入系统库-待定)

三、代码集成

1、 在AppDelegate 中添加属性window,GDTModSDK 的BUG,若去掉, GDTModSDK有概率Crash

Swift语言:
    var window: UIWindow?
    
Objective-C语言
    @property (nonatomic, strong) UIWindow *window;

2、 增加初始化代码

Swift 语言:
    在入口AppDelegate类中的 func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool 方法中增加下方代码:
        import EmAds
        EmAdsSDK.initSDK(launchOptions: launchOptions, isDebug: true, emlAppId: "填写你易售申请的APPID") { err in
            if err.code == .succeed {
                //初始化成功,每次启动仅有一次执行
            } else {
                //初始化失败,此逻辑可能会多次执行
            }
        }
    
Objective-C语言:
    在入口AppDelegate类 的方法 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 中增加下方代码:
        #import <EmAds/EmAds.h>
        [EmAdsSDK initSDKWithLaunchOptions:launchOptions isDebug:YES emlAppId:@"填写你易售申请的APPID" resultHandler:^(EmAdError * _Nonnull err) {
            if(err.code == EmAdErrorCodeSucceed) {
                //初始化成功,每次启动仅有一次执行
            } else {
                //初始化失败,此逻辑可能会多次执行
            }
        }];

isDebug = TRUE 表示控制台输出SDK内部日志

3、广告集成

1、Splash 广告集成
    
Swift 语言:
   
    import EmAds
    class SplashViewController: UIViewController {
        var splash: EmSplash?
        override func viewDidLoad() {
            super.viewDidLoad()
        }

        @IBAction func btnClickedAction(_ sender: UIButton) {
            self.splash = EmSplash.init(viewController: self, delegate: self)
            self.splash!.loadAndShow()  //加载并展示
        }
        
        @IBAction func btnClickedAction4(_ sender: UIButton) {
            self.splash?.destroy()      //销毁, 关闭广告时也会自动销毁, 可以重复调用
        }
        
    }

    extension SplashViewController: EmSplashDelegate {
        func onSplashComplete() {
            //广告倒计时结束的回调, 有些渠道不会执行此回调,会走onAdClose 回调
        }
        
        func onSplashSkip() {
            //广告跳过动作的回调, 有些渠道不会执行此回调,会走onAdClose 回调
        }
        
        func onAdSucceed() {
             //广告加载成功的回调
        }
        
        func onAdClose() {
             //广告被关闭的回调 
        }
        
        func onAdExposure() {
             //广告被曝光的回调
        }
        
        func onAdFailed(error: EmAdError) {
            //广告获取错误的回调,获取未能获取到广告等错误
        }
        
        func onAdClicked() {
            //广告被点击的回调
        }
    }


    
Objective-C语言:

    #import <EmAds/EmAds.h>

    @interface SplashViewController ()<EmSplashDelegate>

    @property (nonatomic, strong) EmSplash *splash;

    @end

    @implementation SplashViewController

    - (void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view.
    }

    - (IBAction)splashBtnAction:(UIButton *)sender {
        self.splash = [[EmSplash alloc] initWithViewController:self delegate:self];
        [self.splash loadAndShow]; //加载并展示
    }

    - (IBAction)splashBtnAction4:(UIButton *)sender {
        if(self.splash != nil) {
            [self.splash destroy]; //销毁, 关闭广告时也会自动销毁, 可以重复调用
        }
    }


    #pragma mark EmSplashDelegate


    - (void)onAdClicked { 
        SOCDemoDebugLog(@"EmSplashDelegate onAdClicked");   //广告被点击的回调
    }

    - (void)onAdClose { 
        SOCDemoDebugLog(@"EmSplashDelegate onAdClose");     //广告被关闭的回调
    }

    - (void)onAdExposure { 
        SOCDemoDebugLog(@"EmSplashDelegate onAdExposure");  //广告被曝光的回调
        
    }

    - (void)onAdFailedWithError:(EmAdError * _Nonnull)error { //广告获取错误的回调,获取未能获取到广告等错误
        NSString *str = [NSString stringWithFormat:@"EmSplashDelegate onAdExposure error: %@", error];
        SOCDemoDebugLog(str);
    }

    - (void)onAdSucceed { 
        SOCDemoDebugLog(@"EmSplashDelegate onAdSucceed");   //广告加载成功的回调
        
    }

    - (void)onSplashComplete { 
        SOCDemoDebugLog(@"EmSplashDelegate onSplashComplete");  //广告倒计时结束的回调,有些渠道不会执行此回调,会走onAdClose 回调
    }

    - (void)onSplashSkip {
        SOCDemoDebugLog(@"EmSplashDelegate onSplashSkip");  //广告跳过动作的回调, 有些渠道不会执行此回调,会走onAdClose 回调
    }

    @end

2、Interstitial 广告集成

Swift 语言:

    import EmAds
    class InterstitialViewController: UIViewController {
        var interstitial: EmInterstitial?
        override func viewDidLoad() {
            super.viewDidLoad()
        }

        @IBAction func btnInterstitialClickedAction(_ sender: UIButton) {
            self.interstitial = EmInterstitial.init(viewController: self, delegate: self)
            self.interstitial!.loadAndShow() //加载并展示广告
        }
        
        @IBAction func btnInterstitialClickedAction2(_ sender: UIButton) {
            self.interstitial = EmInterstitial.init(viewController: self, delegate: self)
            self.interstitial!.loadOnly()   //只加载广告,但不展示
        }
        
        @IBAction func btnInterstitialClickedAction3(_ sender: UIButton) {
            self.interstitial?.show()       //展示已经加载的广告,与loadOnly() 配合使用
        }
        
        
        @IBAction func btnInterstitialClickedAction4(_ sender: UIButton) {
            self.interstitial?.destroy()    //销毁广告,可以多次调用,也可以不手动调用,如果广告对象的携带者self被销毁, 广告也会自动销毁
        }
    }

    extension InterstitialViewController: EmInterstitialDelegate {
        
        func onAdSucceed() {
            //广告加载成功的回调
        }
        
        func onAdClose() {
            //广告被关闭的回调
        }
        
        func onAdExposure() {
            //广告被曝光的回调
        }
        
        func onAdFailed(error: EmAdError) {
            //广告获取错误的回调,获取未能获取到广告等错误
        }
        
        func onAdClicked() {
            //广告被点击的回调
        }
        
        
    }

    
Objective-C语言:

    #import <EmAds/EmAds.h>

    @interface InterstitialViewController ()<EmInterstitialDelegate>

    @property (nonatomic, strong) EmInterstitial *interstitial;

    @end

    @implementation InterstitialViewController

    - (void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view.
    }

    - (IBAction)interstitialBtnAction:(UIButton *)sender {
        self.interstitial = [[EmInterstitial alloc] initWithViewController:self delegate:self];
        [self.interstitial loadAndShow];    //加载并展示广告
    }


    - (IBAction)interstitialBtnAction2:(UIButton *)sender {
        self.interstitial = [[EmInterstitial alloc] initWithViewController:self delegate:self];
        [self.interstitial loadOnly];   //只加载广告,但不展示
    }


    - (IBAction)interstitialBtnAction3:(UIButton *)sender {
        if(self.interstitial != nil) {
            [self.interstitial show];   //展示已经加载的广告,与loadOnly 配合使用
        }
    }


    - (IBAction)interstitialBtnAction4:(UIButton *)sender {
        if(self.interstitial != nil) {
            [self.interstitial destroy];     //销毁广告,可以多次调用,也可以不手动调用,如果广告对象的携带者self被销毁, 广告也会自动销毁
        }
    }



    #pragma mark EmInterstitialDelegate

    - (void)onAdClicked {
        //广告被点击的回调
    }

    - (void)onAdClose {
        //广告被关闭的回调
    }

    - (void)onAdExposure {
        //广告被曝光的回调
    }

    - (void)onAdFailedWithError:(EmAdError * _Nonnull)error {
        //广告获取错误的回调,获取未能获取到广告等错误
    }

    - (void)onAdSucceed {
        //广告加载成功的回调
    }

    @end

3、 Banner 广告集成

Swift 语言:

    import EmAds
    class BannerViewController: UIViewController {

        @IBOutlet weak var adContannerView: UIView!
        var banner: EmBanner?
        
        override func viewDidLoad() {
            super.viewDidLoad()
        }

        @IBAction func btnBannerClickedAction(_ sender: UIButton) {
            self.banner = EmBanner.init(viewController: self, adContainer: self.adContannerView, delegate: self)
            self.banner!.loadAndShow() //加载并展示广告
        }
        
        @IBAction func btnBannerClickedAction4(_ sender: UIButton) {
            self.banner?.destroy()  //销毁广告,可以多次调用,也可以不手动调用,如果广告对象的携带者self被销毁, 广告也会自动销毁, 销毁时会从界面移除
        }
        
    }

    extension BannerViewController: EmBannerDelegate {
        
        func onAdSucceed() {
            //广告加载成功的回调
        }
        
        func onAdClose() {
            //广告被关闭的回调
        }
        
        func onAdExposure() {
            //广告被曝光的回调
        }
        
        func onAdFailed(error: EmAdError) {
            //广告获取错误的回调,获取未能获取到广告等错误
        }
        
        func onAdClicked() {
            //广告被点击的回调
        }
        
        
    }

    
Objective-C语言:
    
    #import <EmAds/EmAds.h>

    @interface BannerViewController ()<EmBannerDelegate>

    @property (nonatomic, strong) EmBanner *banner;
    @property (weak, nonatomic) IBOutlet UIView *adContainnerView;

    @end

    @implementation BannerViewController

    - (void)viewDidLoad {
        [super viewDidLoad];
    }

    - (IBAction)bannerBtnAction:(UIButton *)sender {
        self.banner = [[EmBanner alloc] initWithViewController:self adContainer:self.adContainnerView delegate:self];
        [self.banner loadAndShow];  //加载并展示广告
    }

    - (IBAction)bannerBtnAction4:(UIButton *)sender {
        [self.banner destroy];  //销毁广告,可以多次调用,也可以不手动调用,如果广告对象的携带者self被销毁, 广告也会自动销毁, 销毁时会从界面移除
    }



    #pragma mark EmBannerDelegate


    - (void)onAdClicked {
        //广告被点击的回调
    }

    - (void)onAdClose {
        //广告被关闭的回调
    }

    - (void)onAdExposure {
        //广告被曝光的回调
    }

    - (void)onAdFailedWithError:(EmAdError * _Nonnull)error {
        //广告获取错误的回调,获取未能获取到广告等错误
    }

    - (void)onAdSucceed {
        //广告加载成功的回调
    }

    @end

4、 NativeExpress 广告集成(相对特殊,需要自行处理关闭逻辑)

Swift 语言:
    import EmAds
    class NativeExpressViewController: UIViewController {
        
        var nativeExpress: EmNativeExpress?
        
        fileprivate var dataArray: [Any] = [] // TableView数据源 = 开发商的数据源 + 广告, 可能多种类型
        
        fileprivate var adExpressViews: NSMutableArray = NSMutableArray.init() // 存放拉取成功的广告,用于手动展示
        
        fileprivate lazy var tblView: UITableView = {
            var tbl: UITableView = UITableView.init(frame: self.view.bounds, style: .plain)
            tbl.register(UITableViewCell.self, forCellReuseIdentifier: "nativeexpresscell")
            tbl.register(ExamTableViewCell.self, forCellReuseIdentifier: "ExamTableViewCell")
            
            tbl.delegate = self
            tbl.dataSource = self
            tbl.tableFooterView = UIView.init()
            return tbl
        }()
        
        
        override func viewDidLoad() {
            super.viewDidLoad()
            // Do any additional setup after loading the view.
            self.tblView.frame = CGRect.init(x: 0, y: 400, width: self.view.bounds.width, height: self.view.bounds.height - 400)
            self.view.addSubview(self.tblView)
        }
        
        //测试数据
        func rePrepareTestData() {
            let obj1 = ExamCellModel.init()
            obj1.title = "1、 双12开抢春运火车票!成都至广州可购夜间动车票"
            obj1.subtitle = "2019年的日历越来越薄,以201x开头的年份也即将结束,一年一度的春运大幕即将拉开。"
            obj1.tag = "新闻"
            obj1.ctime = "12-11 12:17"
            obj1.cellh = 100
            obj1.showtype = 1
            let obj2 = ExamCellModel.init()
            obj2.title = "2、 双12开抢春运火车票!成都至广州可购夜间动车票"
            obj2.subtitle = "2019年的日历越来越薄,以201x开头的年份也即将结束,一年一度的春运大幕即将拉开。"
            obj2.tag = "新闻"
            obj2.ctime = "12-11 12:17"
            obj2.cellh = 100
            obj2.showtype = 1
            let obj3 = ExamCellModel.init()
            obj3.title = "3、 双12开抢春运火车票!成都至广州可购夜间动车票"
            obj3.subtitle = "2019年的日历越来越薄,以201x开头的年份也即将结束,一年一度的春运大幕即将拉开。"
            obj3.tag = "新闻"
            obj3.ctime = "12-11 12:17"
            obj3.cellh = 100
            obj3.showtype = 1
            self.dataArray = [obj1, obj2, obj3]
        }
        
        @IBAction func btnClickedAction(_ sender: UIButton) {
            self.dataArray = []
            self.adExpressViews = []
            self.tblView.reloadData()
            
            self.rePrepareTestData()
            
            self.nativeExpress = EmNativeExpress.init(viewController: self, 
                                                      delegate: self,
                                                      adSize: CGSizeMake(self.view.bounds.size.width, 0))
            self.nativeExpress!.loadAndShow() //加载并展示广告
            
        }
        
        @IBAction func btnClickedAction2(_ sender: UIButton) {
            self.dataArray = []
            self.adExpressViews = []
            self.tblView.reloadData()
            
            self.rePrepareTestData()
            
            self.nativeExpress = EmNativeExpress.init(viewController: self, 
                                                      delegate: self,
                                                      adSize: CGSizeMake(self.view.bounds.size.width, 0))
            self.nativeExpress!.loadOnly()  //加载但不展示广告
            
        }
        
        /// 手动展示广告
        @IBAction func btnClickedAction3(_ sender: UIButton) {
            if self.nativeExpress?.isLoadAndShow == true {
                SDemoDebugLog("请先调用self.nativeExpress.loadOnly()")
                return
            }
            
            if self.nativeExpress?.isLoaded != true {
                SDemoDebugLog("广告未加载成功或广告加载中")
                return
            }
            
            self.showNativeAds() //展示广告,信息流广告比较特殊,需要自行处理展示逻辑
        }
        
        
        @IBAction func btnnativeExpressClickedAction4(_ sender: UIButton) {
            self.nativeExpress?.destroy()   //销毁广告,信息流广告比较特殊,需要自行处理从界面删除逻辑

            self.dataArray.removeAll()
            self.adExpressViews.removeAllObjects()
            self.tblView.reloadData()
        }
        
        
        
        /// 信息流广告比较特殊, 渲染逻辑需要自行处理
        func showNativeAds() {
            //模拟将拉取的广告放在第二个位置
            for i in 0 ..< self.adExpressViews.count {
                if let v = self.adExpressViews[i] as? EmNativeExpressView {
                    v.render() //不能缺少
                    
                    var isInArray = false
                    for it in self.dataArray {
                        if let tIt = it as? EmNativeExpressView, tIt == v {
                            isInArray = true
                            break
                        }
                    }
                    if isInArray {//广告已经在列表中了
                        return
                    }
                    self.dataArray.insert(v, at: 1)
                }
            }
            
            if self.adExpressViews.count == 0 {
                self.rePrepareTestData()
            }
            self.tblView.reloadData()
        }
        
        deinit {
            SDemoDebugLog("NativeExpressViewController deinit")
        }
    }

    extension NativeExpressViewController: UITableViewDelegate, UITableViewDataSource {
        func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
            return self.dataArray.count
        }
        
        func numberOfSections(in tableView: UITableView) -> Int {
            return 1
        }
        
        func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
            let item = self.dataArray[indexPath.row]
            if item is ExamCellModel {
                return (item as! ExamCellModel).cellh!
            } else {
                if let ex = (item as? EmNativeExpressView)?.expressView {   //从返回的广告View直接获取高度进行布局
                    return ex.frame.size.height
                }
            }
            return 0
        }
        
        
        func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
            let item = self.dataArray[indexPath.row]
            if item is ExamCellModel {//展示集成商的自己的数据
                let cell = tableView.dequeueReusableCell(withIdentifier: "ExamTableViewCell", for: indexPath) as! ExamTableViewCell
                cell.selectionStyle = .none
                cell.setItem(item as! ExamCellModel)
                return cell
            } else {//展示广告数据源
                if let ex = (item as? EmNativeExpressView)?.expressView {
                    let eItemView = item as! EmNativeExpressView
                    let cell = tableView.dequeueReusableCell(withIdentifier: "nativeexpresscell", for: indexPath)
                    cell.selectionStyle = .none
                    
                    let subView = cell.contentView.viewWithTag(1000)
                      
                    let superview = subView?.superview
                    if superview != nil {
                        subView?.removeFromSuperview()
                    }
                      
                    let view = ex
                    view.tag = 1000
                    cell.contentView.addSubview(view)
                    cell.accessibilityIdentifier = "nativeTemp_ad"
                    eItemView.trackImpression() // 【必须注意的是 必须要调用,否则影响到计费, 必须确认展示在界面后,才能调用此方法】
                    return cell
                }
            }
            return UITableViewCell.init()
        }
        
        
    }

    extension NativeExpressViewController: EmNativeExpressDelegate {
        func onAdSucceed(adViews: [EmNativeExpressView]?, nativeExpressAd: EmNativeExpress) {
            //广告被加载成功的回调
            //需要手动处理界面自动展示的逻辑
            if nativeExpressAd.isLoadAndShow {
                self.adExpressViews = NSMutableArray(array: adViews ?? [])
                self.showNativeAds()
            } else {//如果是加载后并手动展示
                self.adExpressViews = NSMutableArray(array: adViews ?? [])
            }
            
        }
        
        func onAdClose(adView: EmNativeExpressView?, nativeExpressAd: EmNativeExpress) {
            //广告被关闭的回调
            //需要手动执行界面移除
            let tAdExpressViews = NSMutableArray.init(array: self.adExpressViews)
            for i in 0 ..< tAdExpressViews.count {
                if let tAd = tAdExpressViews[i] as? EmNativeExpressView, tAd.isEqual(adView) {//注意:此处不能用=或==判断
                    self.adExpressViews.removeObject(at: i)
                    tAd.destoryExpressView()
                    break
                }
            }
            self.showNativeAds()
        }
        
        func onAdExposure(adView: EmNativeExpressView?, nativeExpressAd: EmNativeExpress) {
            //广告被曝光的回调
        }
        
        func onAdFailed(error: EmAdError, nativeExpressAd: EmNativeExpress) {
            //广告获取错误的回调,获取未能获取到广告等错误
        }
        
        func onAdClicked(adView: EmNativeExpressView?, nativeExpressAd: EmNativeExpress) {
            //广告被点击的回调
        }
        
        func onAdRenderSuccess(adView: EmNativeExpressView?, nativeExpressAd: EmNativeExpress) {
            //广告被渲染成功的回调, 不是所有渠道都会执行此回调
        }
        
        func onAdRenderFailed(adView: EmNativeExpressView?, nativeExpressAd: EmNativeExpress) {
            //广告被渲染失败的回调, 不是所有渠道都会执行此回调
        }
        
    }


    
Objective-C语言:
  
    #import <EmAds/EmAds.h>

    @interface NativeExpressViewController ()<EmNativeExpressDelegate, UITableViewDelegate, UITableViewDataSource>

    @property (nonatomic, strong) EmNativeExpress *nativeExpress;

    @property (nonatomic, strong) NSMutableArray* dataArray;
    @property (nonatomic, strong) NSMutableArray* adExpressViews;

    @property (nonatomic, strong) UITableView* tblView;

    @end

    @implementation NativeExpressViewController

    - (UITableView *)tblView {
        if (_tblView == nil) {
            _tblView = [[UITableView alloc] initWithFrame:self.view.bounds style:(UITableViewStylePlain)];
            [_tblView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"nativeexpresscell"];
            [_tblView registerClass:[ExamTableViewCell class] forCellReuseIdentifier:@"ExamTableViewCell"];
            _tblView.delegate = self;
            _tblView.dataSource = self;
        }
        return _tblView;
    }

    - (void)viewDidLoad {
        [super viewDidLoad];
        
        self.tblView.frame = CGRectMake(0, 400, self.view.bounds.size.width, self.view.bounds.size.height - 400);
        [self.view addSubview:self.tblView];
    }

    //测试数据
    - (void)rePrepareTestData {
        ExamCellModel* obj1 = [[ExamCellModel alloc] init];
        obj1.title = @"1、 双12开抢春运火车票!成都至广州可购夜间动车票";
        obj1.subtitle = @"2019年的日历越来越薄,以201x开头的年份也即将结束,一年一度的春运大幕即将拉开。";
        obj1.tag = @"新闻";
        obj1.ctime = @"12-11 12:17";
        obj1.cellh = 100;
        obj1.showtype = 1;
        ExamCellModel* obj2 = [[ExamCellModel alloc] init];
        obj2.title = @"2、 双12开抢春运火车票!成都至广州可购夜间动车票";
        obj2.subtitle = @"2019年的日历越来越薄,以201x开头的年份也即将结束,一年一度的春运大幕即将拉开。";
        obj2.tag = @"新闻";
        obj2.ctime = @"12-11 12:17";
        obj2.cellh = 100;
        obj2.showtype = 1;
        ExamCellModel* obj3 = [[ExamCellModel alloc] init];
        obj3.title = @"3、 双12开抢春运火车票!成都至广州可购夜间动车票";
        obj3.subtitle = @"2019年的日历越来越薄,以201x开头的年份也即将结束,一年一度的春运大幕即将拉开。";
        obj3.tag = @"新闻";
        obj3.ctime = @"12-11 12:17";
        obj3.cellh = 100;
        obj3.showtype = 1;
        self.dataArray = [[NSMutableArray alloc] initWithArray:@[obj1, obj2, obj3]];
    }

    - (IBAction)btnAction:(UIButton *)sender {
        self.dataArray = [[NSMutableArray alloc] init];
        self.adExpressViews =  [[NSMutableArray alloc] init];
        [self.tblView reloadData];
        
        [self rePrepareTestData];
        
        
        self.nativeExpress = [[EmNativeExpress alloc] initWithViewController:self delegate:self adSize:(CGSizeMake(self.view.bounds.size.width, 0))];
        [self.nativeExpress loadAndShow]; //加载并展示广告
    }


    - (IBAction)btnAction2:(UIButton *)sender {
        self.dataArray = [[NSMutableArray alloc] init];
        self.adExpressViews =  [[NSMutableArray alloc] init];
        [self.tblView reloadData];
        
        [self rePrepareTestData];

        self.nativeExpress = [[EmNativeExpress alloc] initWithViewController:self delegate:self adSize:(CGSizeMake(self.view.bounds.size.width, 0))];
        [self.nativeExpress loadOnly];  //加载但不展示广告
    }


    - (IBAction)btnAction3:(UIButton *)sender {
        if (self.nativeExpress != nil) {
            if(self.nativeExpress.isLoadAndShow) {
                NSLog(@"请先调用[self.nativeExpress loadOnly]");
                return;
            }
            
            if(!self.nativeExpress.isLoaded) {
                NSLog(@"广告未加载成功或广告加载中");
                return;
            }
            
            [self showNativeAd];//展示广告,信息流广告比较特殊,需要自行处理展示逻辑

        }
    }


    - (IBAction)btnAction4:(UIButton *)sender {
        if (self.nativeExpress != nil) {
            [self.nativeExpress destroy]; //销毁广告,信息流广告比较特殊,需要自行处理从界面删除逻辑
        }
        [self.dataArray removeAllObjects];
        [self.adExpressViews removeAllObjects];
        [self.tblView reloadData];
    }

    // 信息流广告比较特殊, 渲染逻辑需要自行处理
    - (void)showNativeAd {
        for (NSInteger i = 0; i < self.adExpressViews.count; i++) {
            EmNativeExpressView *av = self.adExpressViews[i];
            [av render];//不能缺少
            Boolean isInArray = NO;
            for (NSInteger j = 0; j < self.dataArray.count; j++) {
                id it = self.dataArray[j];
                if(it == av) {
                    break;
                }
            }
            if (isInArray) {
                return;
            }
            [self.dataArray insertObject:self.adExpressViews[i] atIndex:1];
        }
        
        if (self.adExpressViews.count == 0) {
            [self rePrepareTestData];
        }
        [self.tblView reloadData];

    }

    #pragma mark  UITableViewDelegate, UITableViewDataSource

    - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
        return  1;
    }

    - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
        return self.dataArray.count;
    }

    - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
        if ([self.dataArray[indexPath.row] isKindOfClass:[ExamCellModel class]]) {
            return ((ExamCellModel *)self.dataArray[indexPath.row]).cellh;
        } else {
            CGFloat height = ([self.dataArray[indexPath.row] expressView]).frame.size.height; //从返回的广告View直接获取高度进行布局
            NSLog(@"=======> %f", height);
            return height;
        }
    }

    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
        UITableViewCell *cell;
        if ([self.dataArray[indexPath.row] isKindOfClass:[ExamCellModel class]]) {
            cell = [tableView dequeueReusableCellWithIdentifier:@"ExamTableViewCell"];
            ((ExamTableViewCell *)cell).item = self.dataArray[indexPath.row];
            return cell;
        } else {
            cell = [tableView dequeueReusableCellWithIdentifier:@"nativeexpresscell" forIndexPath:indexPath];
            cell.selectionStyle = UITableViewCellSelectionStyleNone;

            UIView *subView = (UIView *)[cell.contentView viewWithTag:1000];
            if ([subView superview]) {
                [subView removeFromSuperview];
            }
            UIView *view = [self.dataArray[indexPath.row] expressView];

            view.tag = 1000;
            [cell.contentView addSubview:view];
            cell.accessibilityIdentifier = @"nativeTemp_ad";
            id item = self.dataArray[indexPath.row];
            EmNativeExpressView *eItemView = (EmNativeExpressView *)item;
            [eItemView trackImpression];    // 【必须注意的是 必须要调用,否则影响到计费, 必须确认展示在界面后,才能调用此方法】
            return cell;
        }
    }


    #pragma mark EmNativeExpressDelegate


    - (void)onAdSucceedWithAdViews:(NSArray<EmNativeExpressView *> * _Nullable)adViews nativeExpressAd:(EmNativeExpress * _Nonnull)nativeExpressAd {
        //广告被加载成功的回调
        //需要手动处理界面自动展示的逻辑
        if (nativeExpressAd.isLoadAndShow) {
            self.adExpressViews = [[NSMutableArray alloc] initWithArray: adViews];
            [self showNativeAd];
        } else {
            self.adExpressViews = [[NSMutableArray alloc] initWithArray: adViews];
        }
    }

    - (void)onAdClickedWithAdView:(EmNativeExpressView * _Nullable)adView nativeExpressAd:(EmNativeExpress * _Nonnull)nativeExpressAd { 
        //广告被点击的回调
    }

    - (void)onAdCloseWithAdView:(EmNativeExpressView * _Nullable)adView nativeExpressAd:(EmNativeExpress * _Nonnull)nativeExpressAd {
        //广告被关闭的回调
        //需要手动执行界面移除
        
        NSMutableArray *tAdExpressViews = [[NSMutableArray alloc] initWithArray: self.adExpressViews];
        for(int i = 0; i < tAdExpressViews.count; i++) {
            EmNativeExpressView *tAd = (EmNativeExpressView *)tAdExpressViews[i];
            if([tAd isEqual:adView]) {//注意:此处不能用=或==判断
                [self.adExpressViews removeObjectAtIndex:i];
                [tAd destoryExpressView];
                break;
            }
        }
        [self showNativeAd];
    }

    - (void)onAdExposureWithAdView:(EmNativeExpressView * _Nullable)adView nativeExpressAd:(EmNativeExpress * _Nonnull)nativeExpressAd { 
        //广告被曝光的回调
    }

    - (void)onAdFailedWithError:(EmAdError * _Nonnull)error nativeExpressAd:(EmNativeExpress * _Nonnull)nativeExpressAd {
        //广告获取错误的回调,获取未能获取到广告等错误 
    }

    - (void)onAdRenderFailedWithAdView:(EmNativeExpressView * _Nullable)adView nativeExpressAd:(EmNativeExpress * _Nonnull)nativeExpressAd { 
        //广告被渲染失败的回调, 不是所有渠道都会执行此回调
    }

    - (void)onAdRenderSuccessWithAdView:(EmNativeExpressView * _Nullable)adView nativeExpressAd:(EmNativeExpress * _Nonnull)nativeExpressAd { 
        //广告被渲染成功的回调, 不是所有渠道都会执行此回调
        
    }


    @end

5、RewardVideo 广告集成

Swift 语言:

    import EmAds
    class RewardVideoViewController: UIViewController {
        var rewardVideo: EmRewardVideo?
        override func viewDidLoad() {
            super.viewDidLoad()
        }

        @IBAction func btnClickedAction(_ sender: UIButton) {
            self.rewardVideo = EmRewardVideo.init(viewController: self, delegate: self)
            self.rewardVideo!.loadAndShow()  //加载并展示广告
        }
        
        @IBAction func btnClickedAction2(_ sender: UIButton) {
            self.rewardVideo = EmRewardVideo.init(viewController: self, delegate: self)
            self.rewardVideo!.loadOnly()     //加载但不展示广告
        }
        
        @IBAction func btnClickedAction3(_ sender: UIButton) {
            self.rewardVideo?.show()        //展示已加载的广告,与loadOnly()配套使用
        }
        
        @IBAction func btnClickedAction4(_ sender: UIButton) {
            self.rewardVideo?.destroy()     //销毁广告,可以多次调用,也可以不手动调用,如果广告对象的携带者self被销毁, 广告也会自动销毁
        }
        
    }


    extension RewardVideoViewController: EmRewardVideoDelegate {
        
        func onAdSucceed() {
            //广告被加载成功的回调
        }
        
        func onAdClose() {
            //广告被关闭的回调
        }
        
        func onAdExposure() {
            //广告被曝光的回调
        }
        
        func onAdFailed(error: EmAdError) {
            //广告获取错误的回调,获取未能获取到广告等错误 
        }
        
        func onAdClicked() {
            //广告被点击的回调
        }
        
        func onVideoCached() {
            //广告被缓存的回调,少部分渠道会执行,可以不用过于关注
        }
        
        func onVideoComplete() {
            //广告播放结束的回调
        }
        
        func onVideoSkip() {
            //广告被跳过操作的回调, 有些渠道不会执行,会走onAdClose回调
        }
        
        func onAdReward() {
            //广告到了激励时间的回调
            //TODO: 您可以在此给用户分配激励权益   
        }
        
        func onRewardServerInf(inf: EARewardServerCallBackInf) {
            //备用回调,暂时没有用到
        }
        
    }

    
Objective-C语言:

    #import <EmAds/EmAds.h>

    @interface RewardVideoViewController ()<EmRewardVideoDelegate>

    @property (nonatomic, strong) EmRewardVideo *rewardVideo;

    @end

    @implementation RewardVideoViewController

    - (void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view.
    }

    - (IBAction)rewardVideoBtnAction:(UIButton *)sender {
        self.rewardVideo = [[EmRewardVideo alloc] initWithViewController:self delegate:self];
        [self.rewardVideo loadAndShow]; //加载并展示广告
    }


    - (IBAction)rewardVideoBtnAction2:(UIButton *)sender {
        self.rewardVideo = [[EmRewardVideo alloc] initWithViewController:self delegate:self];

        [self.rewardVideo loadOnly];//加载但不展示广告
    }


    - (IBAction)rewardVideoBtnAction3:(UIButton *)sender {
        if(self.rewardVideo != nil) {
            [self.rewardVideo show]; //展示已加载的广告,与loadOnly配套使用
        }
    }



    - (IBAction)rewardVideoBtnAction4:(UIButton *)sender {
        if(self.rewardVideo != nil) {
            [self.rewardVideo destroy]; //销毁广告,可以多次调用,也可以不手动调用,如果广告对象的携带者self被销毁, 广告也会自动销毁
        }
    }


    #pragma mark EmRewardVideoDelegate


    - (void)onAdClicked {
        //广告被点击的回调
    }

    - (void)onAdClose {
        //广告被关闭的回调
    }

    - (void)onAdExposure {
        //广告被曝光的回调
    }

    - (void)onAdFailedWithError:(EmAdError * _Nonnull)error {
        //广告获取错误的回调,获取未能获取到广告等错误 
    }

    - (void)onAdSucceed {
        //广告被加载成功的回调
    }


    - (void)onAdReward {
        //广告到了激励时间的回调
        //TODO: 您可以在此给用户分配激励权益   
    }

    - (void)onRewardServerInfWithInf:(EARewardServerCallBackInf * _Nonnull)inf {
        //备用回调,暂时没有用到
    }

    - (void)onVideoCached {
        //广告被缓存的回调,少部分渠道会执行,可以不用过于关注
    }

    - (void)onVideoComplete {
        //广告播放结束的回调
    }

    - (void)onVideoSkip {
        //广告被跳过操作的回调, 有些渠道不会执行,会走onAdClose回调
    }


    @end

6、 FullScreenVideo 广告集成

Swift 语言:


    import EmAds

    class FullScreenVideoViewController: UIViewController {
        var fullScreenVideo: EmFullScreenVideo?
        override func viewDidLoad() {
            super.viewDidLoad()
        }

        @IBAction func btnClickedAction(_ sender: UIButton) {
            self.fullScreenVideo = EmFullScreenVideo.init(viewController: self, delegate: self)
            self.fullScreenVideo!.loadAndShow() //加载并展示广告
        }
        
        @IBAction func btnClickedAction2(_ sender: UIButton) {
            self.fullScreenVideo = EmFullScreenVideo.init(viewController: self, delegate: self)
            self.fullScreenVideo!.loadOnly() //加载但不展示广告
        }
        
        @IBAction func btnClickedAction3(_ sender: UIButton) {
            self.fullScreenVideo?.show() //展示已加载的广告,与loadOnly()配套使用
        }
        
        @IBAction func btnClickedAction4(_ sender: UIButton) {
            self.fullScreenVideo?.destroy() //销毁广告,可以多次调用,也可以不手动调用,如果广告对象的携带者self被销毁, 广告也会自动销毁
        }
    }


    extension FullScreenVideoViewController: EmFullScreenVideoDelegate {
        
        func onAdSucceed() {
            //广告被加载成功的回调
        }
        
        func onAdClose() {
            //广告被关闭的回调
        }
        
        func onAdExposure() {
            //广告被曝光的回调
        }
        
        func onAdFailed(error: EmAdError) {
            //广告获取错误的回调,获取未能获取到广告等错误 
        }
        
        func onAdClicked() {
            //广告被点击的回调
        }
        
        func onVideoCached() {
            //广告被缓存的回调,少部分渠道会执行,可以不用过于关注
        }
        
        func onVideoComplete() {
            //广告播放结束的回调
        }
        
        func onVideoSkip() {
            //广告被跳过操作的回调, 有些渠道不会执行,会走onAdClose回调
        }
        
    }


    
Objective-C语言:

    #import <EmAds/EmAds.h>

    @interface FullScreenVideoViewController ()<EmFullScreenVideoDelegate>

    @property (nonatomic, strong) EmFullScreenVideo *fullScreenVideo;

    @end

    @implementation FullScreenVideoViewController

    - (void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view.
    }

    - (IBAction)fullScreenVideoBtnAction:(UIButton *)sender {
        self.fullScreenVideo = [[EmFullScreenVideo alloc] initWithViewController:self delegate:self];
        [self.fullScreenVideo loadAndShow]; //加载并展示广告
    }


    - (IBAction)fullScreenVideoBtnAction2:(UIButton *)sender {
        self.fullScreenVideo = [[EmFullScreenVideo alloc] initWithViewController:self delegate:self];
        [self.fullScreenVideo loadOnly];//加载但不展示广告
    }


    - (IBAction)fullScreenVideoBtnAction3:(UIButton *)sender {
        if(self.fullScreenVideo != nil) {
            [self.fullScreenVideo show]; //展示已加载的广告,与loadOnly配套使用
        }
    }



    - (IBAction)fullScreenVideoBtnAction4:(UIButton *)sender {
        if(self.fullScreenVideo != nil) {
            [self.fullScreenVideo destroy];//销毁广告,可以多次调用,也可以不手动调用,如果广告对象的携带者self被销毁, 广告也会自动销毁
        }
    }


    #pragma mark EmFullScreenVideoDelegate


    - (void)onAdClicked {
        //广告被点击的回调
    }

    - (void)onAdClose {
        //广告被关闭的回调
    }

    - (void)onAdExposure {
        //广告被曝光的回调
    }

    - (void)onAdFailedWithError:(EmAdError * _Nonnull)error {
        //广告获取错误的回调,获取未能获取到广告等错误 
    }

    - (void)onAdSucceed {
        //广告被加载成功的回调
    }


    - (void)onVideoCached {
        //广告被缓存的回调,少部分渠道会执行,可以不用过于关注
    }

    - (void)onVideoComplete {
        //广告播放结束的回调
    }

    - (void)onVideoSkip {
        //广告被跳过操作的回调, 有些渠道不会执行,会走onAdClose回调
    }


    @end

四、SDK错误码

0       成功
9901    ⼴告返回的数据为空,⼀般为渠道⼴告⽆填充
9905    当前策略中⽆有效的SDK渠道,或渠道信息为空
9906    策略调度时发⽣异常,不支持的渠道
9907    未获取到策略,appid 请求配置错误
-2009   数据解析错误
20999   网络断开
20997   appid 重试请求配置多次错误
20996   SDK初始化失败
20989   SDK未启用
20995   加载开屏广告失败
20994   加载插屏广告失败
20993   加载Banner广告失败
20992   加载原生模板,信息流广告失败
20991   加载激励视频广告失败
20990   加载全屏视频广告失败
20988   UnSupport iOS Version