Skip to content

iOS 常见问题

Q1:编译报 no such module 'UjuAdCore'

原因SWIFT_INCLUDE_PATHS 未正确指向 xcframework 的 Headers 目录。

解决

  1. 确认 UjuAdCore.xcframework 已添加到项目
  2. 在 Build Settings 中配置 SWIFT_INCLUDE_PATHS
    • SWIFT_INCLUDE_PATHS[sdk=iphoneos*] = $(inherited) $(SRCROOT)/Frameworks/UjuAdCore.xcframework/ios-arm64/Headers
    • SWIFT_INCLUDE_PATHS[sdk=iphonesimulator*] = $(inherited) $(SRCROOT)/Frameworks/UjuAdCore.xcframework/ios-arm64_x86_64-simulator/Headers
  3. 关闭 SWIFT_ENABLE_EXPLICIT_MODULES(设为 NO

详见 准备工作

Q2:链接报 undefined symbol: GRPC.xxx / SwiftProtobuf.xxx

原因:未使用合并后的 xcframework,或重复引入了 gRPC-Swift / SwiftProtobuf 依赖。

解决

  1. 确认使用的是 3.4.2 及以上版本的 UjuAdCore.xcframework(已合并 UjuGRPCStatic)
  2. 移除 Podfile 中的 grpc-swift / swift-protobuf 依赖
  3. 检查是否有多份 UjuAdCore 副本(如同时通过 xcframework 和 CocoaPods 引入)

Q3:SDK 初始化失败,错误码 1(initFailed)

排查步骤

  1. 检查 appId / appKey(RSA 公钥)是否正确(由优聚智汇平台分配)
  2. 检查 region 是否选对(.domestic 国内 / .singapore 海外)
  3. 开启 isDebug = true,查看控制台日志中的具体失败原因
  4. 确认网络可达对应区域的服务器域名

Q4:广告加载失败,错误码 102(noFill)

说明:这是正常业务响应,表示 ADX 本次无出价。可能原因:

  • 当前广告位无填充
  • 用户频次已达到上限
  • 设备/区域无匹配广告

建议:可降级到其他广告源,或间隔一段时间后重试。

Q5:开屏广告 onAdDismissed 何时回调?

回调时机

  • 用户主动跳过开屏(点击跳过按钮)
  • 开屏倒计时结束

建议:在 onAdDismissed 中进入主界面,不要在 onAdClosed 中进入(后者仅表示广告视图被移除)。

Q6:激励视频如何判断用户应得奖励?

判断逻辑

  • onAdRewardArrived() 回调 → 应发放奖励(用户观看完整视频)
  • onAdSkippedVideo() 回调 → 不应发放奖励(用户跳过视频)
  • onAdPlayComplete() 回调 → 视频播放完成(不直接等于发奖,以 onAdRewardArrived 为准)

详见 激励视频 - 奖励判断逻辑

Q7:如何调试 SDK 内部日志?

方法 1(推荐):设置 isDebug = true

swift
let config = UjuAdInitConfig.create(
    appId: "...",
    appKey: "...",
    isDebug: true,  // ★ 开启 SDK 内部日志
    // ...
)

SDK 内部通过 NSLog 输出日志,可在 Xcode 控制台或 Console.app 中查看。发布前请改为 false

方法 2:使用系统 OSLog

swift
import os.log
let log = OSLog(subsystem: Bundle.main.bundleIdentifier ?? "your.app", category: "UjuAdCore")
os_log("custom log: %{public}@", log: log, message)

Q8:CocoaPods 集成时报 SDK does not support iOS 12

解决:UjuAdCore 最低支持 iOS 13.0,在 Podfile 中设置:

ruby
platform :ios, '13.0'

Q9:如何在 macOS 上编译?

不支持UjuAdCore.xcframework 为 iOS-only,在 macOS host 上编译会失败。SDK 仅支持 iOS 平台编译,不支持 macOS。

Q10:SDK 是否支持 bitcode?

不支持。Xcode 16+ 已废弃 bitcode,SDK 也不启用 bitcode。请在 Build Settings 中关闭 ENABLE_BITCODE

Q11:如何确认 SDK 版本?

swift
print(UjuAdCore.shared.getVersion())  // "3.4.2"

或查看 xcframework 内的 UjuAdCore.version.json

Q12:运行时崩溃 EXC_BAD_ACCESS (code=1, address=0x30 / 0x50),崩溃栈在 ManagedAtomic.__allocating_initNIOPosix.BaseSocketChannel.isActive.getter

根因

UjuAdCore 静态链接了 swift-atomics,其中 ManagedAtomic<Value> 是一个带 @_alwaysEmitIntoClient 标记的泛型 class。该标记会让 init 等方法被内联到调用方,但泛型 class 的 type metadata accessor 函数是由静态库按需提供的。

在静态库 demand-driven 链接模式下,链接器会扫描"哪些 .o 被引用",只保留被引用的 .o。由于 @_alwaysEmitIntoClientinit 内联到了调用方,泛型 class 的 type metadata accessor 在调用方看来没有任何符号引用,于是被链接器判定为"未使用"而 dead-strip

运行时 ManagedAtomic.__allocating_init 会调用 type metadata accessor 获取 metadata,被 dead-strip 后该函数返回 nil,随后访问 metadata 字段就会访问到地址 0x30/0x50EXC_BAD_ACCESS

崩溃出现在 SDK 发起首次网络请求时,表现为:

  • SDK 发起首次网络请求后立即崩溃
  • 崩溃出现在 SDK 内部子线程
  • 崩溃栈顶:ManagedAtomic.__allocating_initBaseSocketChannel.isActive.getter

解决

在 Target → Build SettingsOther Linker Flags 中添加 -force_load,强制保留 UjuAdCore 静态库的全部 .o(按 SDK 条件选择对应 slice):

text
OTHER_LDFLAGS[sdk=iphoneos*] = -force_load $(SRCROOT)/Frameworks/UjuAdCore.xcframework/ios-arm64/libUjuAdCore-iphoneos.a
OTHER_LDFLAGS[sdk=iphonesimulator*] = -force_load $(SRCROOT)/Frameworks/UjuAdCore.xcframework/ios-arm64_x86_64-simulator/libUjuAdCore-simulator.a

CocoaPods 集成方:CocoaPods 会自动设置 OTHER_LDFLAGS = -ObjC,该标志对 Objective-C class 有 dead-strip 保护,但对 Swift 泛型 class 的 type metadata accessor 无效。若使用 CocoaPods 静态库模式集成,仍需手动追加 -force_load。建议优先使用 XCFramework 集成。

Q13:Xcode 26 运行时崩溃在主线程 objc_msgSend(SIGSEGV),崩溃栈含 __NSThreadPerformPerform

根因:Xcode 26 默认启用 Debug Dylib 特性(ENABLE_DEBUG_DYLIB = YES),会把 App 代码拆分到 <App>.debug.dylib(可热重载)和主二进制两部分。该拆分与 UjuAdCore 静态库的符号链接冲突:SDK 的部分符号被划到 .debug.dylib,而 type metadata accessor 被划到主二进制,跨 dylib 边界访问时元数据不一致 → objc_msgSend 访问已释放 receiver → SIGSEGV。

解决:在 Target → Build Settings 中设置:

text
ENABLE_DEBUG_DYLIB = NO

影响:仅关闭 Xcode 26 的 Swift UI 预览热重载加速特性,不影响 SDK 功能,也不影响 Debug 调试(断点、LLDB、NSLog 全部正常)。生产 Release 构建不受影响(Release 配置本就不启用 Debug Dylib)。

Q14:如何自查"符号是否被正确链接"?

集成完成后,可在终端执行以下命令验证关键符号是否被保留到 App 主二进制(路径替换为实际产物):

bash
# 1. _AtomicsShims 的 C 桥接函数(若缺失 → -force_load 未生效)
nm -gU <App>.app/<App> | grep "__sa_"
# 期望输出:__sa_retain_n / __sa_release_n

# 2. ManagedAtomic<Bool> 的 type metadata(若缺失 → 仍会崩溃)
nm <App>.app/<App> | grep "7Atomics13ManagedAtomicCySbG"
# 期望包含 MR(metadata accessor)符号

若以上检查不通过,请回到 准备工作 复核 OTHER_LDFLAGSENABLE_DEBUG_DYLIB 配置。

Q15:集成 UjuAdExt 后链接报 symbol(s) not found for architecture arm64,含 _OBJC_CLASS_$_*** 未定义

原因:UjuAdExt zip 内 Vendor/ 目录下的第三方 DSP SDK 为静态库(.framework 内含 .a),其 OC 类符号在默认链接模式下会被 dead-strip,必须逐个 -force_load

解决

OTHER_LDFLAGS 中追加 Vendor 目录下每个 .xcframework 内静态库的 -force_load(路径替换为实际解压后的目录结构):

text
OTHER_LDFLAGS[sdk=iphoneos*] = $(inherited) \
  -force_load $(SRCROOT)/Frameworks/UjuAdCore.xcframework/ios-arm64/libUjuAdCore-iphoneos.a \
  -force_load $(SRCROOT)/Frameworks/UjuAdExt.xcframework/ios-arm64/libUjuAdExt-iphoneos.a \
  -force_load $(SRCROOT)/Frameworks/Vendor/Adx24/<SDKName>.xcframework/ios-arm64*/<SDKName>.framework/<SDKName> \
  -force_load $(SRCROOT)/Frameworks/Vendor/Adx28/<SDKName>.xcframework/ios-arm64*/<SDKName>.framework/<SDKName>

详见 准备工作 - 步骤 5

Q16:集成 UjuAdExt 后,SDK 启动日志输出 Info.plist 反射扫描完成,成功注册 0 个第三方适配器

原因:UjuAdExt 适配器不会自动注册,必须在 Info.plist 中配置 uju_adapter_* key 才能被 SDK 反射发现。

解决

在 Info.plist 中添加适配器注册 key(当前 UjuAdExt 3.4.2 内置 2 个 DSP 适配器):

xml
<key>uju_adapter_adx24</key>
<string>Adx24Adapter.Adx24AdapterFactory</string>
<key>uju_adapter_adx28</key>
<string>Adx28Adapter.Adx28AdapterFactory</string>

配置后,开启 isDebug = true 重启 App,SDK 启动日志应输出 Info.plist 反射扫描完成,成功注册 N 个第三方适配器(N 为您配置的适配器数量)。

详见 准备工作 - 步骤 5

Q17:Objective-C 项目如何接入?

说明:优聚智汇 SDK 使用 Swift 开发,公开 API 包含 structSendable 协议、Builder 模式等 Swift 独有特性,未提供独立的 Objective-C 兼容层。纯 Objective-C 项目需通过 Swift 混编 + 自行封装 @objc wrapper 接入。

接入步骤

  1. 开启 Swift 混编:在 Xcode 项目中新建一个 .swift 文件(可仅写一行 import UjuAdCore),Xcode 会提示创建 Bridging Header,选择 Create Bridging Header 即可开启 Swift 编译支持。

  2. 封装 @objc Wrapper:由于 UjuAdCoreUjuAdObject 等公开类未继承 NSObject,且 UjuAdInitConfigstruct + 静态 builder,OC 无法直接调用,需在 Swift 文件中编写继承 NSObject 的 wrapper 类并用 @objc 暴露给 OC。示例:

swift
import UjuAdCore

@objc(UjuAdBridge)
final class UjuAdBridge: NSObject {

    @objc static func initialize(appId: String,
                                  appKey: String,
                                  isDebug: Bool,
                                  rsaPublicKey: String) {
        let config = UjuAdInitConfig.create(
            appId: appId,
            appKey: appKey,
            isDebug: isDebug,
            rsaPublicKey: rsaPublicKey
        )
        UjuAdCore.shared.initialize(nil, config: config)
    }

    @objc static func start() {
        UjuAdCore.shared.start(nil)
    }

    @objc static func version() -> String {
        return UjuAdCore.shared.getVersion()
    }
}
  1. OC 调用:在 OC 代码中通过 #import "<ProductName>-Swift.h" 调用 wrapper:
objc
#import "YourApp-Swift.h"

[UjuAdBridge initializeWithAppId:@"YOUR_APP_ID"
                          appKey:@"YOUR_APP_KEY"
                          isDebug:YES
                     rsaPublicKey:@"YOUR_RSA_PUBLIC_KEY"];
[UjuAdBridge start];
NSLog(@"SDK version: %@", [UjuAdBridge version]);

注意事项

  • BaseInitListener、广告监听器等协议含 Sendable 约束,无法用 @objc 直接暴露,需在 wrapper 中用 @objc protocol 或 block 桥接回调。
  • UjuAdRegion 等 enum 需在 wrapper 中转为 NSInteger 暴露给 OC。
  • 5 种广告对象(UjuAdObject)的工厂方法和监听器都建议在 wrapper 中封装为 OC 友好接口。
  • 详细 Wrapper 设计可参考 官方 ExternalApp Demo 中的 Swift 桥接实现。

联系技术支持

如果您遇到本文未覆盖的问题,可以通过以下方式获取支持:

相关链接