2月5-7日QCon全球软件开发大会携手100+位大咖讲师落定北京,点击查看完整日程>> 了解详情
写点什么

我庆幸果断放弃了 SwiftUI:它还不够成熟

  • 2022-09-04
    北京
  • 本文字数:3049 字

    阅读完需:约 10 分钟

我庆幸果断放弃了SwiftUI:它还不够成熟

SwiftUI 很好,但是苹果对它投资不足。


在 2019 年的 WWDC 大会上,苹果推出了一个全新的 SwiftUI 框架,这是一个现代化的 UI 界面编码结构,它是基于 Swift 从头开始构建的。新框架使用声明性范例,让开发者用更少的代码编写相同的 UI。


SwiftUI 的愿景是降低开发 iOS 门槛,吸引更多开发者、丰富 iOS 的业态。并且 SwiftUI 可以“实现一次编码,可适应五端 Apple 产品平台”, 包括 watchOS、tvOS、macOS 等,以此统一苹果平台的 UI 框架。


苹果传递出来的消息就像是说:“SwiftUI 是一个了不起的用户界面框架,而且 100% 绝对会成为苹果平台上应用开发的未来。”


这些年,也有一些用 SwiftUI 重写 UIKit 应用程序的案例,去年奈飞新版 iOS App 的登录界面也完全由 SwiftUI 重构。



本文的作者 chsxf,是一家独立游戏工作室的首席开发,也是 15 年的苹果用户,他想尝试将 SwiftUI 放到自己的项目中,但是最终失败了。他发表了一篇博客,总结了尝试并放弃 SwiftUI 的过程,这篇文章在 Hacker News 上引发了开发者们的大量讨论:


“恕我直言,SwiftUI 是一个很好的机会,但苹果公司对它投资不足。这是一项很好的技术,响应式方法非常适合许多典型的基于视图的需求,但对如何处理边缘情况,文档中非常缺乏相关的说明。”


“这是个好主意,但 SwiftUI 的主要问题是完全不成熟。”“它具有复杂的行为,不适用于需要大容量或复杂 UI 的 App。”


“而且 SwiftUI 改进太慢了。”......


chsxf 的博客原文翻译:


最近,我手头正好有个“The Untitled Project”(名字还没想好)项目需要完成。考虑到配套创作工具 CiderKit 在发展成熟的过程中也变得愈发复杂,再加上创建各种窗口和 UI 元素的实际需求,我决定尝试用用 SwiftUI。这是个宝贵的机会,能让我认真体验一把 SwiftUI 并探索其内部工作原理。


起初项目工作良好,我对 SwiftUI 的表现可以说非常满意,我甚至创建了自己的修改器,以便更轻松地显示警报消息。但美好的甜蜜期很快过去,接下来我就要说道说道 SwiftUI 的那些“坏毛病”了。


实时检查器不好用


接下来,我开始了 SwiftUI 探索之旅的第二站——为地图编辑器创建实时检查器。跟其他创作工具一样,这款检查器的功能就是选定一个对象,并把可检查的对应属性显示在一个临时的用户界面元素当中。过程当中,Swift 协议和它处理泛型的方式也给我带来了不少麻烦,但这里我们就不过多展开了。


我还遇到了其他问题,因为 SwiftUI 高度依赖于 View 协议的实现结构,但 View 协议又有关联的类型,所以只能把它当成约束来用。好在配合 some 关键字和 opaque 类型等设计,我最终还是为可选对象找到了一种实现方法,让每个对象都能提供自身特定的 UI 元素。



之所以下决心选择 SwiftUI,就是因为初步测试时效果不错。如上图所示,地图编辑器位于左侧,检查器位于右侧。起初,我测试了一个 UI 元素,那是个用于开灯和关灯的勾选框。它运行良好,所以我根本想象不到后续会出什么大乱子。


