Hash Generator — Swift source
Compute SHA-1, SHA-256, SHA-384 and SHA-512 hashes of any text at once, using the browser's native SubtleCrypto. Copy each digest. 100% client-side.
This is the Swift implementation — the same logic the interactive tool runs, in a shareable, citable form.
// hash — SHA-1 / SHA-256 / SHA-384 / SHA-512 digest of a UTF-8 string.
//
// Language: Swift 5.9 (Apple CryptoKit / swift-crypto)
// Source: CosmoDev polyglot showcase port of the `hash` tool, ported from
// src/tools/HashGenerator.tsx (the canonical TypeScript island).
// License: display source — part of CosmoDev's polyglot tool pages.
//
// The TypeScript reference delegates to `crypto.subtle.digest`, and the Rust
// port uses the RustCrypto `sha1`/`sha2` crates. The Swift standard library
// ships no crypto, so the idiomatic path is Apple's CryptoKit — and on
// non-Apple platforms the open-source swift-crypto package, which exposes
// the same SHA1/SHA256/Sha384/SHA512 types under `import Crypto` (the
// ecosystem equivalent named where Rust names its crates). The canImport
// switch below picks the right module either way.
//
// Behavior mirrors the TypeScript reference: UTF-8 input, lowercase hex
// output, and rejection of unknown algorithm names (HashError, the Swift
// standing of the Rust port's HashError enum). SHA-1 is offered for legacy
// compatibility only; it is not collision-resistant.
import Foundation
#if canImport(CryptoKit)
import CryptoKit
#else
import Crypto // swift-crypto on Linux
#endif
/// Canonical algorithm names. The spellings match the TypeScript union so the
/// same string works across every port. The case order matches the order the
/// React island renders.
public enum HashAlgorithm: String, CaseIterable, Sendable {
case sha1 = "SHA-1"
case sha256 = "SHA-256"
case sha384 = "SHA-384"
case sha512 = "SHA-512"
/// The algorithm selected when a caller passes an empty name — the
/// optional-parameter default the TypeScript reference declares.
public static let `default`: HashAlgorithm = .sha256
}
/// Errors thrown by `digest`. A concrete enum (not a string) so callers can
/// switch on the kind — the same shape as the Rust port's HashError.
public enum HashError: Error, Equatable, Sendable {
/// The algorithm string was not one of the supported names.
case unknownAlgorithm(String)
}
/// Lowercase hexadecimal encoding of a byte sequence — Foundation-free, the
/// Swift shape of the Rust port's `to_hex`.
private func toHex<S: Sequence>(_ bytes: S) -> String where S.Element == UInt8 {
let digits = Array("0123456789abcdef".unicodeScalars)
var out = String.UnicodeScalarView()
for byte in bytes {
out.append(digits[Int(byte >> 4)])
out.append(digits[Int(byte & 0x0f)])
}
return String(out)
}
/// Computes the named digest of `text` and returns it as lowercase hex.
///
/// `text` is UTF-8 encoded before hashing (String.utf8), so the result is
/// correct for arbitrary Unicode (emoji, accents, CJK). Passing an empty
/// `algorithm` selects SHA-256; passing an unknown name throws
/// `HashError.unknownAlgorithm`.
public func digest(_ algorithm: String, _ text: String) throws -> String {
// A String's utf8 view is the exact byte sequence crypto.subtle.digest
// receives from TextEncoder in the TypeScript reference.
let utf8 = Data(text.utf8)
switch algorithm {
case HashAlgorithm.sha1.rawValue:
// CryptoKit keeps SHA-1 behind the Insecure namespace (as does
// swift-crypto) to signal its broken collision resistance.
return toHex(Insecure.SHA1.hash(data: utf8))
case HashAlgorithm.sha256.rawValue:
return toHex(SHA256.hash(data: utf8))
case HashAlgorithm.sha384.rawValue:
return toHex(SHA384.hash(data: utf8))
case HashAlgorithm.sha512.rawValue:
return toHex(SHA512.hash(data: utf8))
case "":
return toHex(SHA256.hash(data: utf8))
default:
throw HashError.unknownAlgorithm(algorithm)
}
}
/// Computes every supported digest of `text`, returning (algorithm, hex)
/// pairs in the fixed order the React island renders — the Swift shape of
/// the Rust port's `hash_all` vector. Each hash is independent.
public func hashAll(_ text: String) -> [(name: String, hex: String)] {
// Every name is a canonical constant, so the unknown-name throw is
// statically unreachable; try! documents that rather than hiding it.
HashAlgorithm.allCases.map { alg in
(name: alg.rawValue, hex: try! digest(alg.rawValue, text))
}
}
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 →