iOS Development Made Simple!
  • Home
  • Blog
  • Contact
  • Enroll Now
  • Enroll Now

Caesar Cipher – iOS Engineering And Algorithms

  • Yasser Farahi
  • April 25, 2022April 25, 2022
  • Algorithms, Interview Challenges, Programming, Swift

For this article I have decided to write a Caesar Cipher algorithm.

First let me give you a brief intro to this subject. A Caesar Cipher is a basic encryption algorithm that takes all the letters of a message and encrypts them by shifting them over some fixed amount. For example a Caesar Cipher with an offset of 15 would map the alphabet as followed:

Alphabet
ABCDEFGHIJKLMNOPQRSTUVWXYZ
Shifted Alphabet
PQRSTUVWXYZABCDEFGHIJKLMNO

Message Example
Input: WELCOME TO RISING GALAXY
Output: LTARDBT ID GXHXCV VPAPMN

Let’s get started and find out whether we are up for this challenge. 

STEP ONE – let’s write an enumeration and define our Encryption modes.

1
2
3
4
import Foundation
 
//YOU ARE NOW HERE
enum Encryption { case encrypting, decrypting }

STEP TWO – Now, go ahead and create a class, name this class CCipher. Soon we are going to define our encryption method within this class. Let’s add a single private alphabet property of type String to our class. As we are going to initialise this property through the init method, you just have to declare this variable, there is no need to initialise it with a default value.

1
2
3
4
5
6
7
8
9
10
//YOU ARE NOW HERE
class CCipher {
    
    private var alphabet: String
 
    init(_ alphabet: String) {
        self.alphabet = alphabet
    }
 
}

STEP THREE – Next, we are going to write our encryption algorithm. Our function should take three input parameters and have a String return type. The first input parameter is going to accept our message and should be of type String.

The second input parameter will be an Int, we are going to use this parameter to define the amount of time that we desire to shift each character.

The last input parameter is going to be of type Encryption, the handy dandy enumeration type that we created at the start of this article, remember?. This last input parameter should allow us to specify what we are expecting from our algorithm to do for us, e.g are we encrypting or decrypting a message?

1
2
3
4
5
    //YOU ARE NOW HERE
    func encryption(_ message: String, _ n: Int = 15, mode: Encryption) -> String {
        //Just for now let's satisfy our method and return an empty String
         return " "
    }

STEP FOUR -Now Inside the encryption method, let’s go ahead and create a result property of type String. Later we are going to append all of the shifted characters to this variable, and return the final result as our method’s return type.

1
2
3
4
5
6
7
    func encryption(_ message: String, _ n: Int = 15, mode: Encryption) -> String {
        
         //YOU ARE NOW HERE
         var result: String = ""
         return restul
 
    }

STEP FIVE – Let’s create two more properties. These are going to be two Arrays of type Character. The first Array will be used to chop our message string into individual letters of type Character. And the other one will be used to store alphabet letters. Later we are going to use the alphabet array as a lookup table, this will enable us to find the correct substitute for each character from the message array.

1
2
3
4
5
6
7
8
9
    func encryption(_ message: String, _ n: Int = 15, mode: Encryption) -> String {
        
         //YOU ARE NOW HERE
         var result: String = ""
         let messageChars = Array(message)
         let lookup = Array(self.alphabet)
 
         return restul
    }

STEP SIX – Things are about to get exciting 😎 are you ready?. Let’s write a simple for-loop and loop through our message characters (messageChars). Inside this loop we should create a reference property of type character, this variable is going to refer to the shifted character.

Let me explain this to you in an expanded manner. During each loop iteration we are looking at the current letter from the messages array, then through some magic, which we’ll be implementing shortly. We’ll try to find the correct shifted substitute for the current letter. Once we have found the correct letter we are going to assign that character to the reference variable that we just created, and at the end of each loop append the shifted character to the result variable.