但在开始实现更复杂的检查器视图时,特别是涉及带有/不带步进器或颜色选择器的多个文本字段时,整个运行速度开始剧烈下降。SpriteKit 视图一般都能以每秒 60 帧的完美速率呈现(只要用的不是英特尔孱弱的 iGPU)。但每当 SwiftUI 更新检查器视图时(这种更新可能出现在移动过程中,甚至是在输入文本字段的时候),渲染速率都会下降到每秒 10 到 15 帧,而且相当不稳定。这显然让人无法容忍。


我认真做了一番分析,并发现了几个问题。首先,由可选对象提供的视图在每次重绘时都是在完全重新创建。我虽然通过缓存稍稍提升了性能表现,但实际体验仍然非常糟糕。事实证明,SwiftUI 检查器视图就是没法提供合理的重绘速度。我在网上查找了解决方案,最后编写了一个延迟版本的 ObservableObject,由它来强制每秒只发布一次更改(参见以下代码)。


import Combine

import Foundation


extension ObservableObject { func delayed(_ delay: TimeInterval = 1.0) -> DelayedObservableObject<Self> { return .init(object: self, delay: delay) } }


@dynamicMemberLookup

class DelayedObservableObject<Object>: ObservableObject where Object: ObservableObject {

private var original: Object

private var subscription: AnyCancellable?


fileprivate init(object: Object, delay: TimeInterval) {

self.original = object

subscription = object.objectWillChange

.throttle(for: RunLoop.SchedulerTimeType.Stride(delay), scheduler: RunLoop.main, latest: true)

.sink { [weak self] _ in self?.objectWillChange.send() }

}


subscript<Subject>(dynamicMember keyPath: WritableKeyPath<Object, Subject>) -> Subject {

get { original[keyPath: keyPath] }

set { original[keyPath: keyPath] = newValue }

}

}


随着重绘频率的降低,终于能比较顺畅地操作地图上的对象了,每秒的帧率浮动一般就只有个位数。但这会导致检查器中的值出现延迟,因此在地图编辑器的交互过程中(比如使用移动工具时)结果不准确,所以效果还是称不上完美。


但我觉得这可能只是个独立问题,并不能因此把 SwiftUI 一棒子打死。所以,我打算继续探索。


越来越慢


在实现了第一个检查器之后,我开始研究另一个主题:Sprite 资产编辑器。利用这款工具,我可以用多个 sprite 拼接成复杂的资产,再最终为它们制作动画。它的显示效果就是主窗口中的一张表,出于学习的目的,我当然还是想继续用 SwiftUI 喽。毕竟初次尝试肯定会有种种问题,应该再给它一次机会。



如大家所见,这是个复杂的窗口,包含多种不同上下文(上方的「Sprite 资产数据库」列表,左侧的特定「Sprite 资产数据库」内容,以及其他与选定 Sprite 资产对应的编辑器元素)。我需要为每个上下文创建一个视图,这些视图同时又是其他视图的「子视图」,然后把需要的数据传递给特定视图。


但上图展示的效果其实是在 AppKit 中完成的,因为我在 SwiftUI 一直实现不了预期的功能。大家应该注意到了,中间的 SpriteKit 视图上有三个按钮(分别是+、200%和-)。这些按钮只跟管理 SpriteKit 视图缩放的 @State 相关联。尽管几乎不涉及任何其他数据,在界面更新前单击这些按钮,也会产生将近一秒钟的巨大延迟。我刚开始以为是因为地图编辑器的 SpriteKit 主视图仍在后台渲染。所以我尝试在工作表显示出来后禁用渲染,但结果没有任何改变。


变更从一种环境传播至另一环境时,我也遇到了类似的延迟问题。这可以说是压死骆驼的最后一根稻草了,我决定放弃 SwiftUI,继续用 AppKit。


总结


其实没能在项目中用到 SwiftUI,会让我感觉有点遗憾。我仍然觉得它是一项很棒的技术,只是可能不适合我的这个特定用例。但我真的不确定是不是自己的用法有问题。我打算在 Nihongo no Kana 的更新版本中再用用 SwiftUI,毕竟那款 iOS/iPadOS 应用的重绘频率低得多,所以应该不会有太大问题。


