☰
swiftui-ui-patterns - app-wiring
2026/10/10 6:00:47 网站建设 项目流程

应用装配与依赖图

意图

展示如何装配应用外壳(TabView + NavigationStack + 弹窗),并在一个地方安装全局依赖图(环境对象、服务、流式客户端、SwiftData ModelContainer)。

推荐结构

  1. 根视图设置标签页、各标签页的路由器和弹窗。
  2. 一个专门的视图修饰器安装全局依赖和生命周期任务(认证状态、流式监听器、推送令牌、数据容器)。
  3. 功能视图只从环境中获取它们需要的内容;功能特有的状态保持局部化。

依赖选择

  • 对应用级服务、共享客户端、主题/配置,以及许多后代确实需要的值,使用@Environment。
  • 对功能局部的依赖和模型,优先使用初始化器注入。不要仅仅为了避免传一两个参数就把依赖放进环境。
  • 保持可变的特性状态不出现在环境中,除非它是有意跨应用大部分区域共享的。
  • 仅在作为旧代码回退方案,或项目已经针对真正共享的对象标准化使用它时,才使用@EnvironmentObject。

根外壳示例(通用)

@MainActorstructAppView:View{@StateprivatevarselectedTab:AppTab=.home@StateprivatevartabRouter=TabRouter()varbody:someView{TabView(selection:$selectedTab){ForEach(AppTab.allCases){tabinletrouter=tabRouter.router(for:tab)NavigationStack(path:tabRouter.binding(for:tab)){tab.makeContentView()}.withSheetDestinations(sheet:Binding(get:{router.presentedSheet},set:{router.presentedSheet=$0})).environment(router).tabItem{tab.label}.tag(tab)}}.withAppDependencyGraph()}}

最小AppTab示例:

@MainActorenumAppTab:Identifiable,Hashable,CaseIterable{casehome,notifications,settingsvarid:String{String(describing:self)}@ViewBuilderfuncmakeContentView()->someView{switchself{case.home:HomeView()case.notifications:NotificationsView()case.settings:SettingsView()}}@ViewBuildervarlabel:someView{switchself{case.home:Label("Home",systemImage:"house")case.notifications:Label("Notifications",systemImage:"bell")case.settings:Label("Settings",systemImage:"gear")}}}

路由骨架:

@MainActor@ObservablefinalclassRouterPath{varpath:[Route]=[]varpresentedSheet:SheetDestination?}enumRoute:Hashable{casedetail(id:String)}

依赖图修饰器(通用)

使用一个修饰器来安装环境对象,并在活动账户/客户端变化时处理生命周期钩子。这样能保持装配一致,避免在调用点忘记某个依赖。

extensionView{funcwithAppDependencyGraph(accountManager:AccountManager=.shared,currentAccount:CurrentAccount=.shared,currentInstance:CurrentInstance=.shared,userPreferences:UserPreferences=.shared,theme:Theme=.shared,watcher:StreamWatcher=.shared,pushNotifications:PushNotificationsService=.shared,intentService:AppIntentService=.shared,quickLook:QuickLook=.shared,toastCenter:ToastCenter=.shared,namespace:Namespace.ID?=nil,isSupporter:Bool=false)->someView{environment(accountManager).environment(accountManager.currentClient).environment(quickLook).environment(currentAccount).environment(currentInstance).environment(userPreferences).environment(theme).environment(watcher).environment(pushNotifications).environment(intentService).environment(toastCenter).environment(\.isSupporter,isSupporter).task(id:accountManager.currentClient.id){letclient=accountManager.currentClientifletnamespace{quickLook.namespace=namespace}currentAccount.setClient(client:client)currentInstance.setClient(client:client)userPreferences.setClient(client:client)awaitcurrentInstance.fetchCurrentInstance()watcher.setClient(client:client,instanceStreamingURL:currentInstance.instance?.streamingURL)ifclient.isAuth{watcher.watch(streams:[.user,.direct])}else{watcher.stopWatching()}}.task(id:accountManager.pushAccounts.map(\.token)){pushNotifications.tokens=accountManager.pushAccounts.map(\.token)}}}

备注:

  • .task(id:)钩子响应账户/客户端变化,重新初始化服务和监听器状态。
  • 保持该修饰器专注于全局装配;功能特有状态保持在功能内部。
  • 将类型(AccountManager、StreamWatcher 等)调整为与你项目匹配。

SwiftData / ModelContainer

在根处安装你的ModelContainer,使所有功能视图共享同一个 store。将模型列表保持最小化,只包含需要持久化的模型。

extensionView{funcwithModelContainer()->someView{modelContainer(for:[Draft.self,LocalTimeline.self,TagGroup.self])}}

原因:单一容器避免了每个弹窗或标签页重复创建 store,并保持数据一致。

弹窗路由(枚举驱动)

用一个小的枚举和辅助修饰器集中管理弹窗。

enumSheetDestination:Identifiable{casecomposercasesettingsvarid:String{String(describing:self)}}extensionView{funcwithSheetDestinations(sheet:Binding<SheetDestination?>)->someView{sheet(item:sheet){destinationinswitchdestination{case.composer:ComposerView().withEnvironments()case.settings:SettingsView().withEnvironments()}}}}

原因:枚举驱动的弹窗使展示集中化且可测试;新增一个弹窗只需增加一个枚举 case 和一个 switch 分支。

何时使用

  • 拥有多个共享环境对象和服务的包/模块的应用。
  • 需要响应账户/客户端变化并安全地重接线流式/推送的应用。
  • 任何希望在不重复环境设置的情况下,保持一致 TabView + NavigationStack + 弹窗装配的应用。

注意事项

  • 保持依赖修饰器精简;不要把功能状态或重量级逻辑放进去。
  • 确保.task(id:)的工作是轻量的或被适当取消;长时间运行的工作应放在服务中。
  • 如果存在未认证客户端,对流式/监听调用进行门控,以避免重连风暴。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询