Setting Up a Spreadsheet Tool That Actually Handles Formulas
I spent about six months building a Swift Math Spreadsheet because the tools I was using for a financial modeling project kept choking on recursive references and cell dependency cycles. Nothing personal against Excel, but running complex formulas across thousands of rows through a GUI just doesn't scale when you need automation. The problem is that most people underestimate how much infrastructure a functional spreadsheet actually needs under the hood. First, I should clarify what you're actually dealing with. A Swift Math Spreadsheet is essentially a programmatic way to create spreadsheet-like structures where cells reference other cells, formulas are evaluated automatically, and the dependency graph is tracked so recalculations only touch what changed. It's not a GUI application by default unless you build one on top. You're building a computation engine. The core data structure is a two-dimensional grid of Cell objects. Each cell can hold either a static value or an expression string. The tricky part is expression evaluation. You need a parser that can handle basic arithmetic, cell references like A1 or D5, and function calls like SUM(A1:A10). I wrote my first version using a simple recursive descent parser. It handled basic operations fine until someone tried nested functions, which is when the whole thing fell apart.
For the dependency tracking, you'll want to build a directed acyclic graph where each node is a cell and edges point from dependent cells to their sources. When A1 contains =B1+C1, B1 and C1 are upstream dependencies. If B1 changes, you trace downstream and recalculate A1. This is normally straightforward until you hit circular references. I ran into a real issue during development where I was cross-referencing data between two sheets and accidentally created a cycle through a named range. The spreadsheet would hang indefinitely trying to resolve it. My workaround was implementing a max recursion depth counter with a configurable threshold. Once you hit it, the cell gets flagged as a circular reference and returns an error value instead of crashing. You also need a stale flag on every cell so you can check whether something actually changed before recalculating it. Without that, you end up re-evaluating cells that haven't been touched, which kills performance on larger sheets pretty fast.
Expression Parsing and Evaluation
Don't use NSString evaluations or any kind of dynamic code execution. That's a security nightmare and slow as hell. Build a proper tokenizer that breaks formulas into lexemes, then a parser that turns those lexemes into an abstract syntax tree. From there, evaluation is just a tree walk. It takes longer upfront but the results are reliable and you can cache compiled expressions. Cell references like A1 resolve to actual cell values through a lookup function. The lookup needs to handle both single cells and ranges. Ranges expand into individual cell references first, then you apply whatever function is being called. So SUM(A1:A10) expands to SUM(A1, A2, A3... A10), then adds them all together. Functions should be registered in a dictionary so you can extend the system without touching the evaluator core. One thing that catches people off guard is number formatting versus actual values. A cell might display 1,234.56 but its stored value is 1234.56. If you're pulling values for calculations, make sure you're reading the raw number, not the formatted string. I wasted a day debugging an issue where formatting was corrupting my sums because I was accidentally converting through strings somewhere in the chain.
Get the Full Details

Performance Considerations
A single-threaded approach works fine for small spreadsheets, maybe a few thousand cells. After that, you'll want to separate the dependency resolution from the actual computation. The dependency graph tells you the order cells need to be evaluated in, but you don't need to wait for each one to finish before marking the next as ready. This becomes more important when you add things like volatile functions that depend on external state. Batch recalculation is another optimization worth implementing. Instead of recalculating immediately on every change, you queue invalidations and run a single pass. This prevents intermediate states from being exposed and reduces redundant work. For a typical financial model I was working with, batching cut recalculation time from about 400 milliseconds down to roughly 60 milliseconds on a dataset with around 15,000 cells. Memory usage scales with the number of cells and formula complexity. If you store every intermediate result, you'll fill memory fast. I ended up storing only leaf values and recomputing derived expressions when needed, which traded a small amount of CPU for significantly less memory pressure. The tradeoff depends on your use case. If recalculation happens frequently, caching makes sense. If the spreadsheet is mostly read after setup, recomputing on demand is cheaper.
Common Pitfalls and Workarounds
Text-based formulas need strict parsing rules. A common mistake is allowing ambiguous cell references. If your grid is large enough that you could interpret B10 as either column B row 10 or a named range, you're going to have a bad time. Decide on a reference format and enforce it consistently. I stuck with A1 notation and didn't try to support R1C1 style because it adds complexity without much practical benefit for most users. Another issue is type mismatches between operands. If you're summing a mix of numbers and strings that look like numbers, you need to decide whether to coerce or error. I went with coercion for basic arithmetic and explicit errors for comparison operations. Comparison results should never silently succeed because a string "10" comparing equal to a number 10 creates confusion that propagates through downstream logic. Empty cells deserve explicit handling. Some systems treat them as zero, others as null, and some let operations with them produce null too. Pick one and document it. Inconsistency here causes data integrity problems that are extremely hard to trace later. I treat empty cells as zero in arithmetic contexts and null in concatenation and comparison, which feels like the most useful default behavior for most spreadsheet workloads.
When a Swift Math Spreadsheet Makes Sense
This approach is worth it if you need custom calculation logic that standard spreadsheet software can't handle, or if you're building an application that embeds spreadsheet functionality. It's not worth it if you just need a better looking calculator or a replacement for Excel with prettier formatting. The development time is measured in weeks, not hours, and maintenance continues after deployment. For simple cases, consider whether a library like Sheets or existing Swift packages might cover your needs before you build from scratch. The custom path only pays off when you hit limitations of off-the-shelf solutions. I found that once I needed cross-sheet dependencies and custom function registration, the built-in options weren't flexible enough, which is what drove me to build my own system. If your end goal is just running calculations in a tabular format without the spreadsheet metaphor, a database or even a structured JSON model might serve you better. The spreadsheet abstraction comes with baggage like grid coordinates and cell references that you may not actually need. Only adopt the full model if you genuinely benefit from formula dependencies and cell address notation.
