Apple actually provides a QUIC implementation through Network.framework on macOS and iOS. Despite that, we chose MsQuic — and here's why.
1. Better performance
In our testing, MsQuic consistently outperformed Network.framework's QUIC implementation — even though MsQuic isn't fully optimized for Darwin-based systems yet.
2. Network.framework's over-abstraction
Apple's Network.framework abstracts multiple protocols (HTTP, TCP, UDP, QUIC) behind unified APIs like NWConnection and NWConnectionGroup. While this sounds convenient in theory, the abstraction made it incredibly time-consuming to locate and configure the low-level settings we actually needed.
3. Unpredictable and poorly documented behavior in Network.framework
Network.framework wraps lower-level APIs to provide its QUIC support. Although QUIC support was introduced around iOS 11–12, the wrapping still feels incomplete, leading to behaviors that were, frankly, maddening to work with:
- Attempting to connect to an unreachable address would neither fail nor time out — it would just hang in a "connecting" state forever, even though the lower-level logs clearly showed "unreachable, giving up."
- Closing multiplexed streams didn't actually clean them up. Opening and closing streams repeatedly would quickly hit the max stream limit negotiated between client and server, making it impossible to open new streams.
- Connection migration was offered under the name "Multipath," but the conditions for it to work were unclear, and debugging was nearly impossible. With MsQuic, we got connection migration working in just 3 hours.
A heartfelt thank you to the MsQuic contributors. Noctiluca wouldn't exist without your work.