var value = k % 26
if value < 0 { value += 26 }
Use a switch-statement with range patterns instead of the if-conditions:
switch code { case 65...90: // ... case 97...122: // ... default: // ... }
Use constants instead of the literal numbers, e.g.
let uppercaseAletter_A = 65 let uppercaseZletter_Z = 90 let lowercaseAletter_a = 97 let lowercaseZletter_z = 122
var value = k % 26
if value < 0 { value += 26 }
Use a switch-statement with range patterns instead of the if-conditions:
switch code { case 65...90: // ... case 97...122: // ... default: // ... }
Use constants instead of the literal numbers, e.g.
let uppercaseA = 65 let uppercaseZ = 90 let lowercaseA = 97 let lowercaseZ = 122
var value = k % 26
if value < 0 { value += 26 }
Use a switch-statement with range patterns instead of the if-conditions:
switch code { case 65...90: // ... case 97...122: // ... default: // ... }
Use constants instead of the literal numbers, e.g.
let letter_A = 65 let letter_Z = 90 let letter_a = 97 let letter_z = 122
Enumerating UTF-8 characters: Your code uses two nested loops to encrypt letters (based on the UTF-8 code):
var eMessage = ""
let arr = messageToCipher.characters.map { String(0γγ«) }
for ch in arr {
for code in String(ch).utf8 {
if (65<=code && code<=90) || (97<=code && code<=122) {
// ... translate `code` and append to `eMessage` ...
}
else{
eMessage = eMessage + ch
}
}
}
This is unnecessary complicated and has an unwanted side effect if the message contains non-ASCII characters (which are encoded as 2 or more UTF-8 code units):
print(cipher(messageToCipher: "Γ€ β¬ π π§π©", k: 2) )
// ÀÀ β¬β¬β¬ ππππ π§π©π§π©π§π©π§π©π§π©π§π©π§π©π§π©
This could be solved by adding a break
in the else-case. The better solution is to
enumerate the UTF-8 code units directly:
var encoded: [UInt8] = []
for code in messageToCipher.utf8 {
if (65<=code && code<=90) || (97<=code && code<=122) {
// ... translate `code` and append to `encoded` ...
}
else{
encoded.append(code)
}
}
return String(bytes: encoded, encoding: .utf8)!
Code duplication: There is some duplicate code in
if k > 26{
let value = k % 26 == 0 ? k / 26 : k % 26
var pCode = ((Int(code)) + (value))
if (pCode > 122 && (97<=code && code<=122)) || (pCode > 90 && (65<=code && code<=90)) {
pCode = pCode - 26
}
let s = String(UnicodeScalar(UInt8(pCode)))
eMessage = eMessage + s
}else
{
var pCode = Int(code) + k
if (pCode > 122 && (97<=code && code<=122)) || (pCode > 90 && (65<=code && code<=90)) {
pCode = pCode - 26
}
let s = String(UnicodeScalar(UInt8(pCode)))
eMessage = eMessage + s
}
which is not necessary. The if-part works for the case k <= 26
as well.
Exceptional key values: The special handling of the case k % 26 == 0
causes
unexpected results:
print(cipher(messageToCipher: "abc xyz ABC XYZ", k: 26) ) // abc xyz ABC XYZ
print(cipher(messageToCipher: "abc xyz ABC XYZ", k: 52) ) // cde zab CDE ZAB
and is not needed. Negative key values are not handled at all:
print(cipher(messageToCipher: "abc xyz ABC XYZ", k: -2) ) // _`a vwx ?@A VWX
print(cipher(messageToCipher: "abc xyz ABC XYZ", k: -200) )
// Fatal error: Negative value is not representable
Both issues can be solved by computing the remainder of k
modulo 26, as a number
in the range 0...25:
var value = k % 26
if value < 0 { value += 26 }
In addition, this computation can be done once, before entering the loop.
Other issues:
I would use some different argument/variable names, e.g.
message
instead ofmessageToCipher
(it is clear from the context and the function name that this string should be encrypted), orkey
instead ofk
.There are unnecessary parentheses, e.g. in
var pCode = ((Int(code)) + (value))
The usage of white space is not consistent, e.g. in
if (pCode > 122 && (97<=code && code<=122)) || (pCode > 90 && (65<=code && code<=90)) {
Summarizing the suggestions so far, we have
func cipher(message: String, key: Int) -> String {
var offset = key % 26
if offset < 0 { offset += 26 }
var encoded: [UInt8] = []
for code in message.utf8 {
if (65 <= code && code <= 90) || (97 <= code && code <= 122) {
var pCode = Int(code) + offset
if (pCode > 122 && (97 <= code && code <= 122)) || (pCode > 90 && (65 <= code && code <= 90)) {
pCode -= 26
}
encoded.append(UInt8(pCode))
} else {
encoded.append(code)
}
}
return String(bytes: encoded, encoding: .utf8)!
}
Make it functional: If the encryption of a single character is moved to a separate
function then one can apply this to the UTF-8 view with map()
:
func cipher(message: String, key: Int) -> String {
var offset = key % 26
if offset < 0 { offset += 26 }
func utf8encrypt(code: UInt8) -> UInt8 {
// ... compute and return encrypted character ...
}
return String(bytes: message.utf8.map(utf8encrypt), encoding: .utf8)!
}
Further suggestions:
Use a switch-statement with range patterns instead of the if-conditions:
switch code { case 65...90: // ... case 97...122: // ... default: // ... }
Use constants instead of the literal numbers, e.g.
let uppercaseA = 65 let uppercaseZ = 90 let lowercaseA = 97 let lowercaseZ = 122
so that a future reader of your code does not have to guess what the numbers stand for.