HomeiOS DevelopmentExtra Updates from the Swift Workshop

Extra Updates from the Swift Workshop


tags. The recursive HTML-to-markdown converter hit a stack overflow. WTF, who writes HTML like that?! (Microsoft after all!)

The answer was to flatten these ridiculous hierarchies iteratively as an alternative of recursively. SwiftText now handles arbitrarily deep nesting with out breaking a sweat.

The Phrase Doc Drawback

I needed brokers to provide editable paperwork that non-Apple folks may really use. HTML isn’t actually editable, and PDFs are static printouts. However DOCX? That’s the lingua franca of the enterprise world.

Behind the scenes, DOCX is only a strict XML dialect in a ZIP archive — conceptually much like HTML. So I carried out the conversion (#5):

swifttext render doc.md -o doc.docx

The output matches the PDF renderer’s styling: Helvetica Neue physique textual content, sized headings with backside borders, code blocks as single-cell tables with correct padding. Good to have this feature out there for the long run…

SwiftMail

SwiftMail sparked extra neighborhood curiosity than I anticipated. Contributors discovered bugs, instructed enhancements, and submitted PRs. The library noticed heavy use in Put up, which meant each edge case in my mail stream turned a possible bug report.

The Threading Nightmare

Message-IDs seem all over the place in e mail: the Message-ID header, In-Reply-To, References. They’re all presupposed to be within the format , however SwiftMail saved them as uncooked strings with inconsistent angle-bracket dealing with (#122).

This brought on refined bugs in Put up’s reply drafting — threading headers would generally have double angle brackets, generally none. The repair was to introduce a correct MessageID worth kind:

let id = MessageID(localPart: "abc", area: "instance.com")
print(id.description)  // ""

Now angle brackets are dealt with appropriately all over the place, and threading headers simply work. And we unified this between SMTP and IMAP, in order that the identical kind will be spherical tripped. I went by means of 1000’s of emails in my archive and couldn’t discover a single message ID that didn’t adhere to the usual.

The Buffer Restrict Drawback

Somewhat than forcing callers to handle batch sizes manually, I made chunking computerized and clear. Request 10,000 messages? SwiftMail splits it into manageable chunks internally. The API stays clear.

The Calendar Invite Drawback

ICS information have been being handled as textual content our bodies as an alternative of attachments. This meant calendar invitations from Outlook customers weren’t correctly extracted by Put up’s mail-room. The repair? Accurately classify textual content/calendar components as attachments (#126) and provides them a default invite.ics filename when none is specified.

The Linux SSE Drawback

SwiftMCP’s SSE shopper didn’t work on Linux. It used URLSession.bytes(for:), which isn’t out there in FoundationNetworking. The runtime simply threw unsupportedPlatform.

The repair was a URLSessionDataDelegate that streams incoming bytes into an AsyncStream, feeding the identical SSE parser used on Apple platforms. Identical habits, totally different plumbing. Now SwiftMCP shoppers work identically on macOS and Linux.

Put up

Put up matured from a working prototype to one thing I really depend on every day. The mail-room talent types my invoices, archives newsletters, routes GitHub notifications to Discord, and marks spam that my server-side filter missed. Getting right here meant fixing numerous stability points.

A consumer – a buddy 😇 – tried to fetch their complete mailbox — 1000’s of messages — and SwiftMail crashed. The FETCH command line was too lengthy, exceeding NIO’s buffer limits (#118). So we added some inside batching to keep away from this downside.

The Connection Race

The unique structure used two persistent IMAP connections per server: one for IDLE (watching for brand new mail) and one for fetching. The fetch connection wanted periodic NOOP instructions to remain alive — roughly each 17 minutes. However servers timeout connections after about 26-27 minutes.

See the issue? The NOOP interval races with the server timeout. Generally the NOOP arrives too late, the connection dies, and restoration takes 60 seconds with a bunch of log noise (#3). I tasked an AI with analyzing mail.app’s connection logs and piece collectively the IMAP idle methods for GMAIL in addition to Dovecot. This knowledgeable the brand new technique for SwiftMail.

The answer was easier than I anticipated: make fetch connections ephemeral. Create them on-demand when new mail arrives, reuse them if nonetheless alive, dispose if useless. No extra NOOP heartbeat. No extra races. The IDLE connection stays persistent (the IDLE protocol naturally retains it alive), and fetch connections are short-lived by design.

The SIGPIPE Drawback

This one was sneaky. If a hook script crashed whereas postd was nonetheless writing JSON to its stdin, the OS would kill the daemon with SIGPIPE. Not “log an error and proceed” — full daemon demise.

The repair was one line: explicitly ignore SIGPIPE. Now Put up logs “Damaged pipe” and retains working.

The Zero-Config Drawback

The unique setup required a ~/.submit.json config file even when all of your credentials have been within the keychain. That’s friction I didn’t want.

Now Put up auto-discovers servers from the keychain (#1). One command is all you want:

submit keychain add private --host imap.gmail.com --port 993
postd begin  # discovers "private" routinely

SwiftMCP

The MCP framework went from 1.1.0 to 1.4.0 in two weeks. That’s not model inflation — every launch addressed actual wants from my shopper challenge.

Right here’s the factor about MCP: it’s an awesome protocol for APIs normally, not simply AI brokers. You get formalized infrastructure for bidirectional messaging — the server can push to the shopper, not simply reply to requests. And since SwiftMCP generates Swift code for each side, you don’t have to consider the wire protocol. Outline your @MCPServer, and the shopper proxy comes without cost.

Whereas implementing shopper and servers for an enormous challenge I’m engaged on nowadays, I discovered – and eradicated – many extra tough edges and lacking options.

From Logs to Structured Notifications

Initially, I used to be piggy-backing standing updates into regular log notifications. The server would emit a log message like “Queue: 3 jobs working, 0 errors” and the shopper would parse it. That felt silly.

The MCP spec permits customized notifications with arbitrary structured information. So I made that attainable in SwiftMCP. Now my daemon broadcasts well being standing as a correct typed notification:

TRACE: [SwiftMCP] Obtained JSON-RPC message: {
  "jsonrpc": "2.0",
  "methodology": "notifications/healthChanged",
  "params": {
    "concurrencyLimit": 4,
    "errorCount": 0,
    "runningJobs": 0,
    "uptimeSeconds": 210.67,
    "model": "0.1.0"
  }
}

The shopper defines a Codable struct and will get compile-time kind security:

struct HealthStatus: Codable {
    let concurrencyLimit: Int
    let errorCount: Int
    let runningJobs: Int
    let uptimeSeconds: Double
}

extension MyClient: MCPServerProxyNotificationHandling {
    func handleNotification(_ methodology: String, params: HealthStatus) async {
        if params.errorCount > 0 {
            logger.warning("Server reporting (params.errorCount) errors")
        }
    }
}

Useful resource Subscriptions

MCP has an idea of “assets” recognized by URI. I wanted shoppers to look at particular person queue objects — submit a job, get a URI again, monitor its progress.

Now there’s full assist for useful resource subscriptions. The shopper calls subscribeResource(uri:), and when that useful resource adjustments, the server broadcasts an replace to all subscribers. On the server aspect, you simply name broadcastResourceUpdated(uri:) each time state adjustments. SwiftMCP handles the subscription monitoring and fan-out.

This turned my queue system from polling-based to push-based. A lot cleaner.

The File Measurement Drawback

My shopper challenge wanted to add paperwork to an MCP server. Base64 encoding works for small information, however falls aside shortly for something substantial — the JSON payload balloons, you hit message dimension limits, and every little thing grinds to a halt (#73).

The brand new structure makes use of CID-based uploads: shoppers ship cid: placeholders in software arguments, add information to POST /mcp/uploads/{cid} concurrently, and the server delivers the info to your software operate transparently. There’s even progress notifications throughout add. Opting in is one protocol conformance:

@MCPServer
struct MyServer: MCPFileUploadHandling { }

The following steps listed here are being a bit smarter about reminiscence on the server aspect. As an alternative of protecting all of it in RAM a number of occasions, I’m pondering to stream the binary information straight to information and to make use of memory-mapped Knowledge as an alternative. Additionally there ought to be a binary file obtain performance that bypasses the MCP base64 method. Nonetheless pondering easy methods to greatest try this.

The mDNSResponder Drawback

Put up’s daemon makes use of Bonjour for native MCP discovery. Sooner or later it simply stopped working. The logs confirmed: “DNS Error: ServiceNotRunning.”

Seems macOS’s mDNSResponder had restarted (which occurs extra typically than you’d assume — community adjustments, sleep/wake, system updates), and my Bonjour listener died with it (#77).

The repair was a generation-based state machine with exponential backoff. When mDNSResponder dies, SwiftMCP retries: 1s → 2s → 4s → 8s → as much as 60s max. When it comes again, the listener recovers routinely. No extra 1-second retry spam, no extra orphaned duties.

SwiftMCP Meets Xcode

And now for the shock: Xcode now exposes an MCP server. You possibly can allow it underneath Settings → Intelligence → “Permit exterior brokers to make use of Xcode instruments.”

Right here’s what you see when you have a look at it with Claude Code:

Xcode Intelligence settings with MCP enabled

Including it to Claude was fast and painless — however my first intuition was to generate a SwiftMCP shopper proxy so I may remote-control Xcode from any Swift app:

swift run SwiftMCPUtility generate-proxy 
  --command "/Functions/Xcode-beta.app/Contents/Developer/usr/bin/mcpbridge"

That’s when issues obtained attention-grabbing. Xcode by no means confirmed the approval dialog. After some debugging, I found the proxy generator wasn’t sending the notifications/initialized notification after the initialize handshake. The MCP spec requires this, and Xcode’s server is strict about it (#91).

Whereas fixing that, I additionally added shopper identification — Xcode shows the shopper identify and model in its approval dialog:

Xcode approval dialog showing

The proxy generator now routinely derives the shopper identify from the MCP server’s marketed identify. For xcode-tools, you get an XcodeTools enum namespace with a Consumer actor inside — matching the construction you’d get from @MCPServer(generateClient: true).

Generated XcodeTools.swift header showing server metadata

The generator produces typed response structs for all 19 instruments, full with all of the documentation that the MCP software has within the schema.

Generated response structs like BuildProjectResponse

And the Consumer actor with auto-derived naming:

Generated Client actor with connect method

You possibly can see the full generated XcodeTools.swift on GitHub Gist.

I’ve to say, Apple did a pleasant job right here. The MCP schema is nicely fleshed out — they’ve structured response varieties for every little thing. BuildProjectResponse tells you precisely what you get again: success standing, construct log path, errors and warnings as typed arrays. Identical for RunTestsResponse, GetBuildLogResponse, and the remainder. It’s clear somebody thought concerning the developer expertise. My one gripe? A lot of the struct varieties lack documentation feedback, despite the fact that the MCP spec helps descriptions for each area. A missed alternative — however the varieties themselves are self-explanatory sufficient.

Oh and the opposite gripe: you want to have open any challenge you need to work together with. You want tab IDs for nearly all operations. But it surely’s an awesome and really shocking initiative from Apple!

Earlier than all this works although, it’s a must to additionally allow the “Permit exterior brokers to make use of Xcode instruments” change discovered underneath Intelligence.

The 19 Xcode MCP tools organized by category

Conclusion

RL (“actual life”) work drives probably the most attention-grabbing enhancements to my open supply initiatives. When one thing feels off then I simply need to repair it instantly. Or relatively, have an agent repair it instantly.

A giant shopper challenge pushed me to shine SwiftMCP to be used as API, and my curiosity within the Xcode MCP instruments benefitted the shopper proxy components of SwiftMCP. I used to be pondering if it is perhaps attention-grabbing to auto-generate a CLI software for Xcode Instruments. This is perhaps an awesome instance for utilizing the MCP utility….

The opposite enhancements have been pushed principally by me engaged on my OpenClaw’s capability to learn and mange my inboxes. Increasingly I’m utilizing the options to draft me a e mail responses. For these my agent can get the markdown illustration of a HTML e mail and craft a markdown response interleaving the > quoted questions of the e-mail being replied to. And naturally threading headers get preserved to. The following step there may be so as to add the characteristic of really additionally sending such drafts.

Busy occasions!


Classes: Updates

RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

- Advertisment -
Google search engine

Most Popular

Recent Comments