也许 SwiftUI 还没做好全面替代 AppKit 的准备。The Untitled Project 的 CiderKit 创作工具并不是作为 Catalyst 应用构建的,也不依赖于 UIKit。但继续使用 AppKit 的最大优点,就是没有任何延迟而且一切功能完全符合预期。当然,整个构建过程更繁琐,而且自动布局功能也不怎么好用。但我至少可以更好地控制应用程序的行为,而且根据需求随意调整各种元素。


总之,经历了这么一番波折,我还是很庆幸自己果断放弃了 SwiftUI。这可能是我在这个项目上做过的最明智的选择。


参考链接:

https://chsxf.dev/2022/08/28/5-tup-why-i-quit-using-swiftui.html

https://news.ycombinator.com/item?id=32630389

https://xie.infoq.cn/article/28af907f31baa7e7283a31ed4

2022-09-04 12:006408

评论

发布
暂无评论
发现更多内容

Carina 全新版本 v0.11.0 上线!重磅升级不可错过

BoCloud博云

云计算 云原生 #开源

技术分享| 调度平台的好助手-快对讲

anyRTC开发者

音视频 调度 实时消息 快对讲 视频对讲

客随主便-redis 主从同步是怎样的过程?

知识浅谈

redis主从 9月月更

2022年中国证券行业智能投顾专题分析

易观分析

金融 证券

go语言逆向技术之---常量字符串解密

sofiya

python量化交易合约对冲系统开发

l8l259l3365

阿里云视觉智能开放平台9月特惠来袭

夏夜许游

“云上生长”网络研讨会|使用 GPU 和 VPU 等异构硬件加速视频转码

青云技术社区

gpu 音视频

pe文件签名信息检测技术

科技怪咖

区块链入局,远程医疗这回“靠谱”了!

旺链科技

区块链 产业区块链 远程医疗 企业号九月金秋榜

MobTech SMSSDK iOS端快速集成指南

MobTech袤博科技

ios sdk

骞云获得阿里云首批产品生态集成认证,携手阿里云共建新合作

阿里巴巴云原生

阿里云 云原生 加速器 伙伴

云原生(三十二) | Kubernetes篇之平台存储系统介绍

Lansonli

云原生 9月月更

新思科技解析Repo Jacking依赖仓库劫持如何影响供应链安全

InfoQ_434670063458

漏洞 新思科技 软件供应链 Repo Jacking

云原生游戏第 2 讲:OpenKruiseGame 设计理念详解

阿里巴巴云原生

阿里云 容器 云原生 游戏 OpenKruiseGame

Web漏洞靶场搭建(OWASP Benchmark)

sofiya

从检测角度思考美燃油管道商遭勒索攻击事件

科技怪咖

宿舍全屋智能开发指南

OpenHarmony开发者

OpenHarmony

iOS如何实现验证码登录丨MobTech

MobTech袤博科技

ios xcode

程序员“反内卷”大法——和无效加班说再见!

博文视点Broadview

pe文件签名信息检测技术

sofiya

二进制SCA检测工具---技术短板及应对措施

科技怪咖

漫谈软件成分分析(SCA)安全测试技术

科技怪咖

CVE-2022-0847 DirtyPipe漏洞分析

sofiya

go语言逆向技术之---恢复函数名称算法

科技怪咖

MobTech 短信验证REST API

MobTech袤博科技

REST API MobTech袤博科技

超大模型工程化实践打磨,百度智能云发布云原生 AI 2.0 方案

百度Geek说

云原生 人工智能’ 企业号九月金秋榜

java培训程序员靠技术来延长自己的职业周期

小谷哥

移动应用中的第三方SDK隐私合规检测

sofiya

【云原生 | Docker】部署 Django & Nginx & Gunicorn

计算机魔术师

8月月更

软件测试 | 接口自动化测试,如何实现多套环境的自动化测试?

测试人

软件测试 自动化测试 接口测试 测试开发

我庆幸果断放弃了SwiftUI:它还不够成熟_前端_核子可乐_InfoQ精选文章