Skip to content

Compound Interest Calculator — Swift source

Project investment growth with any compounding frequency, scheduled contributions and inflation adjustment. Yearly breakdown, growth curve and CSV export — runs entirely in your browser.

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

// compound-interest-calculator — compound growth projection.
//
// Language: Swift (5.9, standard library only; `pow` from the platform's
// Darwin/Glibc math overlay)
// Source:   CosmoDev polyglot showcase port of the Compound Interest
//           Calculator tool, ported from src/lib/interest.ts (the canonical
//           TypeScript implementation).
// Tool:     https://dev.cosmolabs.org/tools/compound-interest-calculator
// License:  display source — part of CosmoDev's polyglot tool pages.
//
// Display snippet: ports the core project() math from the TypeScript lib.
// The full projection (contributions, inflation, per-year rows) lives in
// the TypeScript/Go sources.
//
// Semantics: monthly simulation with the monthly rate set to the exact
// equivalent of the stated compounding frequency — (1 + r/n)^(n/12) - 1 —
// so the effective annual rate is preserved for every supported frequency.
// Returns the final balance rounded to 2 decimals, or nil on invalid
// input (negative principal, negative years, unsupported frequency,
// non-finite values).

import Foundation

enum CompoundInterestCalculator {
    static let validCompounds: Set<Int> = [1, 2, 4, 12, 365]

    /// Round to 2 decimals, half-up (mirrors the TS `Math.round`).
    static func round2(_ n: Double) -> Double {
        (n * 100 + 0.5).rounded(.down) / 100
    }

    static func project(principal: Double, ratePct: Double, years: Int, compoundsPerYear: Int) -> Double? {
        guard [principal, ratePct].allSatisfy({ $0.isFinite }),
              principal >= 0,
              years >= 0,
              validCompounds.contains(compoundsPerYear)
        else { return nil }

        let n = Double(compoundsPerYear)
        let monthlyRate = pow(1 + ratePct / 100 / n, n / 12) - 1

        var balance = principal
        for _ in 0..<(years * 12) {
            balance *= 1 + monthlyRate
        }
        return round2(balance)
    }
}

// Example:
//   project(principal: 10000, ratePct: 5, years: 10, compoundsPerYear: 12) -> 16470.09
let result = CompoundInterestCalculator.project(principal: 10_000, ratePct: 5, years: 10, compoundsPerYear: 12)
precondition(result == 16_470.09, "expected 16470.09, got \(String(describing: result))")

// Invalid inputs are rejected, never clamped.
precondition(CompoundInterestCalculator.project(principal: -1, ratePct: 5, years: 10, compoundsPerYear: 12) == nil, "negative principal")
precondition(CompoundInterestCalculator.project(principal: 10_000, ratePct: 5, years: -1, compoundsPerYear: 12) == nil, "negative years")
precondition(CompoundInterestCalculator.project(principal: 10_000, ratePct: 5, years: 10, compoundsPerYear: 6) == nil, "unsupported frequency")
precondition(CompoundInterestCalculator.project(principal: .infinity, ratePct: 5, years: 10, compoundsPerYear: 12) == nil, "non-finite value")

print("ok")

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 →