Compare two version strings in Swift
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
Version strings look like ordinary strings, but naive lexicographic comparison gives the wrong result for values such as 1.2.10 and 1.2.9. In Swift, the safest solution is to compare version components numerically, either with a custom parser or, for simpler cases, with string comparison using the .numeric option.
Why Plain String Comparison Fails
A regular string comparison is character-based, not version-aware.
That can produce the wrong ordering because the comparison is driven by characters rather than by numeric segments.
Version strings need to be interpreted as sequences of integers separated by dots.
A Robust Component-by-Component Comparison
A reliable approach is to split each version string by . and compare the integer parts one by one.
This handles missing trailing components by treating them as zero, so 1, 1.0, and 1.0.0 compare as equal.
When .numeric Is Good Enough
Foundation also supports numeric-aware string comparison.
This is concise and works well for many simple version strings. But if you need precise control over semantics such as ignored suffixes, pre-release tags, or zero-padding rules, a custom parser is still better.
So a practical guideline is:
- '
.numericfor quick comparisons of straightforward dotted versions' - custom component parsing for precise application logic
Think About Pre-Release and Build Suffixes
Real version strings are often more complicated than major.minor.patch. You might see values like:
- '
1.2.0-beta' - '
2.0.1+build7' - '
3.1-rc1'
At that point, you need a policy. Do you support full semantic versioning or only numeric dotted releases?
If the app only stores numeric versions, keep the parser strict. If suffixes are expected, strip or interpret them deliberately instead of hoping string comparison will do the right thing.
A Comparable Version Type
If version comparison appears in many places, wrap it in a type instead of repeating ad hoc functions.
This makes call sites cleaner and gives you one place to evolve the comparison rules later.
Common Pitfalls
- Comparing version strings with ordinary
<and>operators. - Forgetting to pad missing trailing components such as
1versus1.0. - Relying on
.numericwhen the versions contain suffixes that need real parsing logic. - Mixing semantic-version rules with simple dotted-number rules without defining the expected behavior.
- Repeating custom comparison code in multiple places instead of centralizing it.
Summary
- Version strings should usually be compared numerically by component, not lexicographically.
- A custom split-and-compare function is the most reliable general solution in Swift.
- '
.compare(..., options: .numeric)is convenient for simpler dotted versions.' - Decide explicitly how to handle missing components and suffixes.
- If version comparison is common in the codebase, wrap it in a dedicated type.

