在CocoaPod框架中Push视图控制器遇报错,需访问项目全局内容
Hey there! Let's tackle your two CocoaPods integration issues step by step. I've run into similar scenarios before, so here's what you can try:
Most push failures stem from not accessing the correct navigation controller or having visibility issues with the target view controller. Here are the common fixes:
a. Get the Current Visible Navigation Controller Correctly
On iOS 13+, relying on UIApplication.shared.keyWindow alone won't work because of scene-based app architecture. Use this method to reliably fetch the active navigation controller:
import UIKit public extension UIApplication { func currentNavigationController() -> UINavigationController? { guard let scene = connectedScenes.first as? UIWindowScene, let keyWindow = scene.windows.first(where: { $0.isKeyWindow }), let rootVC = keyWindow.rootViewController else { return nil } var currentVC = rootVC while let presentedVC = currentVC.presentedViewController { currentVC = presentedVC } // Check if current VC is a nav controller or has one as a child return currentVC as? UINavigationController ?? (currentVC.children.first as? UINavigationController) } }
Then use it to push your view controller:
if let navVC = UIApplication.shared.currentNavigationController() { let targetVC = YourTargetViewController() // Replace with your VC navVC.pushViewController(targetVC, animated: true) } else { print("Error: No active navigation controller found to push the VC") }
b. Resolve View Controller Visibility Issues
If your target view controller lives in the main app (not the framework), the framework can't directly access its type unless you:
- Expose the VC class: Mark the view controller as
public(oropenif you need subclassing) in your main app:public class YourTargetViewController: UIViewController { // ... your code ... } - Or use dynamic class lookup: If you can't make the VC public, use string-based class initialization (note: this is less type-safe):
guard let vcClass = NSClassFromString("YourAppName.YourTargetViewController") as? UIViewController.Type else { print("Error: Couldn't find the target view controller class") return } let targetVC = vcClass.init() // Push as before
c. Check for Storyboard Initialization Errors
If your VC is from a storyboard:
- Ensure the storyboard is either bundled with your framework, or pass the storyboard name and identifier from the main app to the framework (via a configuration method).
- Example framework configuration:
Then in the main app'spublic class YourFrameworkConfig { public static var targetStoryboardName: String = "" public static var targetVCIdentifier: String = "" }didFinishLaunching:
And in the framework:YourFrameworkConfig.targetStoryboardName = "Main" YourFrameworkConfig.targetVCIdentifier = "YourTargetVC"let storyboard = UIStoryboard(name: YourFrameworkConfig.targetStoryboardName, bundle: nil) let targetVC = storyboard.instantiateViewController(withIdentifier: YourFrameworkConfig.targetVCIdentifier) // Push the VC
Frameworks are sandboxed from the main app by default, so you need explicit ways to expose these. Here are the cleanest approaches:
a. Use a Protocol for Decoupled Access
This is the most maintainable method, as it keeps your framework independent of the app's implementation details.
- In your framework, define a protocol that outlines the global properties/functions you need:
public protocol AppGlobalsProvider { static var appGlobalVariable: String { get } static func appGlobalFunction() -> Void } - In the main app, make a class (like
AppDelegate) conform to this protocol:import YourFramework extension AppDelegate: AppGlobalsProvider { static var appGlobalVariable: String { "Hello from the main app!" } static func appGlobalFunction() { print("This is a global function from the main app") } } - In your framework, store a reference to the provider class (set this in the main app):
public class YourFrameworkManager { public static var globalsProvider: AppGlobalsProvider.Type? public static func useGlobalVariable() { guard let provider = globalsProvider else { print("Error: Globals provider not set") return } print(provider.appGlobalVariable) } public static func callGlobalFunction() { globalsProvider?.appGlobalFunction() } } - In the main app's
didFinishLaunching, set the provider:YourFrameworkManager.globalsProvider = AppDelegate.self
b. Use Closures to Pass References
If you need a quick solution, pass closures from the main app to the framework:
- In your framework, define optional closure properties:
public class YourFrameworkManager { public static var globalVariableGetter: (() -> String)? public static var globalFunction: (() -> Void)? } - In the main app, assign these closures to your global variables/functions:
// Main app's global items let appGlobalVariable = "Main app global var" func appGlobalFunction() { print("Main app global function called") } // Set in AppDelegate YourFrameworkManager.globalVariableGetter = { appGlobalVariable } YourFrameworkManager.globalFunction = { appGlobalFunction() } - In the framework, call them safely:
if let variable = YourFrameworkManager.globalVariableGetter?() { print(variable) } YourFrameworkManager.globalFunction?()
c. Use Objective-C Runtime (For Objective-C Compatible Apps)
If your app uses Objective-C or exposes Swift items to Objective-C, you can use runtime to access globals:
- In the main app, mark your global items with
@objcandpublic:@objc public var appGlobalVariable: String = "Main app global" @objc public func appGlobalFunction() { print("Global function called") } - In the framework, use runtime to fetch them:
import ObjectiveC // Get global variable if let variable = objc_getAssociatedObject(UIApplication.shared, "appGlobalVariable") as? String { print(variable) } // Call global function if let function = NSClassFromString("YourAppName.AppDelegate")?.perform(Selector("appGlobalFunction")) { // Handle result if needed }
Note: This method is less type-safe, so use it only if the above approaches don't fit.
内容的提问来源于stack exchange,提问作者hashB

