How to Compare Strings for Equality in Swift

How to Compare Strings for Equality in Swift

Written by

in

Quick answer: In Swift, replace Objective-C’s isEqualToString: with the standard equality operator ==. Swift strings conform to Equatable, so stringA == stringB evaluates canonical Unicode equivalence directly without pointer checks or Foundation bridging methods.

Why this happens

In Objective-C, string variables are pointers to NSString instances on the heap. Using the equality operator == in Objective-C compares pointer addresses rather than the underlying text contents. Objective-C developers relied on [stringA isEqualToString:stringB] or [stringA isEqual:stringB] to verify value equality.

In Swift, String is a first-class value type represented as a struct, not a reference type. Swift’s String conforms to the Equatable protocol. The standard == operator in Swift compares the actual sequence of extended grapheme clusters rather than reference addresses.

A frequent stumbling block when migrating UIKit code from Objective-C involves accessing the string property itself. In UIKit, controls like UITextField hold their values in an optional string property (textField.text). Comparing the UITextField instance directly against a string literal causes compiler errors or runtime type mismatches because a view object cannot equal a string value.

How to fix it

1. Use the == operator for standard string comparison

To check whether two strings represent the same text, compare them with ==. Swift evaluates Unicode canonical equivalence, meaning characters composed in different Unicode forms evaluate as equal if they produce identical linguistic meaning and appearance.

let inputUsername = "developer"
let storedUsername = "developer"

if inputUsername == storedUsername {
    print("Usernames match")
} else {
    print("Usernames do not match")
}

This completely replaces isEqualToString: across all native Swift code.

2. Access the .text property on UIKit controls

When reading user input from controls like UITextField or UILabel, you must read the .text property rather than checking the control directly. Because .text returns String? (an optional), provide a fallback or unwrap the value before evaluation.

import UIKit

class LoginViewController: UIViewController {
    @IBOutlet weak var usernameField: UITextField!
    @IBOutlet weak var passwordField: UITextField!

    @IBAction func loginButtonTapped(_ sender: UIButton) {
        let username = usernameField.text ?? ""
        let password = passwordField.text ?? ""

        if username == "" || password == "" {
            print("Sign in failed. Required fields are empty.")
            return
        }

        print("Submitting credentials...")
    }
}

3. Use .isEmpty to check for empty strings

Comparing a string to an empty string literal (string == "") works, but using the .isEmpty property is faster, clearer, and idiomatic Swift. It checks whether the string’s start index equals its end index without allocating or scanning a literal.

guard let username = usernameField.text, !username.isEmpty,
      let password = passwordField.passwordText, !password.isEmpty else {
    print("Both fields must contain text.")
    return
}

If you want to treat whitespace-only inputs (like spaces or tabs) as empty, trim the string using Foundation methods:

let trimmedUsername = usernameField.text?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""

if trimmedUsername.isEmpty {
    print("Please enter a valid username.")
}

4. Perform case-insensitive or locale-aware comparisons

When comparing identifiers, search terms, or user-entered emails where casing should be ignored, use caseInsensitiveCompare(_:) or compare(_:options:).

let emailEntry = "User@Example.com"
let registeredEmail = "user@example.com"

if emailEntry.caseInsensitiveCompare(registeredEmail) == .orderedSame {
    print("Email addresses match.")
}

For advanced localized comparisons (such as ignoring diacritics and accents like e vs é):

let searchInput = "resume"
let documentTitle = "Résumé"

let result = searchInput.compare(
    documentTitle,
    options: [.caseInsensitive, .diacriticInsensitive],
    range: nil,
    locale: .current
)

if result == .orderedSame {
    print("Found matching document.")
}

How to prevent it

  • Do not call Objective-C runtime methods like isEqual: or isEqualToString: in pure Swift projects.
  • Always access field.text when validating UIKit form controls instead of comparing the UITextField reference.
  • Prefer string.isEmpty over string == "" for clearer intent and minor performance gains.
  • Strip whitespace with trimmingCharacters(in: .whitespacesAndNewlines) when verifying required text input.
  • Use compare(_:options:) when search or authentication logic needs to be case-insensitive or locale-aware.

FAQ

Is isEqualToString: completely deprecated in Swift?

No, but it is unavailable directly on Swift’s native String struct. It is only accessible when explicitly casting a Swift string to an NSString instance, which is unnecessary because == performs full canonical string equality in Swift.

Does Swift == check memory references like Objective-C ==?

No. In Swift, == invokes the Equatable implementation for String, comparing character contents. To check reference equality for class instances in Swift, use the identity operator ===.

How does Swift handle Unicode normalization during string comparison?

Swift strings use extended grapheme clusters for comparison. Even if two strings are composed using different underlying UTF-8 sequences (such as a precomposed character versus a base character combined with an accent mark), the == operator treats them as equal if they share canonical equivalence.

Sources & credits

Parts of this guide are adapted from content licensed under CC BY-SA 4.0; this article is shared under the same license.