Data Unit Converter — Swift source
Convert between digital data units - B, KB/KiB, MB/MiB, GB/GiB, TB/TiB. Switch between decimal (1000) and binary (1024) bases, runs entirely in your browser.
This is the Swift implementation — the same logic the interactive tool runs, in a shareable, citable form.
// =============================================================================
// DataUnitConverter.swift — CosmoDev polyglot showcase port of the
// `data-unit-converter` tool
// -----------------------------------------------------------------------------
// Language : Swift (5.9, standard library + Foundation for pow)
// Source: ported from src/lib/dataUnits.ts (the canonical, live TypeScript
// lib); mirrors src/tool-sources/data-unit-converter/{python.py,rust.rs}
// License : display source — part of CosmoDev's polyglot tool pages
// (dev.cosmolabs.org). Shown verbatim alongside the JS/TS/Go/Rust/
// Python ports and the other language ports.
// -----------------------------------------------------------------------------
// Pure digital-data unit conversion. Convert between B, KB/KiB, MB/MiB,
// GB/GiB, TB/TiB: the decimal base 1000 pairs with KB/MB/GB/TB (SI) and the
// binary base 1024 with KiB/MiB/GiB/TiB (IEC). Deterministic and
// side-effect free; convertData returns .nan for non-finite input, unknown
// units, or an invalid base.
// =============================================================================
import Foundation
/// Supported conversion radix: decimal (`.decimal = 1000`, SI units) or binary
/// (`.binary = 1024`, IEC units).
public enum Base: Int {
case decimal = 1000
case binary = 1024
/// Numeric radix as a `Double`, ready for `pow`.
var radix: Double { Double(rawValue) }
}
/// Recognized digital-data units. The raw values are the symbol strings used
/// in the UI and persisted in shareable URLs — a raw-value enum gives the
/// symbol→unit dictionary lookup (`DataUnit(rawValue:)`) for free.
/// `kilobyte`…`terabyte` are decimal (1000); `kibibyte`…`tebibyte` are binary
/// (1024); `byte` is shared by both bases.
public enum DataUnit: String {
case byte = "B"
case kilobyte = "KB"
case megabyte = "MB"
case gigabyte = "GB"
case terabyte = "TB"
case kibibyte = "KiB"
case mebibyte = "MiB"
case gibibyte = "GiB"
case tebibyte = "TiB"
/// Power-of-base rank: `.byte` is rank 0 and each prefix step adds 1. KB
/// and KiB are both rank 1 — the unit picks the rank, the base decides
/// whether a rank means ×1000 or ×1024.
var exponent: Int {
switch self {
case .byte: return 0
case .kilobyte, .kibibyte: return 1
case .megabyte, .mebibyte: return 2
case .gigabyte, .gibibyte: return 3
case .terabyte, .tebibyte: return 4
}
}
/// Units available under each base, ordered smallest (byte) to largest.
/// The UI uses these to populate the per-base unit pickers.
public static func units(for base: Base) -> [DataUnit] {
switch base {
case .decimal:
return [.byte, .kilobyte, .megabyte, .gigabyte, .terabyte]
case .binary:
return [.byte, .kibibyte, .mebibyte, .gibibyte, .tebibyte]
}
}
}
/// Convert a value between two digital-data units under the given base.
///
/// Strategy: reduce to bytes via `value × base^fromExp`, then divide by
/// `base^toExp`. Non-finite values (Inf/NaN) return `.nan`, mirroring the
/// TypeScript contract. `Base` and `DataUnit` are already constrained by their
/// types; the symbol-based entry point below preserves the original
/// "unknown unit → NaN" behavior for string inputs.
public func convertData(_ value: Double, from: DataUnit, to: DataUnit, base: Base) -> Double {
// Reject Inf/NaN early so they don't propagate through the multiplication.
guard value.isFinite else { return .nan }
let radix = base.radix
return (value * pow(radix, Double(from.exponent))) / pow(radix, Double(to.exponent))
}
/// String-in / NaN-out entry point that mirrors the TypeScript signature
/// (`convertData(value, fromUnit, toUnit, base)`) for unknown-symbol handling.
/// Returns NaN for non-finite input, unrecognized symbols, or a non-canonical
/// base value.
public func convertData(_ value: Double, fromSymbol: String, toSymbol: String, base: Int) -> Double {
guard value.isFinite else { return .nan }
// Only the two canonical bases are valid; anything else is a caller bug.
guard let radix = Base(rawValue: base) else { return .nan }
// Unknown unit symbols short-circuit to NaN, like the TS dictionary miss.
guard let from = DataUnit(rawValue: fromSymbol) else { return .nan }
guard let to = DataUnit(rawValue: toSymbol) else { return .nan }
return convertData(value, from: from, to: to, base: radix)
}
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 →