Swift项目跨依赖层级调用及SwiftUI循环依赖编译问题咨询
解决方案:SwiftUI多Target循环依赖与跨模块调用问题
针对你遇到的两个核心问题,我们可以通过协议抽象+依赖注入的方式解决,核心思路是遵循依赖倒置原则,让低层级模块依赖抽象而非具体实现,避免循环依赖。
一、解决EntryPoint的App结构体引用Experience中ContentView的问题
实现步骤:
- 在EntryPoint中定义抽象协议,描述所需的Scene能力
- 在Experience中实现该协议,返回包含ContentView的Scene
- 在AppTarget中作为中间层,将Experience的实现注入到EntryPoint的App结构体中
代码示例:
EntryPoint模块代码
// EntryPoint/EntryApp.swift import SwiftUI // 定义抽象协议,描述提供Scene的能力 public protocol AppSceneProvider { var scene: some Scene { get } } // 应用入口结构体,依赖抽象协议而非具体实现 public struct EntryApp: App { private let sceneProvider: AppSceneProvider // 对外暴露初始化方法,接收协议实现 public init(sceneProvider: AppSceneProvider) { self.sceneProvider = sceneProvider } public var body: some Scene { sceneProvider.scene } }
Experience模块代码
// Experience/ExperienceSceneProvider.swift import SwiftUI import EntryPoint // 实现EntryPoint定义的协议,提供具体的Scene public struct ExperienceSceneProvider: AppSceneProvider { public var scene: some Scene { WindowGroup { ContentView() } } public init() {} } // Experience/ContentView.swift public struct ContentView: View { public var body: some View { Text("Experience Content") } public init() {} }
AppTarget模块代码
// AppTarget/App.swift import EntryPoint import Experience @main struct MyApp: App { var body: some Scene { // 将Experience的协议实现注入到EntryApp中 EntryApp(sceneProvider: ExperienceSceneProvider()) } }
二、解决EntryPoint的AppDelegate调用Experience方法的问题
同样用协议抽象+依赖注入的方式,让EntryPoint的AppDelegate依赖抽象接口,由Experience实现具体逻辑,再通过AppTarget注入。
实现步骤:
- 在EntryPoint中定义处理启动逻辑的抽象协议
- 在Experience中实现该协议,编写具体的启动操作
- 在AppTarget中初始化EntryPoint的AppDelegate时,注入Experience的协议实现
代码示例:
EntryPoint模块代码
// EntryPoint/EntryAppDelegate.swift import UIKit // 定义处理启动逻辑的抽象协议 public protocol ExperienceLaunchHandler { func handleLaunch(options: [UIApplication.LaunchOptionsKey: Any]?) } public class EntryAppDelegate: UIResponder, UIApplicationDelegate { // 存储协议实现的引用,可选类型避免强依赖 public var launchHandler: ExperienceLaunchHandler? public func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // 如果有注入的实现,调用对应方法 launchHandler?.handleLaunch(options: launchOptions) return true } }
Experience模块代码
// Experience/ExperienceLaunchHandlerImpl.swift import UIKit import EntryPoint // 实现启动处理协议,编写具体操作逻辑 public class ExperienceLaunchHandlerImpl: ExperienceLaunchHandler { public func handleLaunch(options: [UIApplication.LaunchOptionsKey: Any]?) { // 这里添加需要在启动时执行的操作,比如初始化配置、加载资源等 print("Experience launch operations executed") } public init() {} }
AppTarget模块代码
// AppTarget/App.swift import EntryPoint import Experience @main struct MyApp: App { // 使用UIApplicationDelegateAdaptor关联EntryAppDelegate @UIApplicationDelegateAdaptor(EntryAppDelegate.self) var appDelegate init() { // 将Experience的启动处理实现注入到EntryAppDelegate中 appDelegate.launchHandler = ExperienceLaunchHandlerImpl() } var body: some Scene { EntryApp(sceneProvider: ExperienceSceneProvider()) } }
核心原理说明
这种方案通过依赖倒置打破了模块间的直接依赖:
- EntryPoint仅依赖抽象协议,无需导入高层级的Experience模块
- Experience实现EntryPoint定义的协议,形成依赖关系的反转
- AppTarget作为最高层级,负责整合所有模块的实现,完成依赖注入
内容的提问来源于stack exchange,提问作者NightFuryLxD
相关产品推荐
相关产品推荐

