- Published on
How to Replace Characters in a String in Swift
- Authors

- Name
- Mick MacCallum
- @0x7fs
Replacing characters or substrings is a common string manipulation task in Swift. Whether you're formatting URLs, sanitizing user input, or transforming text data, Swift provides several approaches to accomplish this. This article covers the most effective methods for string replacement, from simple character substitution to complex pattern matching.
Using replacingOccurrences(of:with:)
The most straightforward way to replace characters or substrings is using the replacingOccurrences(of:with:) method. This method searches for all occurrences of a specified string and replaces them with another string.
Basic Usage
let aString = "This is my string"
let newString = aString.replacingOccurrences(of: " ", with: "+")
print(newString) // "This+is+my+string"
The method has two optional parameters that give you more control over the replacement:
let aString = "This is my string"
let newString = aString.replacingOccurrences(
of: " ",
with: "+",
options: .literal,
range: nil
)
String Comparison Options
The options parameter accepts a String.CompareOptions value that modifies how the search is performed:
let text = "Hello World, hello Swift"
// Case-insensitive replacement
let result1 = text.replacingOccurrences(
of: "hello",
with: "Hi",
options: .caseInsensitive
)
print(result1) // "Hi World, Hi Swift"
// Literal comparison (default)
let result2 = text.replacingOccurrences(
of: "hello",
with: "Hi",
options: .literal
)
print(result2) // "Hello World, Hi Swift"
Targeting a Specific Range
You can limit the replacement to a specific range within the string:
let text = "The cat sat on the mat"
let range = text.range(of: "cat")!.upperBound..<text.endIndex
let result = text.replacingOccurrences(
of: "at",
with: "OP",
range: range
)
print(result) // "The cat sOP on the mOP"
Using components(separatedBy:) and joined(separator:)
When you're replacing a separator character with another separator, you can split the string into components and rejoin them with a new separator. This approach is particularly clean for formatting operations:
let aString = "This is my string"
let components = aString.components(separatedBy: " ")
let newString = components.joined(separator: "+")
print(newString) // "This+is+my+string"
This method is especially useful when you need to perform additional operations on the components:
let csvData = "apple,banana,cherry,date"
let fruits = csvData.components(separatedBy: ",")
.map { $0.capitalized }
let formatted = fruits.joined(separator: " | ")
print(formatted) // "Apple | Banana | Cherry | Date"
Using map() for Character-Level Replacement
For a more functional programming approach that doesn't rely on Foundation's NSString APIs, you can use map() to transform individual characters:
let aString = "Some search text"
let replaced = String(aString.map {
$0 == " " ? "+" : $0
})
print(replaced) // "Some+search+text"
This approach gives you fine-grained control over the transformation logic:
let text = "Hello World 123"
// Replace digits with asterisks
let censored = String(text.map {
$0.isNumber ? "*" : $0
})
print(censored) // "Hello World ***"
// Replace vowels with underscores
let noVowels = String(text.map {
"aeiouAEIOU".contains($0) ? "_" : $0
})
print(noVowels) // "H_ll_ W_rld 123"
Using Regular Expressions
For complex pattern matching and replacement, regular expressions provide powerful capabilities. Swift 5.7 introduced the new Regex API, making pattern matching more type-safe and easier to use.
Using NSRegularExpression (Traditional Approach)
import Foundation
let text = "My phone number is 555-1234 and my zip is 12345"
let pattern = "\\d{3}-\\d{4}"
if let regex = try? NSRegularExpression(pattern: pattern) {
let result = regex.stringByReplacingMatches(
in: text,
range: NSRange(text.startIndex..., in: text),
withTemplate: "***-****"
)
print(result) // "My phone number is ***-**** and my zip is 12345"
}
Using Swift Regex (Swift 5.7+)
let text = "Contact me at user@example.com or admin@test.org"
let emailPattern = /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b/
let censored = text.replacing(emailPattern, with: "[REDACTED]")
print(censored) // "Contact me at [REDACTED] or [REDACTED]"
The new Regex API also supports more complex replacements with capture groups:
let text = "John Smith (age: 30), Jane Doe (age: 25)"
let pattern = /(\w+)\s+(\w+)\s+\(age:\s+\d+\)/
let result = text.replacing(pattern) { match in
"\(match.output.2), \(match.output.1)" // Reverse first and last name
}
print(result) // "Smith, John, Doe, Jane"
Using RangeReplaceableCollection Methods
For more control over the replacement process, you can use lower-level methods like replaceSubrange(_:with:):
var text = "Hello World"
if let range = text.range(of: "World") {
text.replaceSubrange(range, with: "Swift")
}
print(text) // "Hello Swift"
This approach is useful when you need to replace content while building up a string:
var markdown = "Check out [this link](url) and [another](url2)"
var mutableMarkdown = markdown
while let range = mutableMarkdown.range(of: "[", options: .literal) {
if let endRange = mutableMarkdown.range(of: "]", options: .literal, range: range.upperBound..<mutableMarkdown.endIndex) {
mutableMarkdown.replaceSubrange(range.lowerBound..<endRange.upperBound, with: "")
} else {
break
}
}
print(mutableMarkdown) // "Check out (url) and (url2)"
Replacing Multiple Different Characters
Sometimes you need to replace multiple different characters in a single pass. Here are several approaches:
Using reduce()
let text = "Hello, World! How are you?"
let unwantedChars = [",", "!", "?"]
let cleaned = unwantedChars.reduce(text) { result, char in
result.replacingOccurrences(of: char, with: "")
}
print(cleaned) // "Hello World How are you"
Using Character Sets
import Foundation
let text = "Hello123World456"
let digits = CharacterSet.decimalDigits
let result = String(text.unicodeScalars.map {
digits.contains($0) ? Character("*") : Character($0)
})
print(result) // "Hello***World***"
Creating a Replacement Dictionary
let text = "Hello World"
let replacements: [Character: Character] = [
"H": "h",
"W": "w",
"o": "0",
"l": "1"
]
let result = String(text.map { replacements[$0] ?? $0 })
print(result) // "he110 w0r1d"
Performance Considerations
Different replacement methods have different performance characteristics:
- replacingOccurrences(of:with:) - Best for simple, one-time replacements of known substrings
- components/joined - Efficient when you're replacing a single separator character
- map() - Good for character-level transformations with custom logic
- Regular expressions - Best for complex pattern matching, but with overhead for compilation
- replaceSubrange - Most efficient for targeted replacements when you already know the range
For multiple replacements on the same string, consider which approach minimizes the number of passes through the string.
Common Use Cases
URL Encoding Spaces
let searchQuery = "swift string methods"
let encoded = searchQuery.replacingOccurrences(of: " ", with: "+")
// Or use proper URL encoding
let properlyEncoded = searchQuery.addingPercentEncoding(
withAllowedCharacters: .urlQueryAllowed
)
Sanitizing User Input
func sanitize(_ input: String) -> String {
let dangerous = ["<", ">", "&", "\"", "'"]
let safe = ["<", ">", "&", """, "'"]
var result = input
for (index, char) in dangerous.enumerated() {
result = result.replacingOccurrences(of: char, with: safe[index])
}
return result
}
let userInput = "<script>alert('XSS')</script>"
print(sanitize(userInput))
// <script>alert('XSS')</script>
Formatting Phone Numbers
let phoneDigits = "5551234567"
let components = phoneDigits.components(separatedBy: CharacterSet.decimalDigits.inverted)
let digitsOnly = components.joined()
if digitsOnly.count == 10 {
let areaCode = digitsOnly.prefix(3)
let prefix = digitsOnly.dropFirst(3).prefix(3)
let suffix = digitsOnly.suffix(4)
let formatted = "(\(areaCode)) \(prefix)-\(suffix)"
print(formatted) // "(555) 123-4567"
}
Conclusion
Swift provides multiple ways to replace characters and substrings, each suited for different scenarios:
- Use
replacingOccurrences(of:with:)for straightforward substring replacement - Use
components/joinedwhen replacing separators - Use
map()for functional character transformations - Use regular expressions for complex pattern matching
- Use
replaceSubrangefor precise, range-based replacements
Choose the method that best fits your use case, considering both code clarity and performance requirements.
Continue Learning
How to get the name of an emoji in Swift
Learn how to use kCFStringTransformToUnicodeName to get the system name for any emoji.
Accessing Environment Variables in Swift
Learn how to access and configure environment variables in Swift applications for configuration management, API keys, and feature toggles.
Aligning Text in SwiftUI
Learn how to align single and multiline text in SwiftUI.
