MIME Type Reference — Swift source
A searchable map of common file extensions to their MIME types - text, image, audio, video, application, font, and multipart. Look up by extension or by type, filter by category, and copy the exact Content-Type string for any format.
This is the Swift implementation — the same logic the interactive tool runs, in a shareable, citable form.
// mime-types — Swift port of the curated extension ↔ MIME table, ported from
// src/lib/mime-types.ts. Display source for CosmoDev's polyglot tool pages.
//
// The table is a `let` array of 71 entries grouped by the seven top-level
// media types (RFC 2046). Each entry's `type` always equals the top-level
// segment of its own `mime`, so the category filter and the Content-Type
// never disagree.
/// The seven top-level media types a curated entry can belong to.
enum MimeCategory: String, CaseIterable, Sendable {
case text, image, audio, video, application, font, multipart
}
/// One curated extension ↔ MIME mapping (immutable value type).
struct MimeEntry: Equatable, Sendable {
/// File extension, lowercase, no leading dot (e.g. "json").
let ext: String
/// Full MIME / Content-Type string (e.g. "application/json").
let mime: String
/// Top-level media type — matches the `mime` string's first segment.
let type: MimeCategory
/// Human-readable format name (e.g. "JSON").
let name: String
}
/// Optional constraints — nil fields are ignored, mirroring `undefined`
/// fields in the TypeScript source.
struct MimeFilter: Sendable {
var type: MimeCategory?
var query: String?
init(type: MimeCategory? = nil, query: String? = nil) {
self.type = type
self.query = query
}
}
enum MimeTable {
/// Display order of the categories (used by the UI filter).
static let mimeCategories: [MimeCategory] = MimeCategory.allCases
/// The curated extension ↔ MIME table. One entry is one (extension,
/// type) pair, so an extension with several MIME types (e.g. .js)
/// appears more than once, and a MIME with several extensions (e.g.
/// image/jpeg) appears once per extension. Values follow IANA / MDN.
static let mime: [MimeEntry] = [
// ── text ────────────────────────────────────────────────────────
.init(ext: "txt", mime: "text/plain", type: .text, name: "Plain text"),
.init(ext: "html", mime: "text/html", type: .text, name: "HTML document"),
.init(ext: "htm", mime: "text/html", type: .text, name: "HTML document"),
.init(ext: "xhtml", mime: "application/xhtml+xml", type: .application, name: "XHTML"),
.init(ext: "css", mime: "text/css", type: .text, name: "CSS stylesheet"),
.init(ext: "csv", mime: "text/csv", type: .text, name: "Comma-separated values"),
.init(ext: "xml", mime: "application/xml", type: .application, name: "XML document"),
.init(ext: "js", mime: "text/javascript", type: .text, name: "JavaScript"),
.init(ext: "js", mime: "application/javascript", type: .application, name: "JavaScript (legacy)"),
.init(ext: "mjs", mime: "text/javascript", type: .text, name: "JavaScript module"),
.init(ext: "json", mime: "application/json", type: .application, name: "JSON"),
.init(ext: "md", mime: "text/markdown", type: .text, name: "Markdown"),
.init(ext: "yaml", mime: "text/yaml", type: .text, name: "YAML"),
.init(ext: "yml", mime: "text/yaml", type: .text, name: "YAML"),
.init(ext: "ics", mime: "text/calendar", type: .text, name: "iCalendar"),
.init(ext: "rtf", mime: "application/rtf", type: .application, name: "Rich text"),
.init(ext: "tsv", mime: "text/tab-separated-values", type: .text, name: "Tab-separated values"),
// ── image ───────────────────────────────────────────────────────
.init(ext: "jpg", mime: "image/jpeg", type: .image, name: "JPEG image"),
.init(ext: "jpeg", mime: "image/jpeg", type: .image, name: "JPEG image"),
.init(ext: "png", mime: "image/png", type: .image, name: "PNG image"),
.init(ext: "gif", mime: "image/gif", type: .image, name: "GIF image"),
.init(ext: "webp", mime: "image/webp", type: .image, name: "WebP image"),
.init(ext: "avif", mime: "image/avif", type: .image, name: "AVIF image"),
.init(ext: "svg", mime: "image/svg+xml", type: .image, name: "SVG image"),
.init(ext: "bmp", mime: "image/bmp", type: .image, name: "Bitmap image"),
.init(ext: "ico", mime: "image/x-icon", type: .image, name: "Icon"),
.init(ext: "tif", mime: "image/tiff", type: .image, name: "TIFF image"),
.init(ext: "tiff", mime: "image/tiff", type: .image, name: "TIFF image"),
.init(ext: "heic", mime: "image/heic", type: .image, name: "HEIC image"),
// ── audio ───────────────────────────────────────────────────────
.init(ext: "mp3", mime: "audio/mpeg", type: .audio, name: "MP3 audio"),
.init(ext: "wav", mime: "audio/wav", type: .audio, name: "WAV audio"),
.init(ext: "ogg", mime: "audio/ogg", type: .audio, name: "Ogg audio"),
.init(ext: "oga", mime: "audio/ogg", type: .audio, name: "Ogg audio"),
.init(ext: "flac", mime: "audio/flac", type: .audio, name: "FLAC audio"),
.init(ext: "aac", mime: "audio/aac", type: .audio, name: "AAC audio"),
.init(ext: "m4a", mime: "audio/mp4", type: .audio, name: "M4A audio"),
.init(ext: "weba", mime: "audio/webm", type: .audio, name: "WebM audio"),
.init(ext: "mid", mime: "audio/midi", type: .audio, name: "MIDI"),
.init(ext: "midi", mime: "audio/midi", type: .audio, name: "MIDI"),
// ── video ───────────────────────────────────────────────────────
.init(ext: "mp4", mime: "video/mp4", type: .video, name: "MP4 video"),
.init(ext: "webm", mime: "video/webm", type: .video, name: "WebM video"),
.init(ext: "avi", mime: "video/x-msvideo", type: .video, name: "AVI video"),
.init(ext: "mov", mime: "video/quicktime", type: .video, name: "QuickTime video"),
.init(ext: "mkv", mime: "video/x-matroska", type: .video, name: "Matroska video"),
.init(ext: "mpeg", mime: "video/mpeg", type: .video, name: "MPEG video"),
.init(ext: "mpg", mime: "video/mpeg", type: .video, name: "MPEG video"),
.init(ext: "ogv", mime: "video/ogg", type: .video, name: "Ogg video"),
.init(ext: "m4v", mime: "video/mp4", type: .video, name: "M4V video"),
// ── application ─────────────────────────────────────────────────
.init(ext: "pdf", mime: "application/pdf", type: .application, name: "PDF document"),
.init(ext: "zip", mime: "application/zip", type: .application, name: "ZIP archive"),
.init(ext: "gz", mime: "application/gzip", type: .application, name: "Gzip archive"),
.init(ext: "tar", mime: "application/x-tar", type: .application, name: "Tar archive"),
.init(ext: "rar", mime: "application/vnd.rar", type: .application, name: "RAR archive"),
.init(ext: "7z", mime: "application/x-7z-compressed", type: .application, name: "7-Zip archive"),
.init(ext: "bin", mime: "application/octet-stream", type: .application, name: "Binary data"),
.init(ext: "exe", mime: "application/x-msdownload", type: .application, name: "Windows executable"),
.init(ext: "jar", mime: "application/java-archive", type: .application, name: "Java archive"),
.init(ext: "wasm", mime: "application/wasm", type: .application, name: "WebAssembly"),
.init(ext: "doc", mime: "application/msword", type: .application, name: "Word document"),
.init(ext: "docx", mime: "application/vnd.openxmlformats-officedocument.wordprocessingml.document", type: .application, name: "Word document (OOXML)"),
.init(ext: "xls", mime: "application/vnd.ms-excel", type: .application, name: "Excel spreadsheet"),
.init(ext: "xlsx", mime: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", type: .application, name: "Excel spreadsheet (OOXML)"),
.init(ext: "ppt", mime: "application/vnd.ms-powerpoint", type: .application, name: "PowerPoint"),
.init(ext: "pptx", mime: "application/vnd.openxmlformats-officedocument.presentationml.presentation", type: .application, name: "PowerPoint (OOXML)"),
// ── font ────────────────────────────────────────────────────────
.init(ext: "woff", mime: "font/woff", type: .font, name: "WOFF font"),
.init(ext: "woff2", mime: "font/woff2", type: .font, name: "WOFF2 font"),
.init(ext: "ttf", mime: "font/ttf", type: .font, name: "TrueType font"),
.init(ext: "otf", mime: "font/otf", type: .font, name: "OpenType font"),
.init(ext: "eot", mime: "application/vnd.ms-fontobject", type: .application, name: "Embedded OpenType"),
// ── multipart ───────────────────────────────────────────────────
.init(ext: "mht", mime: "multipart/related", type: .multipart, name: "MHTML web archive"),
.init(ext: "mhtml", mime: "multipart/related", type: .multipart, name: "MHTML web archive"),
]
/// Lowercase an extension and drop a leading dot so ".JSON" == "json".
/// The while loop drops one or more leading dots, matching /^\.+/.
static func normalizeExt(_ ext: String) -> String {
var e = ext.trimmingCharacters(in: .whitespacesAndNewlines).lowercased()
while e.hasPrefix(".") { e.removeFirst() }
return e
}
/// Every entry whose extension matches. The dot is optional and matching
/// is case-insensitive, so ".js", "JS", and "js" are equivalent.
/// Returns [] for an unknown/empty extension.
static func lookupByExt(_ ext: String) -> [MimeEntry] {
let e = normalizeExt(ext)
guard !e.isEmpty else { return [] }
return mime.filter { $0.ext == e }
}
/// Every entry whose MIME matches, case-insensitively, tolerating
/// surrounding whitespace. One MIME commonly maps to several extensions
/// (image/jpeg → jpg, jpeg), so all are returned. [] for unknown/empty.
static func lookupByMime(_ mimeType: String) -> [MimeEntry] {
let m = mimeType.trimmingCharacters(in: .whitespacesAndNewlines).lowercased()
guard !m.isEmpty else { return [] }
return mime.filter { $0.mime.lowercased() == m }
}
/// Filter the full table by category and/or a free-text query matched
/// (case-insensitively) against ext, mime, and name. Omitted options are
/// ignored; an empty query returns the category-filtered set unchanged.
/// An unknown category value yields nothing (it is not silently coerced).
static func filter(_ opts: MimeFilter = MimeFilter()) -> [MimeEntry] {
let q = (opts.query ?? "").trimmingCharacters(in: .whitespacesAndNewlines).lowercased()
return mime.filter { e in
if let type = opts.type, e.type != type { return false }
guard !q.isEmpty else { return true }
return e.ext.lowercased().contains(q)
|| e.mime.lowercased().contains(q)
|| e.name.lowercased().contains(q)
}
}
}
// .JSON resolves case-insensitively; IMAGE/JPEG maps back to every extension.
for e in MimeTable.lookupByExt(".JSON") { print(".JSON -> \(e.mime) (\(e.name))") }
for e in MimeTable.lookupByMime("IMAGE/JPEG") { print("image/jpeg -> .\(e.ext)") }
print("query \"font\": \(MimeTable.filter(MimeFilter(query: "font")).count) of \(MimeTable.mime.count) entries")
Also available in 13 other languages
Every CosmoDev tool ships its pure logic in TypeScript (web) and Go (CLI), with authored implementations in a dozen-plus languages — the same contract, ported. Compare all languages side by side →