iOS 常见问题
Q1:编译报 no such module 'UjuAdCore'
原因:SWIFT_INCLUDE_PATHS 未正确指向 xcframework 的 Headers 目录。
解决:
- 确认
UjuAdCore.xcframework已添加到项目 - 在 Build Settings 中配置
SWIFT_INCLUDE_PATHS:SWIFT_INCLUDE_PATHS[sdk=iphoneos*] = $(inherited) $(SRCROOT)/Frameworks/UjuAdCore.xcframework/ios-arm64/HeadersSWIFT_INCLUDE_PATHS[sdk=iphonesimulator*] = $(inherited) $(SRCROOT)/Frameworks/UjuAdCore.xcframework/ios-arm64_x86_64-simulator/Headers
- 关闭
SWIFT_ENABLE_EXPLICIT_MODULES(设为NO)
详见 准备工作。
Q2:链接报 undefined symbol: GRPC.xxx / SwiftProtobuf.xxx
原因:未使用合并后的 xcframework,或重复引入了 gRPC-Swift / SwiftProtobuf 依赖。
解决:
- 确认使用的是 3.4.2 及以上版本的
UjuAdCore.xcframework(已合并 UjuGRPCStatic) - 移除 Podfile 中的
grpc-swift/swift-protobuf依赖 - 检查是否有多份 UjuAdCore 副本(如同时通过 xcframework 和 CocoaPods 引入)
Q3:SDK 初始化失败,错误码 1(initFailed)
排查步骤:
- 检查
appId/appKey(RSA 公钥)是否正确(由优聚智汇平台分配) - 检查
region是否选对(.domestic国内 /.singapore海外) - 开启
isDebug = true,查看控制台日志中的具体失败原因 - 确认网络可达对应区域的服务器域名
Q4:广告加载失败,错误码 102(noFill)
说明:这是正常业务响应,表示 ADX 本次无出价。可能原因:
- 当前广告位无填充
- 用户频次已达到上限
- 设备/区域无匹配广告
建议:可降级到其他广告源,或间隔一段时间后重试。
Q5:开屏广告 onAdDismissed 何时回调?
回调时机:
- 用户主动跳过开屏(点击跳过按钮)
- 开屏倒计时结束
建议:在 onAdDismissed 中进入主界面,不要在 onAdClosed 中进入(后者仅表示广告视图被移除)。
Q6:激励视频如何判断用户应得奖励?
判断逻辑:
onAdRewardArrived()回调 → 应发放奖励(用户观看完整视频)onAdSkippedVideo()回调 → 不应发放奖励(用户跳过视频)onAdPlayComplete()回调 → 视频播放完成(不直接等于发奖,以onAdRewardArrived为准)
详见 激励视频 - 奖励判断逻辑。
Q7:如何调试 SDK 内部日志?
方法 1(推荐):设置 isDebug = true
let config = UjuAdInitConfig.create(
appId: "...",
appKey: "...",
isDebug: true, // ★ 开启 SDK 内部日志
// ...
)SDK 内部通过 NSLog 输出日志,可在 Xcode 控制台或 Console.app 中查看。发布前请改为 false。
方法 2:使用系统 OSLog
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 中设置:
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 版本?
print(UjuAdCore.shared.getVersion()) // "3.4.2"或查看 xcframework 内的 UjuAdCore.version.json。
Q12:运行时崩溃 EXC_BAD_ACCESS (code=1, address=0x30 / 0x50),崩溃栈在 ManagedAtomic.__allocating_init 或 NIOPosix.BaseSocketChannel.isActive.getter
根因:
UjuAdCore 静态链接了 swift-atomics,其中 ManagedAtomic<Value> 是一个带 @_alwaysEmitIntoClient 标记的泛型 class。该标记会让 init 等方法被内联到调用方,但泛型 class 的 type metadata accessor 函数是由静态库按需提供的。
在静态库 demand-driven 链接模式下,链接器会扫描"哪些 .o 被引用",只保留被引用的 .o。由于 @_alwaysEmitIntoClient 把 init 内联到了调用方,泛型 class 的 type metadata accessor 在调用方看来没有任何符号引用,于是被链接器判定为"未使用"而 dead-strip。
运行时 ManagedAtomic.__allocating_init 会调用 type metadata accessor 获取 metadata,被 dead-strip 后该函数返回 nil,随后访问 metadata 字段就会访问到地址 0x30/0x50 → EXC_BAD_ACCESS。
崩溃出现在 SDK 发起首次网络请求时,表现为:
- SDK 发起首次网络请求后立即崩溃
- 崩溃出现在 SDK 内部子线程
- 崩溃栈顶:
ManagedAtomic.__allocating_init或BaseSocketChannel.isActive.getter
解决:
在 Target → Build Settings → Other Linker Flags 中添加 -force_load,强制保留 UjuAdCore 静态库的全部 .o(按 SDK 条件选择对应 slice):
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.aCocoaPods 集成方: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 中设置:
ENABLE_DEBUG_DYLIB = NO影响:仅关闭 Xcode 26 的 Swift UI 预览热重载加速特性,不影响 SDK 功能,也不影响 Debug 调试(断点、LLDB、NSLog 全部正常)。生产 Release 构建不受影响(Release 配置本就不启用 Debug Dylib)。
Q14:如何自查"符号是否被正确链接"?
集成完成后,可在终端执行以下命令验证关键符号是否被保留到 App 主二进制(路径替换为实际产物):
# 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_LDFLAGS 与 ENABLE_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(路径替换为实际解压后的目录结构):
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 适配器):
<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 包含 struct、Sendable 协议、Builder 模式等 Swift 独有特性,未提供独立的 Objective-C 兼容层。纯 Objective-C 项目需通过 Swift 混编 + 自行封装 @objc wrapper 接入。
接入步骤:
开启 Swift 混编:在 Xcode 项目中新建一个
.swift文件(可仅写一行import UjuAdCore),Xcode 会提示创建 Bridging Header,选择 Create Bridging Header 即可开启 Swift 编译支持。封装
@objcWrapper:由于UjuAdCore、UjuAdObject等公开类未继承NSObject,且UjuAdInitConfig为struct+ 静态 builder,OC 无法直接调用,需在 Swift 文件中编写继承NSObject的 wrapper 类并用@objc暴露给 OC。示例:
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()
}
}- OC 调用:在 OC 代码中通过
#import "<ProductName>-Swift.h"调用 wrapper:
#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 中用@objcprotocol 或 block 桥接回调。UjuAdRegion等 enum 需在 wrapper 中转为NSInteger暴露给 OC。- 5 种广告对象(
UjuAdObject)的工厂方法和监听器都建议在 wrapper 中封装为 OC 友好接口。 - 详细 Wrapper 设计可参考 官方 ExternalApp Demo 中的 Swift 桥接实现。
联系技术支持
如果您遇到本文未覆盖的问题,可以通过以下方式获取支持:
- 邮件:marco@ujuad.com
- 官网:https://www.ujuad.com
