Swift String Methods: contains, hasPrefix, and More
Swift strings come with many built-in methods that help you search text, check prefixes and suffixes, change letter case, replace parts of a string, and clean up user input. In this article, you will learn how common Swift String methods such as contains(), hasPrefix(), hasSuffix(), lowercased(), and replacingOccurrences(of:with:) work, when to use them, and what mistakes to avoid.
Quick answer: Swift String methods let you inspect and transform text without writing low-level character loops yourself. Use contains() to look for text, hasPrefix() and hasSuffix() to test the start or end of a string, and methods like lowercased() or replacingOccurrences(of:with:) to create a modified copy.
Difficulty: Beginner
Helpful to know first: basic Swift syntax, how variables store values, and that String is a value type used to hold text.
1. What Is String Methods?
String methods are built-in functions provided by Swift’s String type. You call them on a string value to ask questions about the text or to create a changed version of it.
- contains() checks whether a string includes a given piece of text.
- hasPrefix() checks whether a string starts with specific text.
- hasSuffix() checks whether a string ends with specific text.
- lowercased() and uppercased() return new strings with changed letter case.
- replacingOccurrences(of:with:) returns a new string with text replaced.
- Most String methods do not change the original string in place. They return a new value.
These methods solve a very common problem: working with user input, file names, search text, messages, URLs, and formatted output safely and clearly.
A common beginner confusion is expecting String methods to mutate the original value automatically. In Swift, many string operations return a new string, so you usually need to assign the result to a variable or constant.
2. Why String Methods Matters
Strings appear almost everywhere in Swift programs. You use them when validating forms, checking commands, filtering search results, formatting display text, or cleaning imported data.
Without these methods, even simple tasks would require manual loops and character-by-character logic. Built-in methods make code shorter, easier to read, and less error-prone.
- Use contains() for search-like checks.
- Use hasPrefix() for commands, route matching, or file path checks.
- Use hasSuffix() for file extensions and ending patterns.
- Use case-conversion methods when comparisons should ignore capitalization.
- Use replacement methods when normalizing data or generating cleaner output.
You should not use these methods blindly, though. String matching is case-sensitive by default, and a method returning true does not always mean the text is valid in a business-rule sense.
3. Basic Syntax or Core Idea
Calling a method on a String
In Swift, you call a String method with dot syntax. The string value comes first, then the method name and any arguments.
let title = "Swift String Guide"
let hasSwift = title.contains("Swift")
let startsWithSwift = title.hasPrefix("Swift")
let endsWithGuide = title.hasSuffix("Guide")This code asks three questions about the same string. Each method returns a Bool, so the result is either true or false.
Methods that return a changed string
Some methods return a new String instead of a Boolean.
let name = " Alice Smith "
let cleanName = name.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
let lowercaseName = cleanName.lowercased()The original name does not change. Instead, each method call produces a new string value that you can store and use later.
4. Step-by-Step Examples
Example 1: Using contains() to search for text
Use contains() when you want to know whether a string includes a substring anywhere inside it.
let message = "Your order has shipped"
let didShip = message.contains("shipped")
print(didShip)This prints true because the word shipped appears in the message.
Example 2: Using hasPrefix() for command checks
hasPrefix() is useful when input must begin with a known pattern, such as a slash command.
let input = "/help"
if input.hasPrefix("/") {
print("This looks like a command.")
}This checks only the beginning of the string. It does not search the entire value like contains() does.
Example 3: Using hasSuffix() for file extensions
hasSuffix() is often the simplest way to test whether a filename ends with a known extension.
let fileName = "report.pdf"
if fileName.hasSuffix(".pdf") {
print("This is a PDF file.")
}This works well for simple extension checks, though you may still want stronger validation in production code.
Example 4: Using lowercased() for case-insensitive comparison
String matching in Swift is case-sensitive by default. If you want a basic case-insensitive check, normalize both strings first.
let userInput = "SWIFT"
let keyword = "swift"
if userInput.lowercased() == keyword.lowercased() {
print("The values match.")
}This is a common and practical beginner pattern for case-insensitive matching.
Example 5: Replacing text with replacingOccurrences(of:with:)
Use this method when you want to generate a modified copy of a string.
let template = "Hello, NAME!"
let finalMessage = template.replacingOccurrences(of: "NAME", with: "Maya")
print(finalMessage)This prints Hello, Maya!. The original template string remains unchanged.
5. Practical Use Cases
- Checking whether a search field contains a product name or keyword.
- Testing whether a route, command, or URL path starts with a known prefix.
- Validating that a filename ends in .jpg, .png, or .json.
- Cleaning form input by trimming spaces and normalizing case before comparison.
- Replacing placeholders in message templates, emails, or generated reports.
- Filtering user-submitted text for banned words or required phrases.
- Detecting tags, categories, or status labels embedded in text.
6. Common Mistakes
Mistake 1: Expecting contains() to ignore letter case
A very common mistake is assuming that contains() treats uppercase and lowercase letters as the same.
Problem: This code searches for lowercase text inside a string that contains uppercase letters, so the result is false even though the words look similar to a human reader.
let title = "Learn Swift"
let found = title.contains("swift")
print(found)Fix: Normalize both values to the same case before comparing when a simple case-insensitive check is appropriate.
let title = "Learn Swift"
let found = title.lowercased().contains("swift".lowercased())
print(found)The corrected version works because both strings are converted to lowercase before the search.
Mistake 2: Thinking a method changes the original string automatically
Many String methods return a new value instead of mutating the existing one.
Problem: This code calls replacingOccurrences(of:with:) but ignores the returned string, so the original value is still unchanged.
var text = "red, green, blue"
text.replacingOccurrences(of: "green", with: "yellow")
print(text)Fix: Assign the returned string back to a variable or store it in a new constant.
var text = "red, green, blue"
text = text.replacingOccurrences(of: "green", with: "yellow")
print(text)The corrected version works because the new string replaces the old value stored in text.
Mistake 3: Using hasSuffix() on mixed-case file extensions
File names and user input are often inconsistent in capitalization.
Problem: This check fails for uppercase file extensions, even though the file is still a PNG image.
let imageName = "photo.PNG"
if imageName.hasSuffix(".png") {
print("PNG image")
}Fix: Normalize the string before checking the suffix when case should not matter.
let imageName = "photo.PNG"
if imageName.lowercased().hasSuffix(".png") {
print("PNG image")
}The corrected version works because the comparison is performed on a lowercase copy of the filename.
Mistake 4: Calling String methods on an optional String without unwrapping
You often receive text as an optional, especially from input parsing or dictionary lookups.
Problem: This code tries to call a String method on String?, which can produce the compile-time error Value of optional type 'String?' must be unwrapped to refer to member 'contains' of wrapped base type 'String'.
let searchText: String? = "swift guide"
if searchText.contains("swift") {
print("Match found")
}Fix: Unwrap the optional first with if let before calling String methods.
let searchText: String? = "swift guide"
if let searchText = searchText, searchText.contains("swift") {
print("Match found")
}The corrected version works because the method is called only after the optional has been safely unwrapped.
7. Best Practices
Practice 1: Normalize text before user-facing comparisons
User input is often inconsistent. A less reliable approach is comparing raw strings directly when capitalization should not matter.
let input = "Admin"
let allowedRole = "admin"
// Preferred for simple case-insensitive checks
let matches = input.lowercased() == allowedRole.lowercased()This makes simple text validation more predictable for real users.
Practice 2: Use the most specific method for the job
If you only care about the start or end of a string, prefer hasPrefix() or hasSuffix() over broader searches.
let path = "/api/users"
// More specific and clearer than a general substring search
if path.hasPrefix("/api/") {
print("API route detected")
}This improves readability because future readers immediately understand what part of the string matters.
Practice 3: Store transformed results when you will reuse them
If you need multiple comparisons, avoid repeating the same transformation many times.
let email = " [email protected] "
let cleanEmail = email
.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
.lowercased()
print(cleanEmail)This keeps the code simpler and avoids repeating the same cleanup logic in multiple places.
8. Limitations and Edge Cases
- Methods like contains(), hasPrefix(), and hasSuffix() are case-sensitive unless you normalize the text yourself.
- Many String methods return a new value instead of mutating the existing string, which can surprise beginners.
- A simple lowercase comparison is practical, but language-specific text rules can be more complex in international apps.
- hasSuffix(".png") is useful for quick checks, but it is not a full file validation strategy.
- Whitespace matters. A string like " admin " does not match "admin" unless you trim it first.
- Optional strings must be unwrapped before you call String methods on them.
- Some text-processing tasks are better handled by APIs such as splitting, regular expressions, or character sets when basic methods are not enough.
9. Practical Mini Project
This small program cleans a list of filenames, checks for image files, and prints a normalized result. It combines trimming, case normalization, suffix checks, and replacement.
let rawFiles = [
" profile.PNG ",
"notes.txt",
"banner.jpg",
"archive.ZIP"
]
for file in rawFiles {
let cleanFile = file
.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
.lowercased()
let displayName = cleanFile.replacingOccurrences(of: "_", with: " ")
if cleanFile.hasSuffix(".png") || cleanFile.hasSuffix(".jpg") {
print("Image file: \(displayName)")
} else {
print("Other file: \(displayName)")
}
}This example shows a realistic workflow: normalize incoming text, perform consistent checks, and produce output based on the result. These exact steps are common in file handling, imported data cleanup, and search filtering.
10. Key Points
- contains() checks whether a string includes a substring anywhere inside it.
- hasPrefix() checks the beginning of a string, while hasSuffix() checks the end.
- lowercased() and uppercased() return new strings with changed case.
- replacingOccurrences(of:with:) creates a modified copy and does not change the original automatically.
- Swift String matching is case-sensitive unless you normalize the values first.
- Optional strings must be unwrapped before calling String methods on them.
- Choosing the most specific method usually makes code clearer and easier to maintain.
11. Practice Exercise
Try building a small text checker that validates a username string before using it.
- Create a string called username with extra spaces around it.
- Trim leading and trailing whitespace.
- Check whether the cleaned username starts with "admin" in a case-insensitive way.
- Replace any spaces inside the cleaned username with underscores.
- Print the final cleaned username and whether it starts with "admin".
Expected output: a cleaned username and a Boolean result such as true or false.
Hint: Use trimmingCharacters(in:), lowercased(), hasPrefix(), and replacingOccurrences(of:with:).
let username = " Admin User "
let cleanUsername = username
.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
let startsWithAdmin = cleanUsername.lowercased().hasPrefix("admin")
let finalUsername = cleanUsername.replacingOccurrences(of: " ", with: "_")
print("Final username: \(finalUsername)")
print("Starts with admin: \(startsWithAdmin)")12. Final Summary
Swift String methods make everyday text processing much easier. Instead of manually looping through characters, you can use methods like contains(), hasPrefix(), and hasSuffix() to inspect strings quickly, and methods like lowercased(), trimmingCharacters(in:), and replacingOccurrences(of:with:) to create cleaner, more predictable values.
The most important beginner lessons are that String matching is usually case-sensitive, many methods return a new string instead of mutating the original one, and optional strings must be unwrapped before use. Once you are comfortable with these basics, a strong next step is learning Swift string indexing, splitting strings, and working with substrings for more advanced text processing.