1
2
3
4
5
6
7
8
9
10
11
12
    func encryption(_ message: String, _ n: Int = 15, mode: Encryption) -> String {
        var result: String = ""
        let messageChars = Array(message)
        let lookup = Array(self.alphabet)
      
         //YOU ARE NOW HERE
         for c in messageChars {
             var encryptedMessage: Character = " "
         }
 
         return restul
    }

STEP SEVEN – Next, we would like to find out the index of the current character from the messageChars within the alphabet. So, if the current letter is B, its index would be 1.
We could use the firstIndex(of:) method, which is offered by Apple/Swift for free. But hey, we are good software engineers and love to write thing from scratch, right?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
    //First input takes the current Character
    //Second input takes the lookup array i.e Alphabet array
    //This method returns an Integer
 
    private func findIndex(of c: Character, _ arr: [String.Element]) -> Int {
        var n: Int = .zero
        let m = arr.count
        var found: Bool = false
 
        //While Character is not found run this loop
        while !found {
            //If the index that we are trying to get is less than,
            //array count continue
            if n < m {
                //Using a simple ternary operation to determine found value
                //if c i equal to the n(th) array element found becomes true
                //else found remains false
                
                found = c == arr[n] ? true : false
 
                //As long is found remains false, we keep adding one to n
                //as soon as found becomes true we assign the current n value
                //to n and exit the loop
                n = !found ? (n + 1) : n
            }
        }
        return n
    }

STEP EIGHT – Let’s head back to our encryption algorithm. Since we are only interested in Characters/ Letters and not any whitespaces, we should write a simple if statement in which we clearly define if Character is not a whitespace. If the current Character is in fact a whitespace, we would like to append it to the result variable immediately.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
    func encryption(_ message: String, _ n: Int = 15, mode: Encryption) -> String {
        var result: String = ""
        let messageChars = Array(message)
        let lookup = Array(self.alphabet)
      
        
         for c in messageChars {
             var encryptedMessage: Character = " "
            
             //YOU ARE NOW HERE
             if c != " " {
             }
 
             result.append(encryptedMessage)
 
         }
 
         return restul
    }

STEP NINE – During this step we are going to finalize our algorithm. Therefore we need two more new properties and a Switch statement. Without further ado, within the if statement go ahead and create the first property and grab the index of the current Character, this should be done through using our own findIndex method that we created minutes ago.

Next we should create an Integer variable, which is going to be used as an index reference for the shifted character.

Lastly, go ahead and create a Switch statements which gives us access to the encryption cases of the mode parameter.

case . encrypting: key = ((index + n) % lookup.count)
case . decrypting: key = ((index – n) % lookup.count)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
    func encryption(_ message: String, _ n: Int = 15, mode: Encryption) -> String {
 
        var result: String = ""
        let messageChars = Array(message)
        let lookup = Array(self.alphabet)
        
        for c in messageChars {
            var encryptedMessage: Character = " "
            if c != " " {
                //YOU ARE NOW HERE
                let index = findIndex(of: c, lookup)
                var key: Int = .zero
                switch mode {
                case .encrypting: key = ((index + n) % lookup.count)
                case .decrypting: key = ((index - n) % lookup.count)
                }
                encryptedMessage = lookup[key]
            }
 
            result.append(encryptedMessage)
        }
 
        return result
    }

Let me explain to you what is happening inside these cases. Inside each case we are calculating the offset of each Character and assigning the calculated value to the key property. Then we are using the same key property to look into our lookup array and find out the correct substitute for the current Character.

Right now our algorithm is shifting Characters and and is correctly appending the final result to our result property. However we are not done yet. Our encryption algorithm as it is written right now has two huge bugs 🐞🦟.

You may ask yourself what those bugs are. Well, we are able to encrypte our messages, but do you think we’d get the same message back when decrypting?.
The Answer is NO.


Imagine we would like to encrypt a single letter, for instance letter Y. This letter would have an index of 24 within the alphabet array. Let’s say the offset is 15 and for the length we are using the alphabet letters count. (24 + 15) % 26 will give us 13. If we’d take a quick look inside our lookup table we’ll see that letter N becomes our encrypted equivalent of Y.

