@CipherBuilder
func buildEggCipherMessage() -> String {
"A secret report within the guild."
"4 planets have come to our attention"
"regarding a plot that could jeopardize spice production."
}
body
.onAppear {
secret = buildEggCipherMessage()
}
// 1
@resultBuilder
// 2
enum CipherBuilder {
// 3
static func buildBlock(_ components: String...) -> String {
components
.joined(separator: " ")
.replacingOccurrences(of: "e", with: "🥚")
}
}
- @resultBuilder can annotate any type that allows a static method.
- You’ve used an enum because CipherBuilder doesn’t need to have instances created. Instead, it only contains static methods.
- You implement a static buildBlock(_:) function. This is the only requirement for a result builder. Your function takes any number of String arguments and returns a String containing all the arguments joined with a space and all instances of the letter e replaced with the egg emoji: 🥚.
body
.onChange(of: message) { newValue in
secret = processMessage(newValue)
}
.onChange(of: secretMode) { _ in
secret = processMessage(message)
}
}
func processMessage(_ value: String) -> String {
let cipher = SuperSecretCipher(offset: 7)
switch secretMode {
case .encode:
return cipher.cipherRule.encipher(value)
case .decode:
return cipher.cipherRule.decipher(value)
}
}
SuperSecretCipher
struct SuperSecretCipher {
let offset: Int
@CipherBuilder
var cipherRule: CipherRule {
LetterSubstitution(offset: offset)
}
}
CipherBuilder
@resultBuilder
enum CipherBuilder {
static func buildBlock(_ components: CipherRule...) -> CipherRule {
components
}
}
CipherRule
protocol CipherRule {
func encipher(_ value: String) -> String
func decipher(_ value: String) -> String
}
// 1
extension Array: CipherRule where Element == CipherRule {
// 2
func encipher(_ value: String) -> String {
// 3
reduce(value) { encipheredMessage, secret in
secret.encipher(encipheredMessage)
}
}
func decipher(_ value: String) -> String {
// 4
reversed().reduce(value) { decipheredMessage, secret in
secret.decipher(decipheredMessage)
}
}
}
LetterSubstitution
struct LetterSubstitution: CipherRule {
let letters: [String]
let offset: Int
// 1
init(offset: Int) {
self.letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".map(String.init)
self.offset = max(1, min(offset, 25))
}
// 2
func swapLetters(_ value: String, offset: Int) -> String {
// 3
let plainText = value.map(String.init)
// 4
return plainText.reduce("") { message, letter in
if let index = letters.firstIndex(of: letter.uppercased()) {
let cipherOffset = (index + offset) % 26
let cipherIndex = cipherOffset < 0 ? 26
+ cipherOffset : cipherOffset
let cipherLetter = letters[cipherIndex]
return message + cipherLetter
} else {
return message + letter
}
}
}
func encipher(_ value: String) -> String {
swapLetters(value, offset: offset)
}
func decipher(_ value: String) -> String {
swapLetters(value, offset: -offset)
}
}