文字列が許可された文字かどうかをNSCharacterSetで判定する

役に立つかどうかよくわからないTipsシリーズ

また今回も文字列の拡張に関することです

例えばテキストフィールドなどで入力された文字などに
入力値チェックをすることも多いかと思います

そこには「アルファベットしか入れられないよ」とか「数字だけだよー」とか
そういう仕様はよくあるんじゃないかと思います

それを実現する方法としては
例えば正規表現でチェックするという手段もありますし
他にも色々とありそうです

その中から今回は「キャラクターセット」で判定を行う方法で
`String` の拡張をしてみますね

## ベースメソッドを用意する

挙動を実現するためのベースとなるメソッドです
3行だけでシンプルに書けました

```swift
extension String {

    public func isOnly(structuredBy chars: String) -> Bool {
        let characterSet = NSMutableCharacterSet()
        characterSet.addCharactersInString(chars)
        return self.stringByTrimmingCharactersInSet(characterSet).length <= 0
    }
}
```

このメソッドは
**渡した`chars`に書かれている文字列のみで自身が構成されているか**
を返すメソッドです

例えば

```swift
"ABC".isOnly(structuredBy: "ABCDEF") // true
"ABC".isOnly(structuredBy: "BCDEFG") // false
"ABC".isOnly(structuredBy: "abcdef") // false 
```

最初の1行目は
文字列`"ABC"`は、`"A"`、`"B"`、`"C"`、`"D"`、`"E"`、`"F"`のいずれかのみで構成されているか?
ということですので`true`を返します

2行目は
`"BCDEFG"`の中に`"A"`がないので`false`

3行目は
大文字と小文字はちゃんと区別しますよーということです

## ラッピングプロパティを作る

ベースメソッドだけ用意すれば、なんなりとできるのですが
よく使うパターンをラッピングプロパティとして用意しておけば便利です

これはアプリの仕様に合わせ、必要に応じて追加・削除をしてもらえばと思いますが
普段から使えるものとして以下を用意しました

```swift
extension String {

    /// 半角数字のみで構成されているかどうか
    public var isOnlyNumber: Bool {
        return self.isOnly(structuredBy: "0123456789")
    }
    
    /// 半角アルファベットのみで構成されているかどうか
    public var isOnlyAlphabet: Bool {
        return self.isOnly(structuredBy: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
    }
    
    /// 半角英数字のみで構成されているかどうか
    public var isOnlyAlphabetAndNumber: Bool {
        let chars = String.structureOfAlphabetAndNumber
        return self.isOnly(structuredBy: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
    }
}
```

プロパティ化しておくことで、可読性もよくなりますし、コードもすっきりします

```swift
"ABC".isOnlyNumber // false
"ABC".isOnlyAlphabetAndNumber // true
```

ただし、なんだかメソッド定義側が
ガチャガチャしてる気がします

もうひと手間足してみることにします

```swift
extension String {
    
    /// 数字
    public static let structureOfNumber = "0123456789"
    
    /// 小英字
    public static let structureOfLowercaseAlphabet = "abcdefghijklmnopqrstuvwxyz"
    
    /// 大英字
    public static let structureOfUppercaseAlphabet = structureOfLowercaseAlphabet.uppercaseString
    
    /// 英字
    public static let structureOfAlphabet = structureOfLowercaseAlphabet + structureOfUppercaseAlphabet
    
    /// 英数字
    public static let structureOfAlphabetAndNumber = structureOfAlphabet + structureOfNumber
    
    /// 半角数字のみで構成されているかどうか
    public var isOnlyNumber: Bool {        
        let chars = String.structureOfNumber
        return self.isOnly(structuredBy: chars)
    }
    
    /// 半角アルファベットのみで構成されているかどうか
    public var isOnlyAlphabet: Bool {
        let chars = String.structureOfAlphabet
        return self.isOnly(structuredBy: chars)
    }
    
    /// 半角英数字のみで構成されているかどうか
    public var isOnlyAlphabetAndNumber: Bool {
        let chars = String.structureOfAlphabetAndNumber
        return self.isOnly(structuredBy: chars)
    }
}
```

`isOnly(structuredBy:)`メソッドに渡す`chars`を
`String`のクラス定数として小分けに定義しました

先ほどの3つのメソッドの中身が整理できました
`"0123456789"`を3回も書くなんて非効率でしたのでね


これらを用意することにより、
拡張性も少しだけあがりました
例えば半角英数字に加えて半角スペースも許可するならこんな感じのプロパティを用意します

```swift
extension String {
    
    public var isOnlyAlphabetAndNumberAndSpace: Bool {
        let chars = String.structureOfAlphabetAndNumber + " "
        return self.isOnly(structuredBy: chars)
    }
}
```

電話番号のような数字とハイフンのみを許可するなら

```swift
extension String {
    
    public var isOnlyNumberAndHyphen: Bool {
        let chars = String.structureOfNumber + "-"
        return self.isOnly(structuredBy: chars)
    }
}
```
こんな感じでしょうか

電話番号やメールアドレスなどの形式があっているかどうかのバリデーションチェックをするなら
やはり冒頭に書いたとおり正規表現を用いるなどする必要もありますが

入力された文字がまず許可していい値かどうかを判定する用途等であれば
これだけでなかなか強力な機能となります