Skip to content

1.8.0

Latest

Choose a tag to compare

@odrobnik odrobnik released this 02 Jul 08:36
dbb1d0b

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 .ics file instead of showing invite UI.
  • A message carrying exactly one UTF-8 text/calendar attachment (and nothing else) is now built as multipart/alternative — the text/plain body plus the ICS as 7bit text, the shape clients key their Accept/Decline UI off.
  • In multipart/mixed messages, 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)

  • Subject and From/To/Cc display 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 of decodeMIMEHeader(). Pure-ASCII strings pass through unchanged; otherwise it emits Base64 encoded-words (=?UTF-8?B?…?=), each ≤ 75 octets, split on character boundaries and folded with CRLF SPACE.
  • New EmailAddress.headerString() RFC 2047-encodes a non-ASCII display name (encoded-words are never placed inside a quoted-string). Applied in Email+Content and EMLSerializer.
  • 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/FETCH responses larger than that — e.g. a UID SEARCH matching tens of thousands of messages — which surfaced as PayloadTooLargeError and dropped the connection.
  • Both public IMAPServer initializers gain a responseBufferLimit: parameter, defaulting to the new IMAPServer.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 DONE reaches 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 IMAPCommandQueue ownership, so a child task cannot start another tagged command while its parent still owns the queue.
  • Regression coverage for cancelled DONE, untagged BYE during DONE, 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.

Full Changelog

1.7.1...1.8.0