Skip to content

Random Port Generator — Swift source

Generate one or many random TCP/UDP port numbers across registered, ephemeral, or the full range - optionally unique. Runs entirely in your browser with crypto-grade randomness.

This is the Swift implementation — the same logic the interactive tool runs, in a shareable, citable form.

// random-port-generator — Swift port: random/dynamic port picker.
//
// Display port of the CosmoDev Random Port Generator tool — same contract as
// cli/random-port-generator/random-port-generator.go (the live Go twin) and
// src/lib/random-port.ts (the canonical TypeScript implementation). An
// injectable rng makes generation deterministic (the TS test surface);
// invalid custom ranges throw a PortError, mirroring the TS throw. The
// default rng is Double.random(in:) — backed by SystemRandomNumberGenerator,
// a CSPRNG standing in for the TS crypto.getRandomValues default.

enum PortRange: String {
    case any, registered, ephemeral, custom
}

enum PortError: Error {
    /// assertRange() in the TS lib — the TS Error becomes a thrown error.
    case invalidRange(Int, Int)
}

/// PortOptions in the TS lib (count/unique cover randomPorts).
struct PortOptions {
    var range: PortRange = .registered // the TS default (registered ports)
    var min: Int? // custom-range lower bound
    var max: Int? // custom-range upper bound
    var count: Int = 1
    var unique: Bool = false
    var rng: (() -> Double)? // injectable random in [0, 1)
}

let ranges: [PortRange: (Int, Int)] = [
    .any: (1, 65_535),
    .registered: (1024, 49_151),
    .ephemeral: (49_152, 65_535),
]

/// defaultRng() in the TS lib: a CSPRNG draw in [0, 1).
func defaultRng() -> Double {
    Double.random(in: 0..<1) // SystemRandomNumberGenerator (CSPRNG)
}

/// resolveRange() in the TS lib.
func resolveRange(_ opts: PortOptions) -> (lo: Int, hi: Int) {
    if opts.range == .custom { return (opts.min ?? 1, opts.max ?? 65_535) }
    let (lo, hi) = ranges[opts.range]!
    return (lo, hi)
}

func assertRange(_ lo: Int, _ hi: Int) throws {
    if lo < 0 || lo > 65_535 || hi > 65_535 || lo > hi {
        throw PortError.invalidRange(lo, hi)
    }
}

/// randomPort() in the TS lib — Port() in the Go twin.
func randomPort(_ opts: PortOptions = PortOptions()) throws -> Int {
    let rng = opts.rng ?? defaultRng
    let r = resolveRange(opts)
    try assertRange(r.lo, r.hi)
    return r.lo + Int((rng() * Double(r.hi - r.lo + 1)).rounded(.down))
}

/// randomPorts() in the TS lib — Ports() in the Go twin.
func randomPorts(_ opts: PortOptions = PortOptions()) throws -> [Int] {
    let count = max(1, opts.count)
    let r = resolveRange(opts)
    try assertRange(r.lo, r.hi)
    if !opts.unique { return try (0..<count).map { _ in try randomPort(opts) } }

    // Fisher-Yates partial shuffle over the range to pick n unique ports.
    let capacity = r.hi - r.lo + 1
    let n = min(count, capacity)
    let rng = opts.rng ?? defaultRng
    var pool = Array(r.lo...r.hi)
    for i in 0..<n {
        let j = i + Int((rng() * Double(capacity - i)).rounded(.down))
        pool.swapAt(i, j)
    }
    return Array(pool.prefix(n))
}

// Vectors from src/lib/random-port.test.ts — a fixed rng pins every draw.
let zero: () -> Double = { 0.0 }
print("registered+0 = \(try randomPort(PortOptions(rng: zero)))") // 1024
print("any+0 = \(try randomPort(PortOptions(range: .any, rng: zero)))") // 1
print("ephemeral+0 = \(try randomPort(PortOptions(range: .ephemeral, rng: zero)))") // 49152
print("custom 8000-8000 @0.9 = \(try randomPort(PortOptions(range: .custom, min: 8000, max: 8000, rng: { 0.9 })))") // 8000
print("resolveRange(custom defaults) = \(resolveRange(PortOptions(range: .custom)))") // (lo: 1, hi: 65535)
print("count=5 @0 = \(try randomPorts(PortOptions(count: 5, rng: zero)))") // [1024 x5]
print("unique 3 in 1..10 @0.5 = \(try randomPorts(PortOptions(range: .custom, min: 1, max: 10, count: 3, unique: true, rng: { 0.5 })))") // [6, 1, 7]
print("unique 5 in 1..3 @0.5 = \(try randomPorts(PortOptions(range: .custom, min: 1, max: 3, count: 5, unique: true, rng: { 0.5 })))") // capped at capacity 3
let p = try randomPort() // default CSPRNG, registered range
print("default rng registered = \(p) (in 1024..49151)")
do {
    _ = try randomPort(PortOptions(range: .custom, min: 70000)) // min above 65535
    _ = try randomPort(PortOptions(range: .custom, min: 100, max: 50)) // min above max
} catch {
    print("invalid: \(error)")
}

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 →