# Swift String Methods: contains, hasPrefix, and More

Source: https://devdocs10.com/articles/swift/swift-string-methods  
By Murat Bo · Updated 2026-10-17 · DevDocs10 (https://devdocs10.com)

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&rsquo;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

### 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.

```swift
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.

```swift
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.

```swift
let message = "Your order has shipped"
let didShip = message.contains("shipped")

print(didShip)
```

Output (tested with Swift 6.4):

```text
true
```

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.

```swift
let input = "/help"

if input.hasPrefix("/") {
    print("This looks like a command.")
}
```

Output (tested with Swift 6.4):

```text
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.

```swift
let fileName = "report.pdf"

if fileName.hasSuffix(".pdf") {
    print("This is a PDF file.")
}
```

Output (tested with Swift 6.4):

```text
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.

```swift
let userInput = "SWIFT"
let keyword = "swift"

if userInput.lowercased() == keyword.lowercased() {
    print("The values match.")
}
```

Output (tested with Swift 6.4):

```text
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.

```swift
import Foundation

let template = "Hello, NAME!"
let finalMessage = template.replacingOccurrences(of: "NAME", with: "Maya")

print(finalMessage)
```

Output (tested with Swift 6.4):

```text
Hello, Maya!
```

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.

```swift
let title = "Learn Swift"
let found = title.contains("swift")

print(found)
```

Output (tested with Swift 6.4):

```text
false
```

**Fix:** Normalize both values to the same case before comparing when a simple case-insensitive check is appropriate.

```swift
let title = "Learn Swift"
let found = title.lowercased().contains("swift".lowercased())

print(found)
```

Output (tested with Swift 6.4):

```text
true
```

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.

```swift
var text = "red, green, blue"
text.replacingOccurrences(of: "green", with: "yellow")

print(text)
```

Output (tested with Swift 6.4):

```text
red, green, blue
```

**Fix:** Assign the returned string back to a variable or store it in a new constant.

```swift
import Foundation

var text = "red, green, blue"
text = text.replacingOccurrences(of: "green", with: "yellow")

print(text)
```

Output (tested with Swift 6.4):

```text
red, yellow, blue
```

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.

```swift
let imageName = "photo.PNG"

if imageName.hasSuffix(".png") {
    print("PNG image")
}
```

**Fix:** Normalize the string before checking the suffix when case should not matter.

```swift
let imageName = "photo.PNG"

if imageName.lowercased().hasSuffix(".png") {
    print("PNG image")
}
```

Output (tested with Swift 6.4):

```text
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'`.

```swift
let searchText: String? = "swift guide"

if searchText.contains("swift") {
    print("Match found")
}
```

**Fix:** Unwrap the optional first with `if let` before calling String methods.

```swift
let searchText: String? = "swift guide"

if let searchText = searchText, searchText.contains("swift") {
    print("Match found")
}
```

Output (tested with Swift 6.4):

```text
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.

```swift
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.

```swift
let path = "/api/users"

// More specific and clearer than a general substring search
if path.hasPrefix("/api/") {
    print("API route detected")
}
```

Output (tested with Swift 6.4):

```text
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.

```swift
let email = "  USER@Example.COM  "
let cleanEmail = email
    .trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
    .lowercased()

print(cleanEmail)
```

Output (tested with Swift 6.4):

```text
user@example.com
```

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.

```swift
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)")
    }
}
```

Output (tested with Swift 6.4):

```text
Image file: profile.png
Other file: notes.txt
Image file: banner.jpg
Other file: archive.zip
```

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:)`.

```swift
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)")
```

Output (tested with Swift 6.4):

```text
Final username: Admin_User
Starts with admin: true
```

## 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.
