参与学生移动应用大赛有感

目录
- 近期推荐
- SwiftPM:支持同一 Package 多主版本共存 (Allowing Multiple Major Versions of a Package)
- Server-Sent Events in Vapor
- SwiftData 避坑:保存失败后的回滚陷阱 (SwiftData rollback after a failed save)
- 用 Swift 6 打造超大 PSB 极速解码器 (Why I stopped letting Apple’s ImageIO open huge PSDs: a fast native PSB reader in Swift 6)
- SwiftUI 动画底层揭秘:它与 UIKit 的本质区别与跨平台差异 (How SwiftUI Animates, and Where It Is Not Like UIKit)
- 理解 layoutPriority 在 SwiftUI 容器中的布局机制 (The role of layout priority in SwiftUI containers)
- AI 时代的技术栈抉择:为何我依然坚持 Swift 与 Kotlin (Skip, Day, and Rust: Why I’d Still Choose Swift and Kotlin)
- AI 助手悖论:苹果建好了平台,开发者会买账吗? (The AI Assistant Paradox: Apple Can Build the Platform, but Can It Convince Developers?)
- 工具
在过去的三天中,我有幸作为评委参与了由苹果长期支持的 2026 年度「中国高校计算机大赛——移动应用创新赛」。这项赛事已经走过了 11 年,本届参赛高校超过 1000 所,参与学生达数万人。
尽管参与这次活动让我错过了家乡球队在主场捧起联赛冠军奖杯的历史时刻,但通过与大量青年选手的面对面交流,我依然深感不虚此行,收获颇丰。
AI 的普及与能力的飞跃,不仅大幅提升了参赛作品在视觉和功能上的完成度,也正在改变参赛者的构成。根据我在现场的观察,本次参赛队伍中至少有三分之一到一半的选手是女性。作为长期关注开发者生态的周报作者,我深知这一比例明显高于传统技术社区中女性开发者的占比。更耐人寻味的是,拥有计算机专业背景的选手似乎已不再占据绝对主导,不少队伍完全由此前没有开发经验的学生组成,其中又以设计专业居多。当越来越多没有编程背景的人也能独立完成应用开发,「Coder」这个身份的边界,或许正在以超乎想象的速度变得模糊。
然而,AI 在抹平技术门槛的同时,也暴露出不少值得警惕的问题。由于实现成本被大幅压缩,有些团队在开发过程中逐渐失去了克制,不断堆砌看似有用、实则缺乏辨识度的平庸功能。还有部分团队过度依赖 AI 代替真实的调研与访谈,导致目标人群的痛点与解决方案惊人地趋同。这种由 AI 拼凑出的「刻板需求」,在面向弱势群体的作品中尤为明显。此外,由于缺乏扎实的工程基础,一些团队即便拥有绝佳的创意,也很难进一步深化实现、构筑技术壁垒,最终遗憾地止步于「参赛 Demo」,难以成长为真正服务现实人群的成熟产品。
本届大赛还新增了中小学组。其中一些小学生的作品带给了我更多惊喜与触动。一方面,他们的创作目标极其纯粹,灵感可能仅仅来自同学随口的一句话,或一个天马行空的奇思妙想,让人真切感受到唯有在这个年龄段才会迸发出的灵气。另一方面,不少孩子仍然坚持在 Swift Playgrounds 中一行行手写代码。尽管作品中不乏过时的语法、混乱的文件组织,以及各种不成熟的实现,但相比部分过度依赖 AI 包装出的精致成果,这种不断尝试、犯错和修正的探索过程,或许能给他们带来更扎实的成长,也会成为日后最珍贵的记忆。
学生阶段,或许是每个开发者一生中能够做出最不功利作品的黄金窗口。多一些有感而发,去关注并解决身边那些真实可见的小事与痛点。毕竟走进社会之后,现实中的各种束缚与考量,可能再难让我们拥有如此随心、纯粹而真挚的心境,去创作一款产品了。
无论如何,参赛选手们展现出的朝气与迸发的灵感,都让我受益匪浅。更重要的是,这次经历让我真切地感受到,一个人人都能参与软件创作的时代,正在到来。
近期推荐
SwiftPM:支持同一 Package 多主版本共存 (Allowing Multiple Major Versions of a Package)
SwiftPM 长期存在一项让开发者颇为头疼的限制:在同一个依赖图中,如果 Package A 和 B 都依赖 Package C,那么 SwiftPM 必须为 C 选择一个同时满足 A 和 B 版本约束的版本。一旦版本要求无法兼容,开发者就不得不通过限制依赖版本、协调上游升级,甚至 Fork 并重命名仓库来解决冲突。Bri Peticca 提出了一项改进方案:允许同一 Package 的不同主版本在依赖图中共存。提案计划调整依赖解析算法,并通过自动生成 Module Alias 等方式避免模块命名冲突。
目前提案仍处于 Pitch 阶段,关于版本隔离、二进制体积、运行时行为等问题还有不少值得讨论的空间。如果你曾在项目中遇到类似的依赖冲突,或者对多版本共存有自己的看法,不妨前往 Swift Forums 参与讨论。
Server-Sent Events in Vapor
Server-Sent Events(SSE)是一种基于 HTTP 的服务器向客户端持续推送事件的技术。它的核心特点是:客户端发起一次 HTTP 请求,服务器保持响应连接,并在有新数据时持续发送消息。对于只需要单向传输数据的场景,SSE 往往比 WebSocket 更加简单直接。
Kyle Browning 以 Vapor 为例,展示了如何在通用 Streaming API 之上构建轻量级的 SSE 协议封装,并分享了他开发的开源库 vapor-sse。通过 req.sse,开发者可以直接在异步闭包中发送字符串或 Encodable 数据,无须手动处理响应头、事件格式和底层流的管理。整个库只有约 200 行代码,却涵盖了 SSE 协议封装中的关键细节。
SwiftData 避坑:保存失败后的回滚陷阱 (SwiftData rollback after a failed save)
当 SwiftData 的 ModelContext 执行 save() 操作抛出错误时,尚未成功保存的修改并不会自动消失,而是可能继续保留在 Context 中。如果错误来自待保存的数据本身,这些问题数据可能导致后续不相关的修改也无法成功保存;如果错误只是暂时性的,之前失败的修改则可能在后续保存时被意外提交。对于启用了自动保存功能、多个页面共享同一个 ModelContext 的 SwiftData 应用,这尤其容易带来难以察觉的数据一致性问题。
Roland Leth 发现,rollback() 也并非在所有场景下都能按预期工作。在他的故障注入测试中,直接回滚尚未处理的删除操作,会导致恢复后的对象在读取时崩溃;而在回滚前调用 processPendingChanges() 则避免了这一问题。文章还介绍了一种很有启发性的故障注入方法:通过 @TaskLocal 在测试中主动触发保存失败,验证应用的恢复逻辑,同时避免并行测试之间相互干扰。
相比 Core Data,SwiftData 在异常发生时提供的错误信息和处理手段仍然有限。我个人习惯将持久化操作封装在
ModelActor中,并为每组操作创建独立的 actor 实例,以事务的概念在私有ModelContext中集中完成一系列修改,最后统一保存。这样既能明确操作边界,也能避免失败的修改残留在长期共享的 Context 中,从而简化回滚与错误恢复的处理。
用 Swift 6 打造超大 PSB 极速解码器 (Why I stopped letting Apple’s ImageIO open huge PSDs: a fast native PSB reader in Swift 6)
面对一张 69,536 × 22,230 像素、文件大小达到 4.6 GB 的图像,Apple 的 ImageIO 需要 7.4 秒才能生成适合屏幕显示的缩略图,而 Joachim Rütter 设计的 Swift 解码器只需要 0.73 秒。性能差异的关键在于对 PSD/PSB 文件结构的针对性利用:文件包含多个长度明确的区段,而用于预览的合成图像(Flattened Composite)位于文件末尾。
Joachim 分享了他如何利用各区段的长度信息跳过图层数据,通过 RLE 行索引直接读取所需区域,并在读取过程中并行完成降采样,避免将完整图像载入内存。尽管思路看似简单,但实现涉及 Swift 6、UnsafeBufferPointer、pread、并行计算、内存控制和二进制格式解析,是一个很有特色且针对性极强的工程实践案例。
SwiftUI 动画底层揭秘:它与 UIKit 的本质区别与跨平台差异 (How SwiftUI Animates, and Where It Is Not Like UIKit)
在第 154 期周报中,我曾介绍过一场围绕 SwiftUI 动画是否足够「原生」的讨论。SwiftUI 共同创造者 Kyle Macomber 当时解释,SwiftUI 有意选择在应用进程中推进动画,而非像传统 Core Animation 那样将动画交给 Render Server 独立执行。不过,Client-side Animation 是否意味着动画一定会受到主线程阻塞的影响?
Mihaela Mihaljević Jakić 通过 Hook Layer 方法、分析线程调用栈、检查 Core Animation 私有对象与共享内存,并结合模拟器和真实设备录屏,对这一问题进行了深入验证。结果发现,在测试的 macOS 环境中,SwiftUI 动画确实由主线程持续推进,主线程阻塞会导致动画停顿;但在 iOS/iPadOS 上,SwiftUI 使用独立的 AsyncRenderer 线程计算动画值,即使主线程被阻塞,动画仍能继续。而当作者主动阻塞 AsyncRenderer 线程时,SwiftUI 动画也随之停顿。
相比实验结论本身,Mihaela 探索问题的方式或许更值得借鉴。从发现测量工具干扰了观察结果,到不断修正实验方法、交叉验证数据,再到明确列出尚未解决的问题,整篇文章展示了一次严谨而坦诚的技术探索过程。
理解 layoutPriority 在 SwiftUI 容器中的布局机制 (The role of layout priority in SwiftUI containers)
顾名思义,SwiftUI 中的 layoutPriority 用来标注某个视图在布局容器中的优先级,但如何使用这一信息取决于父容器的布局策略,并没有一套适用于所有容器的统一规则。
Natalia Panferova 展示了 layoutPriority 在不同布局容器中的作用:HStack 和 VStack 根据优先级分配可用空间;ZStack 则利用最高优先级的子视图决定自身尺寸。文章还介绍了如何在自定义 Layout 中读取优先级信息,并据此实现自己的布局策略。
在 SwiftUI 中,这种自身只负责提供信息、具体行为由上层容器决定的 Modifier 并不少见,而且是一种重要的设计范式。理解这一点,不仅有助于开发者更深入地掌握 API,也能帮助我们更好地设计出符合 SwiftUI 风格的自定义容器。
AI 时代的技术栈抉择:为何我依然坚持 Swift 与 Kotlin (Skip, Day, and Rust: Why I’d Still Choose Swift and Kotlin)
几天前,Skip 的联合创始人、前 Swift Android 工作组成员 Marc Prud’hommeaux 宣布将工作重心转向 Rust。他认为,当 AI 能够承担更多代码编写工作时,语言对人类开发者的友好程度不再那么重要,可移植性反而值得获得更高的优先级。
Junda Ong 却从相同的前提出发,得出了不同的结论:既然 AI 降低了编写和维护代码的成本,为什么不直接使用 Swift/SwiftUI 和 Kotlin/Compose,分别构建原生应用?对于简单的业务逻辑,维护两份实现并通过共享测试用例验证一致性,可能比引入额外的跨平台技术栈更划算。
Shopify 在不久前宣布将移动应用开发战略从 React Native 转回原生 Swift 和 Kotlin,理由同样是 AI 降低了维护两套原生代码的成本。有趣的是,AI 一方面让 Rust 这样的跨平台共享方案更容易实现,另一方面也让开发者不再需要为了减少重复代码而牺牲原生开发的优势。技术路线并没有因为 AI 而趋于统一,反而可能变得更加多样。
AI 助手悖论:苹果建好了平台,开发者会买账吗? (The AI Assistant Paradox: Apple Can Build the Platform, but Can It Convince Developers?)
在刚刚发布的 iOS 27.3 beta 中,苹果向第三方开发者开放了 Siri AI 的集成能力,并引入 Skills 机制,让开发者能够通过 App Intents 组合多个操作,帮助 Siri AI 完成更复杂的任务。再加上苹果拥有操作系统、身份认证、应用权限和安全支付等基础设施,似乎已经具备了构建个人 AI Assistant 生态的诸多条件。但技术上的准备就绪,是否就意味着开发者愿意主动开放自己的核心业务能力?
Erk Ekin 认为,问题不仅在于适配和维护 App Intents 需要额外投入,更在于 AI Assistant 可能改变企业与用户之间的关系。当用户可以绕过应用界面,直接让 Agent 完成购物、预订等操作时,企业也可能失去推荐商品、推广订阅和建立用户关系的机会。相比之下,OpenClaw 等独立 Agent 可以通过界面自动化直接操作现有服务,无须等待开发者主动适配,但也面临更复杂的安全和授权问题。
本文没有简单地认为 Apple 的路线必然失败,或者独立 Agent 一定胜出。最终,两条路线可能走向相似的架构:通过多种方式访问服务,同时依靠可信的基础设施约束 Agent 的操作权限。
工具
MockMini:用极简 C 与 Swift 实现的网络代理核心
由 yezicheng 开发的 MockMini 是一个免费开源、仍处于早期阶段的实验性网络代理核心。项目以约 2600 行 C 和 Swift 代码,实现用户态 TCP/UDP 数据包处理及多种代理协议支持。其中,C 层负责 TCP 连接状态管理、数据重组与重传,Swift 层实现 Shadowsocks、VMess、Trojan、VLESS 等协议,并借助苹果平台的 Network.framework 与 CryptoKit 完成网络连接及部分加密操作。
相比功能完整的成熟代理核心,MockMini 更值得关注的是其精简的实现规模与清晰的职责划分。项目将协议处理、传输封装和连接管理分层组织,让开发者能够在相对集中的代码范围内追踪数据流转,了解网络代理的内部机制。