SwiftUI 七年之后,为什么它仍像一场没有结束的公测?

2026-08-03 40 预计阅读时间: 1 分钟
来源: oschina.net AI 摘要 Original link

Disclaimer: This article is an AI-assisted summary. Read it together with the original source when precision matters. The summary may omit context, version differences, or edge cases and is not official documentation.

预计阅读时间:11 分钟

SwiftUI 发布已经七年,但开发者对它的抱怨并没有随着时间消失。Yakov Manshin 在一段约 40 分钟的视频中,把 SwiftUI 形容为“一个平庸的故事”,核心判断是:它至今仍然像一个永久公测版。

这并不意味着 SwiftUI 不能用于生产。真正的问题在于,苹果希望开发者相信它代表了未来的界面开发方式,但开发者在数据流、布局、API 稳定性、性能和跨平台能力上,仍然经常需要绕路、试错,甚至回到 UIKit 或 AppKit。

争议不在于声明式语法

SwiftUI 最容易让人喜欢的地方,是它把界面描述成状态的函数。按钮修改状态,状态变化驱动视图刷新,开发者不必手动查找控件、更新文本或维护大量事件回调。

一个最小例子可以这样写:

import SwiftUI

struct CounterView: View {
    @State private var count = 0

    var body: some View {
        VStack(spacing: 16) {
            Text("Count: \\(count)")
                .font(.title)

            Button("Increase") {
                count += 1
            }
            .buttonStyle(.borderedProminent)
        }
        .padding()
    }
}

#Preview {
    CounterView()
}

这段代码的意图清晰:count 是本地状态,body 根据它生成界面。对于简单页面,这种模型比手工同步视图更容易阅读。

但真实应用通常有多个数据源:网络请求、缓存、用户输入、导航状态、系统生命周期,以及不同平台上的环境值。此时,问题不再是“如何声明一个按钮”,而是“哪个对象拥有状态、谁负责更新它、视图何时会重新计算,以及更新是否会产生意外副作用”。

声明式语法降低了样板代码,却没有消除状态管理的复杂性。状态边界设计不清时,SwiftUI 的问题会从显式的回调错误,变成更隐蔽的刷新时机和依赖关系问题。

五个长期没有完全收敛的维度

1. 数据流:简单示例和大型应用之间有距离

@State@Binding、环境依赖和可观察对象让数据可以在视图树中流动,但项目规模扩大后,数据的所有权和生命周期仍然需要开发者自己建立规则。

一个视图可能同时依赖本地状态、共享模型和异步任务。只要其中一层的生命周期判断不准确,就可能出现重复请求、状态被覆盖、视图重新创建后数据丢失等问题。SwiftUI 提供了机制,却没有替团队决定架构。

可以实践的原则是:本地交互状态留在视图附近;跨页面共享的数据交给明确的模型;异步操作集中管理;视图尽量只负责展示状态和发送用户意图。

2. 布局系统:组合很优雅,边界情况很难猜

SwiftUI 的布局看起来像积木:VStackHStackZStackGrid 和各种修饰器可以快速组合出界面。但当需求涉及动态字体、长文本、可滚动区域、键盘、尺寸变化或平台差异时,布局规则会变得不直观。

开发者经常需要在 framefixedSizelayoutPriorityGeometryReader 和自定义布局之间反复试验。一个修饰器调整了尺寸,可能又影响父容器的提议尺寸,最终表现为截断、空白、跳动或不同设备上的不一致。

这类问题尤其考验调试能力,因为代码仍然很短,错误却隐藏在布局计算过程里。

3. API 稳定性:新旧方案并存增加了迁移成本

SwiftUI 伴随系统版本持续演进。新的属性包装器、导航方式、观察模型和容器 API 不断加入,旧 API 并不会立刻消失,项目因此经常同时存在多套实现。

这给库作者和需要支持多个系统版本的应用带来实际负担:同一个功能可能要写多个分支;教程、示例和生产代码之间可能使用不同风格;开发者还要判断某个 API 是暂时不完善,还是已经可以长期依赖。

