Different ways to check if a string contains another string in Swift

⋅ 8 min read ⋅ Swift String

Table of Contents

Swift provides ways to check if a string contains another string, numbers, uppercased/lowercased string, or special characters. Each scenario might need a different method. I will visit some of them. In the end, you should be able to pick and adjust the one to suits your needs.

Check if a string contains another string

You can use range(of:options:), which finds and returns the range of the first occurrence of a given string within the string if found, and return a nil if not.

func range<T>(of aString: T, options mask: CompareOptions = [], range searchRange: Range<Index>? = nil, locale: Locale? = nil) -> Range<Index>? where T : StringProtocol

If the range is not equals nil, that's mean we find a substring within the string.

let c = "C"
let objc = "Objective-C"

objc.range(of: capitalC) != nil
// true

For this simple use case, range(of:options:) might be overkill since it provides more options and parameters than we need. You can use an alternative method, contains(_:), which does the same thing but more concise.

public func contains<T>(_ other: T) -> Bool where T : StringProtocol

The method name and signature convey a clearer message than the previous one.

let c = "C"
let objc = "Objective-C"

objc.contains(c)
// true

You can easily support sarunw.com by checking out this sponsor.

Sponsor sarunw.com and reach thousands of iOS developers.

Check if a string contains a substring case insensitive.

If you want to check whether the string contains a given string by performing a case-insensitive search, you have two ways to do it.

As before you can use range(of:options:) with .caseInsensitive option.

let c = "c"
let objc = "Objective-C"

objc.range(of: c, options: .caseInsensitive) != nil
// true

Or you can use a more meaningful method name of localizedCaseInsensitiveContains(_:).

let c = "c"
let objc = "Objective-C"

objc.localizedCaseInsensitiveContains(c)
// true

Check if a string contains a number

To check whether a string contains a number or not, we use another form of the range-finding method, rangeOfCharacter(from:). This form finds and returns the range of the first character from a given character set. It accepts a character set parameter instead of a string.

You can declare character set like this:

let str = "SwiftUI 2.0"
let mySet = CharacterSet(["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"])

str.rangeOfCharacter(from: mySet) != nil
// true

CharacterSet has many set operations. You can create a character set of digits with insert(charactersIn: String), which is less verbose.

let str = "SwiftUI 2.0"
var mySet = CharacterSet()
mySet.insert(charactersIn: "0123456789")

str.rangeOfCharacter(from: mySet) != nil
// true

But the two examples above might not cover all the cases if you want to support the non-English language. Here are examples of number in the Thai language

0-9 in Thai languages.

,,,,,,,,,

So, instead of study all the possibilities in other languages. Apple provides a class variable, CharacterSet.decimalDigits, that packs every digit for us.

var str = "SwiftUI 2.0"
let digitCharacters = CharacterSet.decimalDigits

str.rangeOfCharacter(from: digitCharacters) != nil
// true

Check if a string contains only a number

To check whether the string contains only numbers, we use the concept of set and CharacterSet.decimalDigits together. For a string to contains only a number, all the characters in that string must be a subset of CharacterSet.decimalDigits.

let numberOnly = "2021"
let str = "SwiftUI 2.0"
let digitsCharacters = CharacterSet.decimalDigits

CharacterSet(charactersIn: numberOnly).isSubset(of: digitsCharacters)
// true

CharacterSet(charactersIn: str).isSubset(of: digitsCharacters)
// false

Check if a string contains only alphanumerics

Apple provides an alphanumerics character set, CharacterSet.alphanumerics. To find a string that contains only alphanumerics, we have to make sure there are no characters outside this set. To do that, we check a string against a converted set of CharacterSet.alphanumerics (which means a set of non-alphanumeric characters).

If we find an occurrence of non-alphanumeric characters, the string is not alphanumerics.
If we don't find any occurrence of non-alphanumeric characters (range equals nil), the string contains only alphanumerics.

let str = "Swift"
let numberOnly = "2021"
let space = "Hello World"
let specialCharacter = "$var"

let nonAlphanumericCharacters = CharacterSet.alphanumerics.inverted // <1>

let isAlphanumerics = numberOnly.rangeOfCharacter(from: nonAlphanumericCharacters) == nil // <2>
// true

let isAlphanumerics2 = space.rangeOfCharacter(from: nonAlphanumericCharacters) == nil // <3>
// false

let isAlphanumerics3 = specialCharacter.rangeOfCharacter(from: nonAlphanumericCharacters) == nil
// false

let isAlphanumerics4 = str.rangeOfCharacter(from: nonAlphanumericCharacters) == nil
// true

