3
\$\begingroup\$

I have been learning Swift for the past day and thought I'd try a basic problem where I can do String manipulation. I am only on pg.100 of the Swift Programming Language on iBooks. Compared to python it seems to be quite difficult to actually modify individual characters as Swift is strongly typed/type safe.

A Caesar cipher is used to encrypt a String by shifting each letter by x, where x is an integer, and replaces each letter withe the shifted letter. So encrypt("AB", 2) becomes "BC"

If anyone could point where I can reduce the number of lines or an easier way of doing/or idioms I have missed that'd be just what I am looking for.

import Cocoa

func encrypt(message: String, shift: Int) -> String {

    let scalars = Array(message.unicodeScalars)
    let unicodePoints = scalars.map({x in Character(UnicodeScalar(Int(x.value) + shift)!)})

    return String(unicodePoints)
}

let message = "Attack at dawn"
print(encrypt(message: message, shift: 2))
\$\endgroup\$
2
\$\begingroup\$

Using the unicodeScalars view of the Swift String is a good start, but the code can be simplified:

  • There is no need to create an array first, you can call map directly on the unicodeScalars collection.
  • The conversion to Character is not needed, the array of shifted unicode scalars can be converted directly back to a string (attribution to this answer on Stack Overflow).

Also the variable name unicodePoints is misleading, because that is an array of Character in your code.

Your method then becomes

func encrypt(message: String, shift: Int) -> String {
    let unicodeScalars = message.unicodeScalars.map { UnicodeScalar(Int($0.value) + shift)! }
    return String(String.UnicodeScalarView(unicodeScalars))
}

However, this is probably not yet what you want. Example:

let message = "Attack at dawn"
print(encrypt(message: message, shift: 2))    // Cvvcem"cv"fcyp
print(encrypt(message: message, shift: 10))   // K~~kmu*k~*nkx

All characters are shifted by the given amount so that a space becomes a quotation mark, and some letters are transformed to non-letters. Also

print(encrypt(message: message, shift: -100))

crashes at runtime, because the unicode scalar value is shifted to an invalid value.

The traditional Caesar Cipher transforms only letters in the range A...Z, and rotates them, so that shifting "Z" by 2 becomes "B". Lowercase letters can be transformed to uppercase, but other characters are usually ignored (either removed or left unchanged).

Here is a possible implementation with an inner function shiftLetter() which transforms a single letter. This inner function is used as argument in

msg.unicodeScalars.map(shiftLetter)

to transform the entire String:

func encrypt(message: String, shift: Int) -> String {

    func shiftLetter(ucs: UnicodeScalar) -> UnicodeScalar {
        let firstLetter = Int(UnicodeScalar("A").value)
        let lastLetter = Int(UnicodeScalar("Z").value)
        let letterCount = lastLetter - firstLetter + 1

        let value = Int(ucs.value)
        switch value {
        case firstLetter...lastLetter:
            // Offset relative to first letter:
            var offset = value - firstLetter
            // Apply shift amount (can be positive or negative):
            offset += shift
            // Transform back to the range firstLetter...lastLetter:
            offset = (offset % letterCount + letterCount) % letterCount
            // Return corresponding character:
            return UnicodeScalar(firstLetter + offset)!
        default:
            // Not in the range A...Z, leave unchanged:
            return ucs
        }
    }

    let msg = message.uppercased()
    return String(String.UnicodeScalarView(msg.unicodeScalars.map(shiftLetter)))
}

Examples:

let message = "Attack at dawn"
print(encrypt(message: message, shift: 2))    // CVVCEM CV FCYP
print(encrypt(message: message, shift: 10))   // KDDKMU KD NKGX
print(encrypt(message: message, shift: -100)) // EXXEGO EX HEAR
\$\endgroup\$
  • \$\begingroup\$ Great review, cheers! \$\endgroup\$ – Lewis Aug 4 '17 at 9:12

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.