Highlights
SwiftMail 1.8.0 makes outgoing mail noticeably better-behaved and IMAP connections more robust. Calendar invites now go out inline per RFC 6047, so Apple Mail, Outlook, Gmail, and iCloud render their Accept/Decline UI instead of showing a bare .ics attachment. Non-ASCII headers (Subject, display names) are now RFC 2047 encoded-word encoded instead of leaking raw 8-bit bytes onto the wire. On the IMAP side, the response parser's buffer limit is now configurable for dense mailboxes, IDLE connections are only reused when their teardown verifiably completed, and concurrent named-connection authentication is coalesced into a single in-flight attempt.
No breaking changes — all new parameters have defaults matching the previous behavior.
Changes
Send text/calendar invites inline per RFC 6047 (#176)
- Calendar invites previously went out like any attachment: base64-encoded with
Content-Disposition: attachment, so mail clients degraded them to a plain.icsfile instead of showing invite UI. - A message carrying exactly one UTF-8
text/calendarattachment (and nothing else) is now built asmultipart/alternative— thetext/plainbody plus the ICS as 7bit text, the shape clients key their Accept/Decline UI off. - In
multipart/mixedmessages, calendar attachments ship inline as 7bit text; all other attachments keep the existing base64 behavior. Non-UTF-8 calendar data falls back to base64. - ICS content is normalized to CRLF before emission — bare-LF input would otherwise violate SMTP DATA framing (RFC 5321 §2.3.8) and defeat dot-stuffing.
- Field-tested for months via Proton Bridge; verified rendering invite UI in Apple Mail, Outlook, Gmail, and iCloud.
RFC 2047 encoded-word encoding for outgoing headers (#178)
SubjectandFrom/To/Ccdisplay names were written verbatim, so non-ASCII values (CJK, accented names, emoji) went out as raw 8-bit bytes — invalid per RFC 5322 and prone to mojibake at the receiver. The library could already decode encoded-words but never encoded them.- New
String.rfc2047EncodedHeader()— the inverse ofdecodeMIMEHeader(). Pure-ASCII strings pass through unchanged; otherwise it emits Base64 encoded-words (=?UTF-8?B?…?=), each ≤ 75 octets, split on character boundaries and folded withCRLF SPACE. - New
EmailAddress.headerString()RFC 2047-encodes a non-ASCII display name (encoded-words are never placed inside a quoted-string). Applied inEmail+ContentandEMLSerializer. - Output round-trips through the existing
decodeMIMEHeader()(verified in tests).
Configurable IMAP response parser buffer limit (#179)
- The response parser's buffer limit was hardcoded to 1 MB. Dense mailboxes can return
SEARCH/FETCHresponses larger than that — e.g. aUID SEARCHmatching tens of thousands of messages — which surfaced asPayloadTooLargeErrorand dropped the connection. - Both public
IMAPServerinitializers gain aresponseBufferLimit:parameter, defaulting to the newIMAPServer.defaultResponseBufferLimit(1 MB) — no behavior change for existing callers. - The value propagates to the primary connection and to spawned idle and named connections, where large IDLE / parallel-FETCH responses actually arrive.
Recycle IMAP IDLE connection only after verified teardown (#180)
- An IDLE connection is treated as reusable only after
DONEreaches the final tagged IDLE completion. If teardown is cancelled, the channel or server terminates mid-DONE, or completion is otherwise uncertain, the connection is recycled instead of reused in an unknown state. - Resilient IDLE session shutdown is now single-owner and idempotent, and the runner is awaited before its dedicated event-loop group shuts down.
- Child tasks no longer inherit
IMAPCommandQueueownership, so a child task cannot start another tagged command while its parent still owns the queue. - Regression coverage for cancelled
DONE, untaggedBYEduringDONE, child-task queue reentry, and repeated IDLE renewals.
Coalesce concurrent named-connection authentication (#181)
- Concurrent
IMAPServer.connection(named:)calls for the same normalized name now share one connection creation, and concurrent authentication attempts on the same handle share a single in-flight attempt instead of racing. - Retry behavior after failure is preserved: the in-flight sentinel clears before waiters return, so a later call can retry normally.
Drop the swift-docc-plugin dependency (#177)
- The plugin was declared but never referenced by any target, CI step, or tooling — its only effect was two extra repo clones for SwiftMail and every consumer.
- Swift Package Index injects the DocC plugin itself when building hosted docs, and Xcode's Build Documentation has DocC built in, so both keep working.