Swift implementation of RFC 5322: Internet Message Format - email message structure and formatting standard.
RFC 5322 defines the Internet Message Format used for email messages. This package provides a pure Swift implementation of RFC 5322-compliant email address validation with extended features beyond RFC 5321, plus message composition with headers and body formatting suitable for generating .eml files.
The package handles email addresses with more permissive rules than SMTP (RFC 5321), supports message structure with proper header ordering, and provides utilities for date formatting and message rendering.
- RFC 5322 Email Addresses: Extended validation beyond SMTP with additional dot-atom rules
- Internet Message Format: Complete message structure with headers and body
- Display Name Support: Parse and format addresses with display names
- Message Composition: Create RFC 5322-compliant messages for .eml files
- Date Formatting: RFC 5322-compliant date/time formatting
- Header Management: Structured header handling with proper ordering
- RFC 5321 Compatibility: Convert between RFC 5322 and RFC 5321 formats
- Type-Safe API: Structured components with compile-time safety
- Codable Support: Seamless JSON encoding/decoding
Add swift-rfc-5322 to your package dependencies:
dependencies: [
.package(url: "https://github.com/swift-standards/swift-rfc-5322.git", from: "0.1.0")
]Then add it to your target:
.target(
name: "YourTarget",
dependencies: [
.product(name: "RFC_5322", package: "swift-rfc-5322")
]
)import RFC_5322
// Parse email address
let email = try RFC_5322.EmailAddress("[email protected]")
print(email.localPart.stringValue) // "user"
print(email.domain.name) // "example.com"
// Parse with display name
let named = try RFC_5322.EmailAddress("John Doe <[email protected]>")
print(named.displayName) // "John Doe"
print(named.addressValue) // "[email protected]"
// Create from components
let addr = try RFC_5322.EmailAddress(
displayName: "Jane Smith",
localPart: .init("jane"),
domain: .init("example.com")
)// Create a message
let message = RFC_5322.Message(
from: try RFC_5322.EmailAddress(
displayName: "John Doe",
localPart: .init("john"),
domain: .init("example.com")
),
to: [try RFC_5322.EmailAddress("[email protected]")],
subject: "Hello from Swift!",
date: Date(),
messageId: RFC_5322.Message.generateMessageId(
from: try RFC_5322.EmailAddress("[email protected]")
),
body: "Hello, World!".data(using: .utf8)!
)
// Render to RFC 5322 format
let emlContent = message.render()
print(emlContent)
// From: John Doe <[email protected]>
// To: [email protected]
// Subject: Hello from Swift!
// Date: Tue, 12 Nov 2025 20:00:00 +0000
// Message-ID: <[email protected]>
// MIME-Version: 1.0
//
// Hello, World!// Message with CC, BCC, and custom headers
let message = RFC_5322.Message(
from: try RFC_5322.EmailAddress("[email protected]"),
to: [try RFC_5322.EmailAddress("[email protected]")],
cc: [try RFC_5322.EmailAddress("[email protected]")],
bcc: [try RFC_5322.EmailAddress("[email protected]")],
replyTo: try RFC_5322.EmailAddress("[email protected]"),
subject: "Meeting Notes",
date: Date(),
messageId: "<[email protected]>",
body: "Meeting summary...".data(using: .utf8)!,
additionalHeaders: [
RFC_5322.Header(name: "X-Priority", value: "1"),
RFC_5322.Header(name: .contentType, value: "text/plain; charset=utf-8")
]
)// Format date in RFC 5322 format
let dateString = RFC_5322.Date.string(from: Date())
// "Tue, 12 Nov 2025 20:00:00 +0000"
// Parse RFC 5322 date
let date = try RFC_5322.Date.date(from: "Tue, 12 Nov 2025 20:00:00 +0000")Extended email address validation per RFC 5322:
public struct EmailAddress: Hashable, Sendable {
public let displayName: String?
public let localPart: LocalPart
public let domain: Domain
public init(displayName: String?, localPart: LocalPart, domain: Domain)
public init(_ string: String) throws
public var stringValue: String // Full format with display name
public var addressValue: String // Just the email address part
public func toRFC5321() throws -> RFC_5321.EmailAddress
}Key differences from RFC 5321:
- Stricter dot-atom rules (no
!or|in local-part) - Validates against consecutive dots
- Checks for leading/trailing dots in local-part
Complete Internet Message Format:
public struct Message: Hashable, Sendable {
public let from: EmailAddress
public let to: [EmailAddress]
public let cc: [EmailAddress]?
public let bcc: [EmailAddress]?
public let replyTo: EmailAddress?
public let subject: String
public let date: Date
public let messageId: String
public let body: Data
public let additionalHeaders: [Header]
public let mimeVersion: String
public func render() -> String
public var bodyString: String?
public static func generateMessageId(from: EmailAddress) -> String
}Structured header management:
public struct Header: Hashable, Sendable {
public let name: HeaderName
public let value: String
}
public enum HeaderName {
case from
case to
case cc
case subject
case date
case messageId
case contentType
case custom(String)
}- swift-rfc-1123 - Domain name validation
- swift-rfc-5321 - SMTP email address format
- Email clients and server implementations
- Message composition tools
- .eml file generators
- Swift 6.0+
- macOS 13.0+ / iOS 16.0+
This library is released under the Apache License 2.0. See LICENSE for details.
Contributions are welcome! Please feel free to submit a Pull Request.