Reflections on Judging a Student Mobile App Competition

Contents
- Recent Recommendations
- Allowing Multiple Major Versions of a Package
- Server-Sent Events in Vapor
- SwiftData rollback after a failed save
- Why I stopped letting Apple’s ImageIO open huge PSDs: a fast native PSB reader in Swift 6
- How SwiftUI Animates, and Where It Is Not Like UIKit
- The role of layout priority in SwiftUI containers
- Skip, Day, and Rust: Why I’d Still Choose Swift and Kotlin
- The AI Assistant Paradox: Apple Can Build the Platform, but Can It Convince Developers?
- Tool
Over the past three days, I had the privilege of serving as a judge at the 2026 China Collegiate Computing Competition — Mobile Application Innovation Contest, an event that Apple has supported for many years. Now in its 11th year, the competition has grown considerably, attracting participants from more than 1,000 universities and tens of thousands of students.
Although attending the event meant missing the historic moment when my hometown football team lifted the league championship trophy on home soil, the opportunity to speak face-to-face with so many young contestants made the trip more than worthwhile. I came away with a great deal to reflect on.
The widespread adoption of AI and its rapidly advancing capabilities have not only dramatically improved the visual polish and functional completeness of the entries but are also reshaping who participates in the competition. Based on my observations at the event, somewhere between one-third and one-half of the contestants were women. As someone who has followed the developer ecosystem closely for years, I know this is substantially higher than the proportion of female developers in traditional tech communities. Even more striking was that students with formal computer science backgrounds no longer seemed to dominate the competition. Many teams consisted entirely of students with no prior development experience, particularly those studying design. As more and more people without programming backgrounds become capable of building complete applications on their own, the boundaries of what it means to be a “coder” may be blurring faster than we ever imagined.
Yet while AI is lowering technical barriers, it is also exposing some troubling problems. With implementation costs dramatically reduced, some teams seem to have lost their sense of restraint, piling on mediocre features that appear useful but offer little that distinguishes their products. Others rely too heavily on AI in place of genuine user research and interviews, resulting in remarkably similar assumptions about their target users’ pain points and equally predictable solutions. These AI-generated “stereotyped needs” were especially apparent in projects aimed at vulnerable populations. Moreover, without a solid engineering foundation, some teams struggled to develop even their most promising ideas into something more substantial or build meaningful technical advantages. Their projects ultimately remained competition demos, never making the leap into mature products capable of serving people in the real world.
This year’s competition also introduced a new category for primary and secondary school students. Some of the elementary school projects were the ones that surprised and moved me the most. For one thing, their motivations were wonderfully pure. An idea might spring from an offhand remark by a classmate or a wildly imaginative thought, revealing a kind of creativity that seems to flourish only at that age. For another, quite a few children were still writing code line by line in Swift Playgrounds. Their projects might have contained outdated syntax, disorganized files, and immature implementations, but compared with some of the polished results produced through excessive reliance on AI, the process of experimenting, making mistakes, and figuring things out for themselves may give these children a much stronger foundation for growth—and memories they will treasure for years to come.
Our student years may be the golden window in a developer’s life when we have the greatest freedom to create without worrying about practical rewards. Perhaps students should follow their own curiosity a little more, paying attention to the small, tangible problems and frustrations they encounter in everyday life. After all, once we enter the working world, its many constraints and practical considerations may make it difficult to ever again approach building an app with quite the same freedom, sincerity, and unguarded enthusiasm.
Whatever the shortcomings I observed, I learned a great deal from the energy and creativity these young contestants brought to the competition. More importantly, the experience gave me a genuine sense that an era in which anyone can participate in creating software is beginning to take shape.
Recent Recommendations
Allowing Multiple Major Versions of a Package
SwiftPM has long had a limitation that causes considerable frustration for developers: if Package A and Package B both depend on Package C within the same dependency graph, SwiftPM must select a single version of C that satisfies the version constraints of both A and B. When those requirements are incompatible, developers have little choice but to restrict dependency versions, coordinate upstream upgrades, or even fork and rename repositories to resolve the conflict. Bri Peticca has proposed an improvement that would allow different major versions of the same package to coexist within a dependency graph. The proposal involves changes to the dependency resolution algorithm and mechanisms such as automatically generated module aliases to avoid naming conflicts.
The proposal is still at the Pitch stage, and questions surrounding version isolation, binary size, and runtime behavior remain open for discussion. If you’ve encountered similar dependency conflicts in your projects or have thoughts on supporting multiple versions of a package, consider joining the discussion on Swift Forums.
Server-Sent Events in Vapor
Server-Sent Events (SSE) is an HTTP-based technology that allows servers to continuously push events to clients. Its defining characteristic is straightforward: the client makes a single HTTP request, the server keeps the response connection open, and new messages are sent as data becomes available. For scenarios that only require one-way communication, SSE is often simpler and more straightforward than WebSocket.
Using Vapor as an example, Kyle Browning demonstrates how to build a lightweight SSE protocol wrapper on top of a general-purpose streaming API and introduces his open-source library, vapor-sse. Through req.sse, developers can send strings or Encodable data directly from an asynchronous closure without manually handling response headers, event formatting, or the underlying stream management. At approximately 200 lines of code, the library covers the essential details of implementing SSE.
SwiftData rollback after a failed save
When a SwiftData ModelContext throws an error during save(), changes that have not been successfully persisted do not automatically disappear. Instead, they may remain in the context. If the failure is caused by the pending data itself, those problematic changes may prevent subsequent, unrelated modifications from being saved. If the error is only temporary, previously failed changes might instead be committed unintentionally during a later save. This can lead to particularly subtle data consistency issues in SwiftData applications that enable autosave and share a single ModelContext across multiple screens.
Roland Leth found that rollback() does not always behave as expected. In his fault-injection tests, rolling back an unprocessed deletion caused a crash when accessing the restored object. Calling processPendingChanges() before the rollback avoided the issue. The article also introduces an insightful fault-injection technique: using @TaskLocal to deliberately trigger save failures in tests, allowing developers to verify their recovery logic while preventing interference between parallel tests.
Compared with Core Data, SwiftData still provides relatively limited error information and recovery mechanisms when something goes wrong. My personal approach is to encapsulate persistence operations in
ModelActor, creating a separate actor instance for each group of operations. Using a transaction-like model, I perform a series of changes within a privateModelContextand save them together at the end. This establishes clear operation boundaries and prevents failed changes from lingering in a long-lived shared context, simplifying rollback and error recovery.
Why I stopped letting Apple’s ImageIO open huge PSDs: a fast native PSB reader in Swift 6
Given an image measuring 69,536 × 22,230 pixels and weighing in at 4.6 GB, Apple’s ImageIO takes 7.4 seconds to generate a thumbnail suitable for on-screen display. A Swift decoder developed by Joachim Rütter, however, completes the same task in just 0.73 seconds. The key to this performance difference lies in taking advantage of the PSD/PSB file structure: the format consists of multiple sections with explicitly defined lengths, while the flattened composite image used for previews is located at the end of the file.
Joachim explains how he uses section lengths to skip layer data, accesses only the required regions through RLE row indexes, and performs downsampling in parallel as the data is read, avoiding the need to load the entire image into memory. Although the approach may sound straightforward, its implementation involves Swift 6, UnsafeBufferPointer, pread, parallel computation, memory management, and binary format parsing. It is a distinctive and highly specialized example of practical engineering.
How SwiftUI Animates, and Where It Is Not Like UIKit
In Issue #154, I covered a debate over whether SwiftUI animations are sufficiently “native.” Kyle Macomber, one of SwiftUI’s original creators, explained that SwiftUI deliberately chose to drive animations within the application process rather than handing them off to the Render Server for independent execution, as traditional Core Animation does. But does client-side animation necessarily mean that animations will be affected when the main thread is blocked?
Mihaela Mihaljević Jakić investigated this question in depth by hooking Layer methods, analyzing thread call stacks, examining private Core Animation objects and shared memory, and combining these findings with screen recordings from simulators and physical devices. The results revealed that, in the tested macOS environment, SwiftUI animations are indeed driven continuously on the main thread and stall when that thread is blocked. On iOS and iPadOS, however, SwiftUI uses a separate AsyncRenderer thread to calculate animation values, allowing animations to continue even when the main thread is blocked. When the author deliberately blocked the AsyncRenderer thread itself, the SwiftUI animation stalled as well.
Perhaps even more valuable than the experimental findings is Mihaela’s approach to investigating the problem. From discovering that her measurement tools were interfering with the results, to repeatedly refining her methodology, cross-checking the data, and explicitly documenting unanswered questions, the article offers a rigorous and candid account of technical exploration.
The role of layout priority in SwiftUI containers
As its name suggests, SwiftUI’s layoutPriority indicates a view’s priority within a layout container. However, how that information is interpreted depends on the parent container’s layout strategy. There is no single set of rules that applies uniformly to every container.
Natalia Panferova demonstrates how layoutPriority behaves in different layout containers: HStack and VStack use priorities when allocating available space, while ZStack uses the highest-priority child views to determine its own size. The article also explains how to read priority information in a custom Layout and use it to implement a layout strategy of your own.
In SwiftUI, modifiers that merely provide information while leaving the actual behavior to a parent container are far from uncommon. In fact, this is an important design pattern. Understanding it not only helps developers gain a deeper grasp of SwiftUI’s APIs but also enables us to design custom containers that feel more natural within SwiftUI’s declarative model.
Skip, Day, and Rust: Why I’d Still Choose Swift and Kotlin
A few days ago, Marc Prud’hommeaux, co-founder of Skip and a former member of the Swift Android Workgroup, announced that he was shifting his focus toward Rust. His reasoning is that as AI takes on more of the work of writing code, a language’s friendliness to human developers becomes less important, while portability deserves greater priority.
Junda Ong, however, starts from the same premise and reaches a different conclusion: if AI has reduced the cost of writing and maintaining code, why not build native applications directly with Swift/SwiftUI and Kotlin/Compose? For relatively simple business logic, maintaining two implementations and verifying their consistency through shared test cases may be more economical than introducing an additional cross-platform technology stack.
Shopify recently announced a shift in its mobile development strategy from React Native back to native Swift and Kotlin, similarly citing AI’s role in reducing the cost of maintaining two native codebases. Interestingly, AI makes cross-platform sharing solutions built with languages like Rust easier to implement, while simultaneously reducing the need for developers to sacrifice the advantages of native development simply to avoid duplicated code. Rather than pushing everyone toward a single technical approach, AI may actually make the landscape more diverse.
The AI Assistant Paradox: Apple Can Build the Platform, but Can It Convince Developers?
In the recently released iOS 27.3 beta, Apple opened up Siri AI integration to third-party developers and introduced a Skills mechanism that allows developers to combine multiple operations through App Intents, enabling Siri AI to handle more complex tasks. Combined with Apple’s existing infrastructure for operating systems, identity authentication, app permissions, and secure payments, the company appears to have many of the foundations needed to build a personal AI assistant ecosystem. But does having the technology in place necessarily mean developers will be willing to expose their core business capabilities?
Erk Ekin argues that the challenge goes beyond the additional effort required to integrate and maintain App Intents. AI assistants could fundamentally change the relationship between businesses and their users. When users can bypass an app’s interface and ask an agent to handle shopping, reservations, and other tasks directly, businesses may lose opportunities to recommend products, promote subscriptions, and build customer relationships. Independent agents such as OpenClaw, by contrast, can interact with existing services through UI automation without waiting for developers to provide explicit integrations, but they also face more complicated security and authorization challenges.
The article does not simply conclude that Apple’s approach is bound to fail or that independent agents will inevitably prevail. Ultimately, the two approaches may converge on a similar architecture: accessing services through multiple mechanisms while relying on trusted infrastructure to constrain what agents are authorized to do.
Tool
MockMini: A Minimalist Network Proxy Core Built with C and Swift
Developed by yezicheng, MockMini is a free, open-source experimental network proxy core that is still in its early stages. With approximately 2,600 lines of C and Swift code, the project implements user-space TCP/UDP packet handling and support for multiple proxy protocols. The C layer handles TCP connection state management, data reassembly, and retransmission, while the Swift layer implements protocols including Shadowsocks, VMess, Trojan, and VLESS. On Apple platforms, it uses Network.framework and CryptoKit for network connections and some cryptographic operations.
Compared with mature, full-featured proxy cores, what makes MockMini particularly interesting is its compact implementation and clear separation of responsibilities. By organizing protocol handling, transport encapsulation, and connection management into distinct layers, the project allows developers to trace how data flows through a network proxy within a relatively small codebase, making its internal mechanisms easier to understand.