Now let see whether we are able to get Y back by applying almost the same calculation. With the only difference, this time we should subtract the letter’s index from the offset value. (13 – 15) % 26 will give us -2. This will definitely produce an out of range error. Even if we convert this value to a positive value, we’d get the wrong result,
turning -2 into 2 will give us C, and that’s not what we are looking for.

Let me explain it to you in code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//Letter Y has an index of 24
// Offset is 15
// Length is 26
//Let's see what index we'd get, through a simple calculation
let yIndex = 24
let offset = 15
let length = 26
let encryptedIndex = (yIndex + offset) % length
print(encryptedIndex) // 13
 
//Encrypted letter has the index 13 (N)
// Offset is 15
// Length is 26
let nIndex = 13
let decryptedIndex = (nIndex - offset) % length
print(decryptedIndex) // -2
//A. This will produce an out of range error
//B. Even if we convert this value into a positive value
//   it would become 2 which will give us letter C and not Y

Also one of our cases may produce a negative key, e.g -1 or -15 or -25, since we don’t have any negative indices this will definitely cause an out of range error and crash our algorithm.

So we need to write two more methods to prevent those bugs from causing our algorithm to fail.
One method to normalize the index range, and another method to convert negative values into positive values. For the last functionality we could use the standard Swift abs() method. But again, as good iOS engineers we should be able to write our own methods, right!?

1
2
3
4
5
6
7
8
9
10
    private func _abs( _ n: Int ) -> Int {
        //If n is less than zero, it's negative
        return n < 0 ? n * -1 : n
    }
    
    private func normalize( _ key: Int, _ length: Int ) -> Int {
        //If n is less than zero, it's negative and out of range
        // So we are adding key to the length to get a normalized index
        return key < 0 ? (key + length) : key
    }

FINAL STEP – let’s head back to our encryption algorithm for the last time. After the closing curly brace of the Switch statement, go ahead and use both _abs and normalize methods and assign the correct Character to the encryptedMessage property.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
    func encryption(_ message: String, _ n: Int = 15, mode: Encryption) -> String {
 
        var result: String = ""
        let messageChars = Array(message)
        let lookup = Array(self.alphabet)
        
        for c in messageChars {
            var encryptedMessage: Character = " "
            if c != " " {
                let index = findIndex(of: c, lookup)
                var key: Int = .zero
                switch mode {
                case .encrypting: key = ((index + n) % lookup.count)
                case .decrypting: key = ((index - n) % lookup.count)
                }
                //YOU ARE NOW HERE
                encryptedMessage = lookup[_abs(normalize(key, lookup.count))]
            }
 
            result.append(encryptedMessage)
        }
 
        return result
    }

Well folks, we managed to write a Caesar Cipher algorithm from scratch. I hope you liked this article, Thank you for your time.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
import Foundation
 
enum Encryption { case encrypting, decrypting }
 
class CCipher {
 
    private var alphabet: String
 
    init(_ alphabet: String) {
        self.alphabet = alphabet
    }
 
    func encryption(_ message: String, _ n: Int = 15, mode: Encryption) -> String {
 
        var result: String = ""
        let messageChars = Array(message)
        let lookup = Array(self.alphabet)
        
        for c in messageChars {
            var encryptedMessage: Character = " "
            if c != " " {
                let index = findIndex(of: c, lookup)
                var key: Int = .zero
                switch mode {
                case .encrypting: key = ((index + n) % lookup.count)
                case .decrypting: key = ((index - n) % lookup.count)
                }
                encryptedMessage = lookup[_abs(normalize(key, lookup.count))]
            }
 
            result.append(encryptedMessage)
        }
 
        return result
    }
    
    private func _abs( _ n: Int ) -> Int {
        return n < 0 ? n * -1 : n
    }
    
