In this blog post, I will explain the three built-in Swift options for case transformations on Strings, their locale-aware variants, and what support is offered in SwiftUI.
I will also explain why capitalized
is equivalent to titlecase
related functions in other programming languages and why it should not be mixed up with "Title Case" in natural languages.
This blog post is NOT about the various case styles (camelCase
, PascalCase
, snake_case
or kebab-case
) for naming variables.
Let's get started.
A natural language has a variety of case styles. Here are common case styles in the English language.
Case style | Example |
Sentence case | The quick brown fox jumps over the lazy dog |
All uppercase / All caps | THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG |
All lowercase | the quick brown fox jumps over the lazy dog |
Start case | The Quick Brown Fox Jumps Over The Lazy Dog |
Title Case | The Quick Brown Fox Jumps over the Lazy Dog |
Programming languages may have built-in support for some but not all of these cases. In Swift we have:
Swift API | Case style |
uppercased() | All uppercase / All caps |
lowercased() | All lowercase |
capitalized | Start case |
let example = "The Quick Brown Fox Jumps over the Lazy Dog"
example.lowercased() // the quick brown fox jumps over the lazy dog
example.uppercased() // THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG
example.capitalized // The Quick Brown Fox Jumps Over The Lazy Dog
Let's ignore the confusing fact that lowercased()
and uppercased()
are functions and capitalized
is a property.
What I do want to address is the confusion that other programming languages may use different names for Swift's capitalized. Kotlin has titlecase. VBA has something called "proper case".
In text processing, title case usually involves the capitalization of all words irrespective of their part of speech.
What programmers call "title case" (or in Swift: capitalized
) is known as Start Case by authors which is a simplified variant.
As software programmers, we mean the mapping that is applied to the initial character in a word according to the mapping rules defined in the Unicode Standard.
The titlecase mapping in Unicode differs from the uppercase mapping in that a number of characters require special handling. These are chiefly ligatures and digraphs such as 'fl', 'dz', and 'lj', plus a number of polytonic Greek characters. For example, U+01C7 (LJ) maps to U+01C8 (Lj) rather than to U+01C9 (lj).
By the way, the topic of case mapping is so complicated and important that it has its own FAQ.
Some case mappings depend on language or locale. Therefore Swift provides variants for case transformation considering the locale.
"i".uppercased(with: Locale(identifier: "tr_TR")) // returns "İ"
"ı".uppercased(with: Locale(identifier: "tr_TR")) // returns "I"
For convenience reasons Swift also provides APIs to get the case transformation in the current locale
When working with text that’s presented to the user, use locale-aware APIs
Another important fact to know is that case transformations aren’t guaranteed to be symmetrical or to produce strings of the same lengths as the originals.
"Straße".count // 6
"Straße".uppercased().count // 7
"Straße".uppercased() // STRASSE
Let's talk about SwiftUI.
You have support for lowercase and uppercase transformation with textCase(_:) introduced with iOS 14. But no capitalized option.
struct ContentView: View {
var example = "heLLo woRLd"
var body: some View {
VStack {
Text(example) // heLLo woRLd
Text(example).textCase(.lowercase) // hello world
Text(example).textCase(.uppercase) // HELLO WORLD
}
.padding()
}
}
You can build a custom Swift property wrapper as explained by John Sundell.
@propertyWrapper struct Capitalized {
var wrappedValue: String {
didSet { wrappedValue = wrappedValue.capitalized }
}
init(wrappedValue: String) {
self.wrappedValue = wrappedValue.capitalized
}
}
Then use it in your SwiftUI view.
struct ContentView: View {
@Capitalized
var example = "heLLo woRLd"
var body: some View {
VStack {
Text(example) // Hello World
}
.padding()
}
}
You can also build a more complicated Swift property wrapper that accepts the case for transformation as shown by Andy Ibanez.