 # Play With Code: Palindromes In Swift

Written by LearnAppMaking on February 2 2020 in App Development, Play With Code, Swift The date 02-02-2020 is a palindrome. Palindromes are words that read the same forward as backward. And they’re great fun to play with in Swift! In this tutorial, we’ll discuss and code 3 approaches to check if a string is a palindrome in Swift.

## What’s A Palindrome?

Firs things first: What’s a palindrome?

A palindrome is a word, number, phrase or other sequence that reads the same forward as it reads backward. A few examples:

• A man, a plan, a canal – Panama
• Doc, note: I dissent. A fast never prevents a fatness. I diet on cod.
• Step on no pets

(Source: Palindrome)

A palindrome is a nerdy word joke!

If you look closely, you’ll see that you can essentially “flip” or rotate a palindrome around its middle. What’s on the left of the middle, is also on the right. It doesn’t matter if the number of characters is even or odd. And as we can see in the examples above, palindromes can also include punctuation.

You can get pretty crazy with palindromes. For example, a Lychrel number is an integer number that cannot form a palindrome after repeated reversal and addition of its digits. Take 56, which becomes palindromic after one iteration: 56 + 65 = 121.

And as you’ve guessed, palindromes are a prime subject for playing around with strings in Swift!

## Palindrome 1: Comparing String Characters

We’re going to figure out if a given string is a palindrome, by using Swift programming. Before we write some code, we’ll have to agree on a strategy or algorithm.

Here’s one such algorithm:

1. Take the first and last character of the string
2. Compare them against each other:
• If they’re the same, continue with step 3
• If they’re not the same, stop, the string is not a palindrome
3. Look at the next character, so the second one from the start, and the before last character at the end, then go to step 2

With this algorithm, we’re essentially “walking” the characters from both the start and the end of the string. You compare them against each other, and when every character pair is equal, the string is a palindrome.

Let’s code that in Swift. We’ll start by defining the function `isPalindrome(_:)`. Like this:

``````func isPalindrome(_ value: String) -> Bool
{

}
``````

In the above code, we’ve defined a function named `isPalindrome(_:)`. It has one unnamed parameter `value` of type `String`. The function returns a `Bool` value, either `true` or `false`. We’re going to check if `value` is a palindrome, and return `true` if it is, and `false` if it’s not.

Next up, we’re adding the following code to the function:

``````let len = value.count / 2

for i in 0..<len
{

}
``````

This is a for loop. Here’s how it works:

• We’re first dividing the length of the input string `value` by 2. We’ll only need to iterate half of the string, because we’re starting at both ends. The length of half the string is assigned to `len`.
• The for loop will iterate from `0` to `len` (exclusive). So, when the input string has 7 characters, we’ll loop from 0 to 3: `0, 1, 2, 3`. We can use these integer values as the constant `i` within the loop.

The above code also uses a trick. What happens when the length of the string is an odd number, like 7? In that case, `len` will be 3. The code `value.count / 2` is essentially `7 / 2`, which is 3.5, but since both `7` and `3` are integers, the resulting type of `len` is an integer too. You can’t assign a decimal value, like 3.5, to an integer. As a result, the fraction of the decimal value is “chopped off”, and we end up with `3`.

OK, next up. We’ll need to get the characters at the start and end of the input string. Add the following code inside the loop:

``````let start = value.index(value.startIndex, offsetBy: i)
let end = value.index(value.endIndex, offsetBy: (i * -1) - 1)
``````

What’s going on here?

• The first line, with `value.index(...)`, will create an index that starts at the beginning of the string plus `i`. So, when `i` is `0`, we’ll get the first character of the string. When `i = 1`, we’ll get the second, and so on.
• The second line does the same thing, except it starts at the end of the string and goes backwards. We start at the end of the string, then subtract `i` from the index number, and subtract `1` to get the one before that.

In Swift, string indices are complicated. First, we’ll need to work with an index to get a character from a string. Integers as indices won’t work. For now, let’s assume that `start` and `end` correspond to the first and last positions in the string.

Then, add the following code in the for loop:

``````if value[start] != value[end] {
return false
}
``````

The above code will compare the character for the `start` index with the character for the `end` index, by using subscript syntax. If they’re not the same, the function returns `false`, because this string is not a palindrome!

Finally, at the end of the function, outside the for loop, add this code:

``````return true
``````

Why do we need that? When the for loop has walked all the characters in the string, we’ve proved that they are all equal (not not equal), so the string is a palindrome! We return `true`.

Here’s the entire function:

``````func isPalindrome(_ value: String) -> Bool
{
let len = value.count / 2

for i in 0..<len
{
let start = value.index(value.startIndex, offsetBy: i)
let end = value.index(value.endIndex, offsetBy: (i * -1) - 1)

if value[start] != value[end] {
return false
}
}

return true
}
``````

Does it work? Let’s find out!

Awesome!

## Palindrome 2: Solving It Recursively

Let’s continue with another approach. Instead of using a for loop, we’ll recursively iterate the string. We’ll take smaller and smaller pieces of the string, continually checking if the first and last characters are the same.

Here, check this out:

