You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SwiftUI如何避免导航硬编码?大型生产级应用架构问询

SwiftUI生产级应用中可复用导航视图的架构困境

我正在为一款大型、可投入生产的SwiftUI应用设计架构,却反复卡在同一个问题上——这暴露出SwiftUI的一处重大设计缺陷。至今仍没人能给出一套完整可用、适用于生产环境的解决方案:如何在SwiftUI中实现包含导航功能的可复用视图?

由于SwiftUI的NavigationLink与视图强绑定,这种方式根本无法在大型应用中规模化落地。NavigationLink在小型示例里用着没问题,但一旦想在单个应用里复用多个视图,甚至跨模块(比如iOS、WatchOS等平台复用视图),麻烦就来了。

设计痛点很明确:NavigationLink被硬编码到视图中,比如写死NavigationLink(destination: MyCustomView(item: item))。但如果包含这个NavigationLink的视图需要复用,就不能硬编码目标视图,必须有一套机制来动态提供目标视图。

我之前就这个问题提问过,得到了一个看似可行的方向——通过注入目标链接到可复用视图中(参考SwiftUI MVVM Coordinator/Router/NavigationLink的思路)。这个想法大体能走通,但遗憾的是没法扩展到实际生产应用里。当我有多个可复用页面时,逻辑会陷入死循环:比如可复用视图ViewA需要预配置好目标视图ViewB,可ViewB又需要预配置好目标视图ViewC,那我得先给ViewB注入ViewC,再把ViewB注入ViewA,以此类推。但这时候需要传递的数据还没准备好,整个结构直接失效。

我还试过用Environment作为依赖注入机制来注入NavigationLink的目标视图,但这更像是临时凑活的权宜之计,根本不是适合大型应用的可扩展方案。到最后我们会把Environment用在所有场景,而且Environment只能在视图内部使用(没法在独立的Coordinator或ViewModel里用),这会导致结构混乱——毕竟业务逻辑(比如ViewModel代码)要和视图分离,导航也得和视图分离(比如用Coordinator模式)。

在UIKit里这事儿是能解决的,因为我们能访问视图背后的UIViewController和UINavigationController。UIKit的MVC模式本来就有概念混杂的问题,甚至被戏称为“Massive-View-Controller(巨型视图控制器)”而非正经的“Model-View-Controller”。现在类似的问题在SwiftUI里不仅延续了,在我看来还更严重:导航与视图强耦合,完全没法解耦。所以带导航功能的视图根本没法复用。UIKit里能解决的问题,在SwiftUI里我找不到合理的方案。遗憾的是苹果从来没给出过这类架构问题的官方解决方案,只给了一些小型示例应用。我真心希望有人能证明我错了,展示一套适用于大型生产级应用的清晰设计模式。

后续更新

2020年6月(赏金到期前)

赏金快到期了,遗憾的是还是没人能给出可行的示例。如果找不到其他解决方案,我会发起新的赏金并附上链接。感谢所有人的贡献!

2020年6月18日(苹果官方回复)

我收到了苹果关于此问题的回复,他们提出了一套解耦视图与模型的方案:

enum Destination {
    case viewA
    case viewB
    case viewC
}

struct Thing: Identifiable {
    var title: String
    var destination: Destination
    // … 省略其他内容 …
}

struct ContentView {
    var things: [Thing]
    var body: some View {
        List(things) {
            NavigationLink($0.title, destination: destination(for: $0))
        }
    }

    @ViewBuilder func destination(for thing: Thing) -> some View {
        switch thing.destination {
        case .viewA: return ViewA(thing)
        case .viewB: return ViewB(thing)
        case .viewC: return ViewC(thing)
        }
    }
}

我的回复是:

感谢反馈。但如你所见,视图中仍存在强耦合问题。现在ContentView需要知晓所有可导航到的视图(ViewA、ViewB、ViewC)。正如我所说,这在小型示例应用中可行,但无法扩展到大型生产级应用。想象一下,我在GitHub的一个项目中创建了一个自定义视图,然后将其导入我的应用中。这个自定义视图对其可导航到的其他视图一无所知,因为这些视图是我的应用所特有的。我希望我把问题解释得更清楚了。我认为解决此问题的唯一清晰方案是像UIKit那样分离导航与视图(例如使用UINavigationController)。谢谢,Darko

所以这个问题依然没有清晰可行的解决方案,只能期待WWDC 2020能带来转机。

2021年9月更新

使用AnyView并不是解决这个问题的通用好方案。在大型应用里,基本上所有视图都得设计成可复用的,这意味着AnyView会被到处用。我曾和两位苹果开发者交流过,他们明确告诉我AnyView的性能远不如原生View,只能在特殊情况下使用。根本原因是AnyView的类型没法在编译时解析,必须在堆上分配,这会带来额外的性能开销。

2022年6月更新(WWDC带来转机)

苹果在WWDC上推出了全新的SwiftUI NavigationStack。NavigationStack通过使用.navigationDestination修饰符,允许将目标视图与当前可见视图分离,终于实现了一套清晰的Coordinator模式。感谢苹果倾听用户的需求!

内容的提问来源于stack exchange,提问作者Darko

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.07 11:17:35