Search code examples
iosswiftuikit

Exclusion implementation in reversing string in Swift


In ViewController - I have two text fields, like text field for text wihch user wants to reverse and text field for exclusions, and result-UILabel which shows the result.

In first text field user typing some text for reverse and result-UILabel shows the result of reversed text.

In the second text field, I want to make an exception for letters which shouldn't be reversed in result-UILabel. They should be untouchable in the word of the reverse at the time of reversing the text from the first field and should remain in their places.

The model function is in another swift file in another class.


Model function:

import Foundation

class ReverseWords {

public func reverse(textField: String) -> String {
    
    if textField.isEmpty {
        return ""
    }
    
    return textField.trimmingCharacters(in: .whitespacesAndNewlines)
        .components(separatedBy: " ")
        .map { String ( $0.reversed() ) }
        .joined(separator: " ")
    }
}

Using model function for first text field in ViewController:

resultLabel.text = reverseWords.reverse(textField:
reverseTextField.text!.trimmingCharacters(in: .whitespacesAndNewlines))

Example:

First text field (for reverse) print:

FogmEistEr

Second text field (for exclusions) letters which should be untouchable of reverse, print:

E

And result label shows:

rtsiEmgoEF

How can I implement this?

How can I call exceptionTextField in model to check his characters inside? Actually, I don't want to do this between classes, but I would like to look at the solution.

I think it would be better to do it in ViewController, but I got confused…

Have you any ideas, how to implement this?


Solution

  • I'd just make an extension on String:

    extension String {
        func reversed(omittingCharactersIn characterSet: CharacterSet) -> String {
            var reversed = reversed()
                .filter { String($0).rangeOfCharacter(from: characterSet) == nil }
            let excluded = enumerated()
                .filter { String($0.element).rangeOfCharacter(from: characterSet) != nil }
            for (i, char) in excluded {
                reversed.insert(char, at: reversed.index(reversed.startIndex, offsetBy: i))
            }
            return String(reversed)
        }
    }
    

    All this is doing is reversing all of the characters right away, but removing any characters that should not be reversed. Then it finds and maintains the indices and characters should not move. Finally, it inserts the characters that should not be reversed back to their original position.

    Usage:

    let text = "FogmEistEr"
    let omit = "E"
    print(text.reversed(omittingCharactersIn: CharacterSet(charactersIn: omit)))
    

    Prints:

    rtsiEmgoEF
    

    Here's how I'd plug it into a viewController assuming the reversal would happen either by a button push or by the text simply changing:

    class ViewController: UIViewController {
        @IBOutlet weak var originalTextField: UITextField!
    
        @IBOutlet weak var exclusionTextField: UITextField!
    
        @IBOutlet weak var reversedLabel: UILabel!
    
        override func viewDidLoad() {
            super.viewDidLoad()
            originalTextField.addTarget(self, action: #selector(textChanged(_:)), for: .editingChanged)
            exclusionTextField.addTarget(self, action: #selector(textChanged(_:)), for: .editingChanged)
        }
    
        @objc func textChanged(_ sender: UITextField) {
            tryReverse()
        }
    
        @IBAction func didTapButton(_ sender: UIButton) {
            tryReverse()
        }
    
        private func tryReverse() {
    
            guard let originalText = originalTextField.trimmedTextNilIfEmpty,
                  let exclusionText = exclusionTextField.trimmedTextNilIfEmpty else {
                reversedLabel.text = ""
                return
            }
    
            reversedLabel.text = originalText.components(separatedBy: " ")
                .map {
                    $0.reversed(omittingCharactersIn: CharacterSet(charactersIn: exclusionText))
                }.joined(separator: " ")
        }
    }
    
    extension UITextField {
        var trimmedTextNilIfEmpty: String? {
            if let trimmed = text?.trimmingCharacters(in: .whitespacesAndNewlines) {
                return trimmed.isEmpty ? nil : trimmed
            }
            return nil
        }
    }