RedMadRobot / input-mask-ios

User input masking library repo.
MIT License
578 stars 96 forks source link

Create custom Notation based on existed one. #67

Closed SergeyGamayunov closed 5 years ago

SergeyGamayunov commented 5 years ago

Hey,

my goal is to create mask that allows text with spaces and dashes. Documentation brings me to custom notation - I can create my own set of characters.

But it's pretty a lot of work there, however, InputMask already has notation that almost fits to my task - I just need add to it two more symbols.

Is it possible?

AlexisQapa commented 5 years ago

Just use pre defined characters set with unions like Notation(character: "N", characterSet: CharacterSet(charactersIn: "-").union(CharacterSet.letters), isOptional: false), Then use [N...]

taflanidi commented 5 years ago

Mr. @AlexisQapa, thanks for your help.

@SergeyGamayunov, please let us know if this solution fits your needs.

let listener: MaskedTextFieldDelegate
listener.customNotations = [
  Notation(
    character: "N",
    characterSet: CharacterSet(charactersIn: "-").union(CharacterSet.letters),
    isOptional: false
  ),
]
listener.primaryMaskFormat = "[N…]"
SergeyGamayunov commented 5 years ago

Hey, I just tested it. In a few words - yes, it does, but I had to create some nice workaround for convenient work. In case if you or anybody else would be interested:

That enum represents different character sets. We will combine them in the future.

enum Characters {
        case digits, letters, spaceDash, dotNumber

        var set: CharacterSet {
            switch self {
            case .digits:
                return CharacterSet.decimalDigits
            case .letters:
                return CharacterSet.letters
            case .spaceDash:
                return CharacterSet(charactersIn: " -")
            case .dotNumber:
                return CharacterSet(charactersIn: ".№")
            }
        }
    }

This enum represents different validation types, and variable for constructing custom notations for every validation type (which are also represented by enum):

enum ValidationType {
    case name, oms

    private var notations: [Notation] {
        var set: [Characters]
        var letter: Character
        switch self {
        case .name:
            set = [.letters, .spaceDash]
            letter = "N"
        case .oms:
            set = [.letters, .digits, .dotNumber, .spaceDash]
            letter = "O"
        default:
            return []
        }

        guard let allowedCharacters = characterSet(unionWith: set) else { return [] }
        return [Notation(character: letter, characterSet: allowedCharacters, isOptional: false)]
    }

Please, be aware, that this is a sample code, it also includes format, mask and listener variables which are not specific for current topic

taflanidi commented 5 years ago

Well, @SergeyGamayunov, your code looks overengineered to me.

First of all, why won't you just extend CharacterSet?
Second, your enum is just an abstraction for the sake of abstraction. Especially at this point:

case .digits:
    return CharacterSet.decimalDigits
case .letters:
    return CharacterSet.letters

Third, here,

guard let allowedCharacters = characterSet(unionWith: set) else { return [] }

— you are creating a disconnected coupling problem, which is a bad design smell.

Bad design like this eventually leads to bug misunderstanding.
In your particular case, MaskedTextFieldDelegate would throw a mask compilation error exception, while MaskedTextFieldDelegate nor its internals doesn't have anything with the actual bug: CharacterSet initialisation failure.

Fourth, a minor one. Please, replace

var set: [Characters]
var letter: Character

with

let set: [Characters]
let letter: Character