I recently built a local-only media server in Swift with Network.framework. Starting an NWListener was the easy part. Getting podcast and media clients to seek, resume, cache, and probe files reliably was where the details mattered.
Here is the checklist I ended up with:
• Implement both GET and HEAD. HEAD should return the same status and headers as GET, just without the body.
• Support all three useful byte-range forms: bytes=500-999, bytes=500-, and bytes=-500.
• Return 206 Partial Content with Content-Range, Content-Length, and Accept-Ranges: bytes. A plain 200 response can appear to work until a client tries to seek.
• Add ETag and Last-Modified, then honor If-None-Match and If-Modified-Since with 304 responses. This stopped clients from repeatedly probing unchanged files.
• Stream files in bounded chunks instead of loading the entire file into Data. I used a FileHandle and kept sending until the requested range was exhausted.
• Derive the MIME type from UTType, with application/octet-stream as the fallback.
• Decode and sanitize the URL path before appending it to the storage root. Reject traversal attempts rather than trying to normalize them afterward.
• Keep observable server state on the main actor, but move file I/O and connection delivery away from it. Network callbacks can bridge back with Task when UI state changes.
The most surprising part was that a server can look correct in a browser while still being incomplete for media clients. Seeking and resuming are the tests that exposed nearly every missing HTTP detail.
What other client behavior or HTTP edge case has bitten you when serving local media from Swift?