API 数量增加本身不是问题。问题在于,当核心能力仍在变化时,团队很难形成稳定、可复用的工程约定。

4. 性能:视图代码短,不等于运行成本低

SwiftUI 会根据状态变化重新计算视图描述,但“重新计算”不等于“最终一定重绘”,具体开销取决于依赖范围、视图树规模、列表数据、任务触发方式以及自定义布局。

因此不能只看 body 代码是否简短来判断性能。长列表、复杂动画、频繁状态更新和大规模嵌套视图都应当用 Instruments、日志和实际设备测试验证。必要时,可以把高频变化拆成更小的视图,让状态依赖范围更明确。

可以用下面的命令创建一个最小 SwiftUI 项目来做实验。需要 macOS 和 Xcode,PRODUCT_BUNDLE_IDENTIFIER 请替换成自己的标识符:

mkdir SwiftUIDiagnostics
cd SwiftUIDiagnostics
swift package init --type executable
swift package generate-xcodeproj
open SwiftUIDiagnostics.xcodeproj

这是一个实验起点,不是完整的生产项目模板。为了比较不同布局或状态更新方式,建议一次只改一个变量,然后在模拟器和真机上观察启动时间、滚动流畅度和内存变化。

5. 跨平台:共享代码不等于共享体验

SwiftUI 的吸引力之一,是希望用同一套声明式 UI 技术覆盖 iPhone、iPad、Mac、Apple Watch 和 Apple TV。但不同平台的输入方式、窗口模型、导航习惯、控件语义和系统能力并不相同。

因此,“一套代码到处运行”与“每个平台都符合用户习惯”之间存在明显差距。共享模型、业务逻辑和部分视图结构通常很有价值;强行共享所有交互细节,则可能让界面同时失去平台特征和可维护性。

苹果官方示例也说明不了全部问题

官方教程和演示项目适合介绍 API 的基本用法,但它们通常拥有有限的数据量、简单的导航和可控的布局环境。真实应用还要处理网络失败、离线缓存、权限、深链接、动态字体、无障碍、系统版本差异以及已有 UIKit 代码。

这也是开发者感到落差的原因:教程里的代码可以在几分钟内搭出页面,生产项目却需要大量边界处理。示例越漂亮,越容易让人低估这些工程成本。

判断 SwiftUI 是否适合一个项目时,不能只问“能不能画出这个页面”,还要问:

  • 目标系统版本是否允许使用当前推荐 API?
  • 团队是否能接受 API 演进带来的迁移工作?
  • 页面是否包含复杂布局、重度动画或大量高频数据?
  • 是否需要与 UIKit、AppKit 或已有组件库长期共存?
  • 跨平台共享的是业务逻辑,还是连交互细节也要完全共享?

更现实的采用方式

SwiftUI 更适合被当作一项持续演进的工具,而不是一次性替换所有旧 UI 技术的承诺。

新项目可以优先用 SwiftUI 验证页面结构和交互;复杂组件可以保留 UIKit 或 AppKit 实现,再通过桥接嵌入 SwiftUI;对性能敏感的列表、编辑器和自定义绘制区域,应尽早用真实数据测试;团队还需要固定最低系统版本和 API 使用边界,避免每个人根据最新教程选择不同方案。

一个务实的检查清单是:

  • 用真实数据验证布局,而不是只用预览数据。
  • 在最老的支持系统和真实设备上测试。
  • 为异步任务、状态所有权和副作用建立明确约定。
  • 对长列表、动画和复杂视图树做性能基准。
  • 允许 SwiftUI 与 UIKit、AppKit 共存,不把技术选型变成意识形态问题。

SwiftUI 七年后仍然受到批评,恰恰说明开发者期待它成为成熟、稳定、跨平台的主力框架。现阶段更准确的结论不是“SwiftUI 不能用”,而是:它已经足够有用,却还没有稳定到可以让团队停止关注底层行为和平台差异。


相关推荐