``````func isPalindrome( _ value: String) -> Bool
{
guard value.count >= 2 else {
return true
}

let end = value.index(value.endIndex, offsetBy: -1)

if value[value.startIndex] == value[end] {
let start = value.index(value.startIndex, offsetBy: 1)
return isPalindrome(String(value[start..<end]))
} else {
return false
}
}
``````

Whoah, what’s going on here?

First, note that the function is essentially the same as the previous one. We’ve got one input `value`, and we’re either returning `true` or `false`. The principle of the function remains the same too: compare the first character of the input string against the last.

Recursion happens when a function calls itself. You can see that happening inside the `if` statement in the above code. We’re calling the `isPalindrome(_:)` function from within itself. As a result, the function repeats itself, and loops over the given values.

The `if value[value.startIndex] == value[end]` code is similar to what we had before. When the first and last characters are equal, continue execution. When they’re not equal, return `false`, in the `else` block.

When both characters are equal, and we continue, what string do we use for the next call of `isPalindrome(_:)`? It’s simple: we cut off the first and last characters of the string, so that the new first and last characters are actually the second and before last characters of the whole string.

Here’s how that works:

• hannah`h == h`anna
• anna`a == a`nn
• nn`n == n` → DONE!

The next time `isPalindrome(_:)` runs, the string has become shorter. We check the first and last characters again, and call the function again, and again, and again, until…

Until when? That’s where this block of code comes in:

``````guard value.count >= 2 else {
return true
}
``````

The guard statement will check if `value.count` is bigger than or equal to `2`, and if it isn’t, the function will return `true`. Differently said, when the length of `value` is 1 or 2, the function returns `true`.

Here’s why:

• When the input string is empty, it’s length is `0`, and given that we’ve come so far, the entire input string must be a palindrome. If it weren’t, we would have called `return false` sooner.
• When the input string’s length is `1`, we assume the entire string is a palindrome, because we’ve come so far without returning `false`. Also, a string of one character is by definition a palindrome.
• When the input string’s length is `2` or more, the `else` block is not invoked and the function executes normally. Differently said, an input string `xx` will continue, and call `isPalindrome(_:)` once more, with an empty string as input.

Awesome!

Why are `let end ...` and `let start ...` so oddly placed? That’s because of `value[start..<end]`. The `value.endIndex` corresponds to the after last index, i.e. the character after the last character in the string. The `end` index thus corresponds to the actual last character in the string. We need this in the `if` statement’s logic, so `let end ...` is placed outside of it. Inside the `if` block, we’re using the range `start..<end` to create a substring starting at `start`, up to `end`, but not including. In other words, `let end ...` is placed outside the `if` statement because we need it twice, whereas `let start ...` is placed inside the `if` block because we only need it in there.

## Palindrome 3: This One Trick…

So far we’ve gone through a lot of trouble to naively check every character of the input string against the other one. We’ve walked from one end of the string to the middle, and from the other end to the middle, checking every character against the other.

Based on this, we can assert that a string is a palindrome if both halves of the string are equal to each other, in reverse. Like this:

• `hannah``han` + `nah``han` + `han``han == han`
• `rotor``ro` + `or``ro` + `ro``ro == ro` (ignoring middle `t`)

Provided that both string halves can be mirrored, we might as well reverse the entire string if both halves are the same anyway. Said differently, would you notice if I spelled `hannah` in reverse? (No.) Conversely, would you notice that I spelled `doghouse` in reverse? (Yes.)

And that, dear coder, is why the simplest algorithm to check if a given string is a palindrome is…

``````func isPalindrome(_ value: String) -> Bool
{
return value == String(value.reversed())
}
``````

A string is a palindrome if it’s equal to the same string in reverse. Awesome!

By the way, while we’re at it, you may have noticed that many palindromes are sentences, riddles, etc. Since string equality in Swift is strict, i.e. it includes punctuation too, we’ll need something to sanitize one such riddle if we want to run it through our `isPalindrome(_:)` function.

Here’s how you do that:

``````func sanitize(_ value: String) -> String
{
return value.lowercased().replacingOccurrences(of: "[^a-z]+", with: "", options: .regularExpression)
}
``````

The `sanitize(_:)` function will take a `value` string as input, and returns the same string as lowercase, with any character but lowercase A to Z removed. It uses regular expressions to replace characters in the string.

• `Step on no pets` becomes `steponnopets`, for which `isPalindrome(_:)` returns `true`
• `A man, a plan, a canal – Panama` becomes `amanaplanacanalpanama`, which is a palindrome

Sweet!

And there you have it: palindromes. We’ve discussed 3 approaches to check if a given string is a palindrome, and we’ve touched on many Swift syntaxes and topics in between.

The big question of course, is when the next `02-02-2020` palindromic date occurs…

``````var value = "10" // Anything but a palindrome
var day = 1 // Increase by one day
var date = Date() // Now

let formatter = DateFormatter()
formatter.dateFormat = "ddMMyyyy"

// Keep checking `value` for palindrome until one is found
while isPalindrome(value) == false
{
// Add 1 day to `date`, assign ddMMyyyy format to `value`
let component = DateComponents(day: day)
date = Calendar.current.date(byAdding: component, to: date)!
value = formatter.string(from: date)
}

print(value)
``````

(It’s February 12th, 2021, or `12-02-2021`. Or December 2nd, 2021, depending on where you live.)