<1> We call .inverted on CharacterSet.alphanumerics which result in a set of non-alphanumeric characters.
<2> We find an occurrence of non-alphanumeric characters. If we can't find (nil), the string is alphanumerics.
<3> If the return range does not equal to nil, that means we find non-alphanumeric characters, so the string is not alphanumerics.

The rest of the examples are just a different set of character sets checking. I will jump right into the implementation.

Check if a string contains only letters

let str = "Swift"
let strWithNumbers = "Swift2"

let letterCharacters = CharacterSet.letters
let nonLetterCharacters = letterCharacters.inverted

let isLetter1 = str.rangeOfCharacter(from: nonLetterCharacters) == nil
// true, contains only letters

let isLetter2 = CharacterSet(charactersIn: strWithNumbers).isSubset(of: letterCharacters)
// false, not contains uppercase letters (2)

Check if a string contains uppercase letters

let lowercase = "swift"
let titlecase = "Swift"

let uppercaseLetters = CharacterSet.uppercaseLetters

lowercase.rangeOfCharacter(from: uppercaseLetters) != nil
// false, contains no uppercase letters

titlecase.rangeOfCharacter(from: uppercaseLetters) != nil
// true, contains uppercase letters

Check if a string contains lowercase letters

let titlecase = "Swift"
let uppercase = "SWIFT"

let lowercaseLetters = CharacterSet.lowercaseLetters

uppercase.rangeOfCharacter(from: lowercaseLetters) != nil
// false, contains no lowercase letters

titlecase.rangeOfCharacter(from: lowercaseLetters) != nil
// true, contains lowercase letters

Check if a string contains special characters

let str = "100%"
let specialCharacters: CharacterSet = ["$", "%", "&"] // <1>

str.rangeOfCharacter(from: specialCharacters) != nil
// true, contains special characters

<1> Define your special characters here.

As you can see, there are many ways to check if a string contains another string. One scenario that you might want to use is a user-level string search. When you do a user-level search, we don't want an exact match comparison because that would make the search less useful or difficult to use.

Here is an example search result from Safari, which is case and diacritic insensitive.

Searching for e in Safari match both e, é, and E.
Searching for e in Safari match both e, é, and E.

You could use range(of:options:) with .caseInsensitive and diacriticInsensitive option.

let c = "c"
let capitalC = "C"
let cWithDiaCritic = "ć"
let objc = "Objective-C"

objc.range(of: c, options: [.caseInsensitive, .diacriticInsensitive]) != nil
// true

objc.range(of: cWithDiaCritic, options: [.caseInsensitive, .diacriticInsensitive]) != nil
// true

objc.range(of: capitalC, options: [.caseInsensitive, .diacriticInsensitive]) != nil
// true

Apple also provides a more meaningful method, localizedStandardContains, which is the most appropriate method for doing user-level string searches, similar to how searches are done generally in the system.

/// Returns a Boolean value indicating whether the string contains the given
/// string, taking the current locale into account.
///
/// This is the most appropriate method for doing user-level string searches,
/// similar to how searches are done generally in the system. The search is
/// locale-aware, case and diacritic insensitive. The exact list of search
/// options applied may change over time.
@available(macOS 10.11, iOS 9.0, *)
public func localizedStandardContains<T>(_ string: T) -> Bool where T : StringProtocol

You can use it like this.

let c = "c"
let capitalC = "C"
let cWithDiaCritic = "ć"
let objc = "Objective-c"

capitalC.localizedStandardContains(cWithDiaCritic)
// true

objc.localizedStandardContains(c)
// true

objc.localizedStandardContains(capitalC)
// true

objc.localizedStandardContains(cWithDiaCritic)
// true

localizedStandardContains is locale-aware, case, and diacritic insensitive. As you can see, c, C, and ć are considered matched.

You can easily support sarunw.com by checking out this sponsor.

Sponsor sarunw.com and reach thousands of iOS developers.

Read more article about Swift, String, or see all available topic

Enjoy the read?

If you enjoy this article, you can subscribe to the weekly newsletter.
Every Friday, you'll get a quick recap of all articles and tips posted on this site. No strings attached. Unsubscribe anytime.

Feel free to follow me on Twitter and ask your questions related to this post. Thanks for reading and see you next time.

If you enjoy my writing, please check out my Patreon https://www.patreon.com/sarunw and become my supporter. Sharing the article is also greatly appreciated.

Become a patron Buy me a coffee Tweet Share
Previous
Different ways to compare string in Swift

String comparison is an essential operation for day to day job. Swift provides a few variations for this. We will visit them in this article.

Next
Create a list of views in SwiftUI using ForEach

Part 1 in the series "Building Lists and Navigation in SwiftUI". We visit the first building block of any list view, content, and how to create them.

← Home