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 →