    private func normalize( _ key: Int, _ length: Int ) -> Int {
        return key < 0 ? (key + length) : key
    }
 
 
    private func findIndex(of c: Character, _ arr: [String.Element]) -> Int {
        var n: Int = .zero
        let m = arr.count
        var found: Bool = false
        while !found {
            if n < m {
                found = c == arr[n] ? true : false
                n = !found ? (n + 1) : n
            }
        }
        return n
    }
}
 
let ccipher = CCipher("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
 
ccipher.encryption("WELCOME TO RISING GALAXY", mode: .encrypting)
//LTARDBT ID GXHXCV VPAPMN
 
ccipher.encryption("LTARDBT ID GXHXCV VPAPMN", mode: .decrypting)
//WELCOME TO RISING GALAXY
Tags: algoritmes Caesar Cipher Data Structure Interview Questions O(2n) Runtime Shifting Characters Shifting Values Software Engineering

Post navigation

Previous Post
Next Post

Categories

  • Algorithms
  • Beginner
  • Courses
  • Dart
  • General
  • Interview Challenges
  • OOP
  • Programming
  • Swift

New Posts

  • iOS Development Xcode Layout And Auto Layout Tools
    August 2, 2022
  • Composition – Relations Between Objects And Classes
    June 13, 2022
  • Aggregation – Relations Between Objects And Classes
    June 8, 2022

Tags

Aggregation algoritmes Association Basics Caesar Cipher Classes Composition Data Structure Dependency Implementation Inheritance Interfaces Interview Questions O(1) Runtime O(2n) Runtime Object Relations OOP Power Of N Reverse Values Shifting Characters Shifting Values Software Engineering String Manipulation Swift Value Swapping Xcode
© Copyright RISING GALAXY. All Rights Reserved | Read Our Privacy Policy  - Terms Of Use - Cookies Policy
Cookies Policy
We use cookies on our website to give you the most relevant experience by remembering your preferences and repeat visits. By clicking “Accept”, you consent to the use of ALL the cookies.
Do not sell my personal information.
Cookie SettingsAccept
Manage consent

Privacy Overview

This website uses cookies to improve your experience while you navigate through the website. Out of these, the cookies that are categorized as necessary are stored on your browser as they are essential for the working of basic functionalities of the website. We also use third-party cookies that help us analyze and understand how you use this website. These cookies will be stored in your browser only with your consent. You also have the option to opt-out of these cookies. But opting out of some of these cookies may affect your browsing experience.
Necessary
Always Enabled
Necessary cookies are absolutely essential for the website to function properly. These cookies ensure basic functionalities and security features of the website, anonymously.
CookieDurationDescription
cookielawinfo-checkbox-analytics11 monthsThis cookie is set by GDPR Cookie Consent plugin. The cookie is used to store the user consent for the cookies in the category "Analytics".
cookielawinfo-checkbox-functional11 monthsThe cookie is set by GDPR cookie consent to record the user consent for the cookies in the category "Functional".
cookielawinfo-checkbox-necessary11 monthsThis cookie is set by GDPR Cookie Consent plugin. The cookies is used to store the user consent for the cookies in the category "Necessary".
cookielawinfo-checkbox-others11 monthsThis cookie is set by GDPR Cookie Consent plugin. The cookie is used to store the user consent for the cookies in the category "Other.
cookielawinfo-checkbox-performance11 monthsThis cookie is set by GDPR Cookie Consent plugin. The cookie is used to store the user consent for the cookies in the category "Performance".
viewed_cookie_policy11 monthsThe cookie is set by the GDPR Cookie Consent plugin and is used to store whether or not user has consented to the use of cookies. It does not store any personal data.
Functional
Functional cookies help to perform certain functionalities like sharing the content of the website on social media platforms, collect feedbacks, and other third-party features.
Performance
Performance cookies are used to understand and analyze the key performance indexes of the website which helps in delivering a better user experience for the visitors.
Analytics
Analytical cookies are used to understand how visitors interact with the website. These cookies help provide information on metrics the number of visitors, bounce rate, traffic source, etc.
Others
Other uncategorized cookies are those that are being analyzed and have not been classified into a category as yet.
SAVE & ACCEPT