Swift新手遇含应用内评分与日志的App Xcode链接错误求助
作为Swift新手,在运行集成应用内评分(In-App Review)与日志(Logging)功能的Godot引擎iOS插件时,Xcode出现链接错误:
ld: Undefined symbols:
os.Logger.logObject.getter : __C.OS_os_log, referenced from:
(1) suspend resume partial function for closure #1 @Sendable () async -> () in static godot_plugin.SwiftClass.launch_review_flow() -> () in InappReviewPlugin.a4
(3) suspend resume partial function for closure #1 @Sendable () async -> () in static godot_plugin.SwiftClass.launch_review_flow() -> () in InappReviewPlugin.a4
os.Logger.init() -> os.Logger, referenced from:
one-time initialization function for logger in InappReviewPlugin.a4
type metadata accessor for os.Logger, referenced from:
one-time initialization function for logger in InappReviewPlugin.a4
(1) suspend resume partial function for closure #1 @Sendable () async -> () in static godot_plugin.SwiftClass.launch_review_flow() -> () in InappReviewPlugin.a4
插件Swift代码基于StoreKit实现应用内评分功能:
import Foundation import SwiftUI import OSLog import StoreKit @available(iOS 16.0, *) extension UIApplication { class func getTopViewController(base: UIViewController? = UIApplication.shared.windows.first { $0.isKeyWindow }?.rootViewController) -> UIViewController? { if let nav = base as? UINavigationController { return getTopViewController(base: nav.visibleViewController) } else if let tab = base as? UITabBarController, let selected = tab.selectedViewController { return getTopViewController(base: selected) } else if let presented = base?.presentedViewController { return getTopViewController(base: presented) } return base } } @available(iOS 16.0, *) extension UIViewController { /// Request a App Store review from the user /// /// - Parameters: /// - delay: The number of seconds this function will wait before showing the modal. func requestReview(delay: Double) { Task { // Use the equation n * 10^9 to convert seconds to nanoseconds. try? await Task.sleep(nanoseconds: UInt64(delay * pow(10.0, 9.0))) if let windowScene = self.view.window?.windowScene, self.navigationController?.topViewController == self { SKStoreReviewController.requestReview(in: windowScene) } } } } @available(iOS 16.0, *) @objcMembers public class SwiftClass : NSObject { static let logger = Logger() static func launch_review_flow() { Task { logger.info("SwiftClass: launch_review_flow(): Task") if let topVC = await UIApplication.getTopViewController() { logger.info("SwiftClass: launch_review_flow(): Task: topVC") await topVC.requestReview(delay: 1.5) } } } }
已知问题与Logger及await语句相关,但多次尝试修复失败,需解决链接错误并获取代码优化建议。
1. 修复OSLogger链接错误
链接错误核心原因是OSLog框架未被正确链接到插件目标,或Godot插件编译配置缺少OSLog支持,可通过以下步骤解决:
- 添加OSLog框架:在Xcode插件目标的
Build Phases->Link Binary With Libraries中,点击+搜索并添加OSLog.framework。 - 验证iOS版本:
Logger要求iOS 14.0+,代码已标注iOS 16.0+,需确保插件Deployment Target设置为16.0或更高。 - 关闭死代码剥离:若上述步骤无效,在插件目标
Build Settings中,将Dead Code Stripping设为NO,避免编译器误删OSLog相关符号。
2. 代码优化建议
(1)Logger初始化优化
显式指定日志子系统和分类,方便后续日志筛选:
static let logger = Logger(subsystem: "com.yourcompany.godot-plugin", category: "InAppReview")
(2)异步睡眠简化
用Task.sleep(for:)替代手动浮点转换,避免计算误差:
// 原代码 try? await Task.sleep(nanoseconds: UInt64(delay * pow(10.0, 9.0))) // 优化后 try? await Task.sleep(for: .seconds(delay))
(3)顶层视图控制器获取优化
适配iOS 13+的UIWindowScene方案,替代旧的UIApplication.shared.windows:
@available(iOS 16.0, *) extension UIApplication { class func getTopViewController() -> UIViewController? { guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene, let rootVC = windowScene.windows.first?.rootViewController else { return nil } var topVC = rootVC while let presentedVC = topVC.presentedViewController { topVC = presentedVC if let navVC = topVC as? UINavigationController { topVC = navVC.visibleViewController ?? topVC } else if let tabVC = topVC as? UITabBarController { topVC = tabVC.selectedViewController ?? topVC } } return topVC } }
(4)评分请求条件增强
添加可用性检查与错误日志,提升鲁棒性:
@available(iOS 16.0, *) extension UIViewController { func requestReview(delay: Double) { guard SKStoreReviewController.isAvailable else { SwiftClass.logger.error("应用内评分当前不可用") return } Task { try? await Task.sleep(for: .seconds(delay)) guard let windowScene = view.window?.windowScene else { SwiftClass.logger.error("无法获取当前窗口场景") return } SKStoreReviewController.requestReview(in: windowScene) } } }
(5)异步函数调用简化
移除不必要的await(getTopViewController并非异步函数),精简代码:
static func launch_review_flow() { Task { logger.info("启动应用内评分流程") guard let topVC = UIApplication.getTopViewController() else { logger.error("无法获取顶层视图控制器") return } await topVC.requestReview(delay: 1.5) } }
内容的提问来源于stack exchange,提问作者Cengiz

