How do you use String.substringWithRange? or, how do Ranges work in Swift?
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
Swift string slicing feels unusual at first because strings are not indexed by plain integers. That design exists to keep string operations correct for Unicode, which means working with substrings requires String.Index values and ranges built from those indices.
Why Integer Indexing Does Not Work
In Swift, a character is not guaranteed to occupy one code unit. Emojis, accented characters, and composed Unicode sequences can take variable storage, so String cannot safely promise that character number 5 lives at byte or code-unit offset 5.
That is why Swift uses String.Index instead of integer subscripts.
The slice here is a Substring, not a full String. That is normal.
Build Ranges with String.Index
To extract a middle portion of a string, compute both the start and end indices and then create a range.
This is the modern equivalent of the older substring(with:) style. In newer Swift, subscripting with a range is the normal approach.
If you need an actual String value instead of a Substring, wrap it explicitly:
Understand the Common Range Forms
Swift uses a few range shapes regularly:
- '
a..<bis half-open and excludesb' - '
a...bis closed and includesb' - '
a...and...bare one-sided ranges'
With strings, half-open ranges are often the most convenient because they match slice boundaries cleanly.
These forms also work with arrays and other Swift collections, which is why understanding ranges pays off beyond string handling.
substringWithRange Is Historical Context
Older Swift versions exposed methods such as substring(with:). If you encounter that in legacy code or old answers, the conceptual idea is still valid, but modern Swift code uses collection slicing syntax instead.
So if the question mentions substringWithRange, the modern answer is usually: build String.Index values, create a range, then slice with subscripting.
Be Careful with Offsets
Using index(_:offsetBy:) assumes the offset is valid. If you move past the end of the string, your code will crash. When the offset may be uncertain, validate the bounds or use APIs that limit movement safely.
This matters when parsing user input or external text where string length is not under your control.
A helpful habit is to keep slicing logic near the string it belongs to instead of passing raw offsets around the codebase. Once integer offsets become detached from the original string, it becomes much easier to make invalid-index assumptions later.
Common Pitfalls
Trying to subscript a String with an integer is the classic beginner mistake. Swift strings do not work that way.
Forgetting that slices return Substring instead of String can lead to type mismatches in later code.
Assuming one visible character always equals one simple offset can create Unicode bugs. Use String.Index and collection APIs instead of byte-level assumptions.
Summary
- Swift strings use
String.Index, not integer subscripts. - Build ranges from valid string indices and slice with subscripting.
- Expect a
Substringresult and convert toStringonly when you need ownership. - Treat old
substringWithRangeexamples as historical syntax, not current best practice.

