[Swift]iOSのデフォルトの関数を活用した Validation String Extension集


# 前置き
個人的に業務や自作でアプリを作るたびに、何回も同じようなことを調べて``Extension`` を作成しているので、メモ書き程度に残しておきたかった。

## String Extension Variables
### 【前後空白改行】

```.swift

extension String {

    var hasSpaceAndNewlines: Bool {
        return self.isEmpty || self.trimmingCharacters(in: .whitespacesAndNewlines) != self
    }

}

print("text".hasSpaceAndNewlines) // false 任意の文字
print("".hasSpaceAndNewlines)     // true 空
print(" ".hasSpaceAndNewlines)    // true 半角スペース
print("　".hasSpaceAndNewlines)    // true 全角スペース
print("\n".hasSpaceAndNewlines)  // true 改行
print("\n\n".hasSpaceAndNewlines)// true 改行2回
```
``.whitespacesAndNewlines ``以外にもオプションが存在する。

### 【地域・言語】

```.swift

extension String {

    /// 言語設定が「英語」かどうか ※1
    var isEnglish: Bool {
        return self.hasPrefix("en")
    }
    
    /// 言語設定が「日本語」かどうか
    var isJapanese: Bool {
        return self.hasPrefix("ja")
    }
    
    /// 地域設定が「日本語」かどうか ※2
    var isJapan: Bool {
        return self.hasSuffix("JP")
    }

}

/// ※１
print("ja".isJapanese)     // true
print("ja-EN".isJapanese)  // true
print("en-JP".isJapanese)  // false
print("aja-EN".isJapanese) // false

/// ※2
print("ja".isJapan)     // false
print("ja-EN".isJapan)  // false
print("en-JP".isJapan)  // true
print("aja-EN".isJapan) // false

```

``Locale.preferredLanguages``で端末の表示言語を取得できるので、そちらを使っても良かったが、サーバーからくる言語判定をしたかったので、少し広めに使い勝手良くしました。
上記以外の言語・地域はお好みで``Extension``に追加してくだしい。

### 【日本語判定】

```.swift

extension String {

    /// 「漢字」かどうか
    var isKanji: Bool {
        let range = "^[\u{3005}\u{3007}\u{303b}\u{3400}-\u{9fff}\u{f900}-\u{faff}\u{20000}-\u{2ffff}]+$"
        return NSPredicate(format: "SELF MATCHES %@", range).evaluate(with: self)
    }
    
    /// 「ひらがな」かどうか
    var isHiragana: Bool {
        let range = "^[ぁ-ゞ]+$"
        return NSPredicate(format: "SELF MATCHES %@", range).evaluate(with: self)
    }
    
    /// 「カタカナ」かどうか
    var isKatakana: Bool {
        let range = "^[ァ-ヾ]+$"
        return NSPredicate(format: "SELF MATCHES %@", range).evaluate(with: self)
    }
    
}

print("otoko".isKanji)   // false
print("男".isKanji)      // true
print("男otoko".isKanji) // false
```
長文や「ひらがな」「カタカナ」が入り混じる場合は``range``をがっちゃんこする必要があります。

### 【日本語変換】

```.swift

extension String {

    /// 「ひらがな」に変換 ※１
    var toHiragana: String? {
        return self.applyingTransform(.hiraganaToKatakana, reverse: false)
    }
    
    /// 「カタカナ」に変換
    var toKatakana: String? {
        return self.applyingTransform(.hiraganaToKatakana, reverse: true)
    }
    
    /// 「ひらがな」を含むかどうか ※2
    var hasHiragana: Bool {
        guard let hiragana = self.toKatakana else { return false }
        return self != hiragana // １文字でもカタカナに変換されている場合は含まれると断定できる
    }
    
    /// 「カタカナ」を含むかどうか
    var hasKatakana: Bool {
        guard let katakana = self.toHiragana else { return false }
        return self != katakana // １文字でもひらがなに変換されている場合は含まれると断定できる
    }

}

/// ※１
print("hiragana".toHiragana) // optional("hiragana") 英語はそのまま
print("ひらがな".toHiragana)   // optional("ひらがな") ひらがなはそのまま
print("ヒラガナ".toHiragana)   // optional("ひらがな") 変換された

/// ※2
print("hiragana".hasHiragana)             // false
print("ひらがな".hasHiragana)               // true
print("ヒラガナ".hasHiragana)               // false
print("hiraganaひらがなヒラガナ".hasHiragana) // true
```
``applyingTransform ``でサクッと作れる。``.hiraganaToKatakana``以外にもオプションがあります。
単語や分に含まれるかどうかは、先ほどの``NSPredicate``を使ったバリデーションでも含まれるかの判定を作れるが、長くなってしまうのでこちらで作る方が短くて完結かと。

### 【英語大文字小文字】

```.swift
extension String {

    /// 「小文字だけ」かどうか
    var isLowrcased: Bool {
        return self == self.lowercased()
    }
    
    /// 「大文字だけ」かどうか
    var isUppercased: Bool {
        return self == self.uppercased()
    }

}

print("english".isLowrcased) // true
print("English".isLowrcased) // false
print("ひらがな".isLowrcased) // true ひらがなは変換されないのでtrueとなる
```
英語にしか使えないので、ちゃんと使いたい方は``NSPredicate``でバリデーションしてくだしい。

### 【URL】

```swift
extension String {

   var isUrl: Bool {
       let linkValidation = NSTextCheckingResult.CheckingType.link.rawValue
       guard let detector = try? NSDataDetector(types: linkValidation) else { return false }

       let results = detector.matches(in: self, options: .reportCompletion, range: NSMakeRange(0, self.characters.count))
       return results.first?.url != nil
    }
    
}

print("http".isUrl)      // false
print("https".isUrl)     // false
print("http://".isUrl)   // false
print("https://".isUrl)  // false
print("http://a".isUrl)  // true
print("https://a".isUrl) // true
```
デフォルトで画像の拡張子あたりまでバリデーションしてくれたら、なおよかったんですけど笑
参考に載せましたが、 ``NSTextCheckingResult ``は``PhoneNumber``,``Address``とかもあります。

### 全部まとめたやつ

```.swift

extension String {

    /// 前後にスペース、改行があるかどうか
    var hasSpaceAndNewlines: Bool {
        return self.isEmpty || self.trimmingCharacters(in: .whitespacesAndNewlines).characters.isEmpty
    }
    
    /// 言語設定が「英語」かどうか
    var isEnglish: Bool {
        return self.hasPrefix("en")
    }
    
    /// 言語設定が「日本語」かどうか
    var isJapanese: Bool {
        return self.hasPrefix("ja")
    }

    /// 地域設定が「日本語」かどうか ※2
    var isJapan: Bool {
        return self.hasSuffix("JP")
    }
    
    /// 「漢字」かどうか
    var isKanji: Bool {
        let range = "^[\u{3005}\u{3007}\u{303b}\u{3400}-\u{9fff}\u{f900}-\u{faff}\u{20000}-\u{2ffff}]+$"
        return NSPredicate(format: "SELF MATCHES %@", range).evaluate(with: self)
    }
    
    /// 「ひらがな」かどうか
    var isHiragana: Bool {
        let range = "^[ぁ-ゞ]+$"
        return NSPredicate(format: "SELF MATCHES %@", range).evaluate(with: self)
    }
    
    /// 「カタカナ」かどうか
    var isKatakana: Bool {
        let range = "^[ァ-ヾ]+$"
        return NSPredicate(format: "SELF MATCHES %@", range).evaluate(with: self)
    }
    
    /// 「ひらがな」に変換 ※１
    var toHiragana: String? {
        return self.applyingTransform(.hiraganaToKatakana, reverse: false)
    }
    
    /// 「カタカナ」に変換
    var toKatakana: String? {
        return self.applyingTransform(.hiraganaToKatakana, reverse: true)
    }
    
    /// 「ひらがな」を含むかどうか ※2
    var hasHiragana: Bool {
        guard let hiragana = self.toKatakana else { return false }
        return self != hiragana // １文字でもカタカナに変換されている場合は含まれると断定できる
    }
    
    /// 「カタカナ」を含むかどうか
    var hasKatakana: Bool {
        guard let katakana = self.toHiragana else { return false }
        return self != katakana // １文字でもひらがなに変換されている場合は含まれると断定できる
    }
    
    /// 「小文字だけ」かどうか
    var isLowrcased: Bool {
        return self == self.lowercased()
    }
    
    /// 「大文字だけ」かどうか
    var isUppercased: Bool {
        return self == self.uppercased()
    }

    /// URLかどうか
    var isUrl: Bool {
        let linkValidation = NSTextCheckingResult.CheckingType.link.rawValue
        guard let detector = try? NSDataDetector(types: linkValidation) else { return false }

        let results = detector.matches(in: self, options: .reportCompletion, range: NSMakeRange(0, self.characters.count))
        return results.first?.url != nil
    }
    
}
```




# あとがき
わりとピンポイントな使い所のものしかないので、「あ、これ！」みたいなやつはなかったかと思います。笑

今回は自分のプロジェクトの使い勝手が良いように作成しているので、上記はあくまでも指標程度に考えていただき、みなさんの使い勝手が良いように書き換えてくだされば、と思っております。

## 参考

### 言語・地域
- [Swiftで文字列の接頭・接尾が任意の文字列と一致しているか確認する方法](http://qiita.com/hiroo0529/items/dfa8776b187fd84c13c3)
- [端末の表示言語を読み取り、言語ごとに分岐する方法まとめ](http://daikimochizuki.jp/2017/01/20/get_language/)

### ひらがな・カタカナ

- [Swift3.0 で ひらがな <-> カタカナ 変換する](http://qiita.com/star__hoshi/items/5104d9ce5d07873e851d)
- [[iOS][Swift]長いパターンの正規表現を扱う方法](http://dev.classmethod.jp/smartphone/iphone/long-regex-pattern-for-ios-swift/)
- [[Swift3]文字列が人名かどうかをバリデーションする方法](http://qiita.com/a_jike/items/444ded923e308396237e)
- [Swiftで入力された文字をカタカナに変換して取得する](http://qiita.com/masashi-sutou/items/8ce4ee9ce0630daffadb)
- [文字列を全角/半角に変換する](http://qiita.com/su_k/items/1dd6c9381bdd39c6cb64)
- [正規表現の使用方法](https://so-zou.jp/software/tech/programming/tech/regular-expression/meta-character/variable-width-encoding.htm)

### URL
- [NSTextCheckingTypeを使って正しいリンク形式かどうかを判断する](http://qiita.com/Kyam28/items/c7ac4c464cbf84c4d555)
- [SwiftのNSDataDetectorクラスで文字列内にあるURLを検出する](http://qiita.com/niwasawa/items/6237989e60a3a0155ff2)

### その他
Phone and Address Validations
1. [Check if exists phone number in String](https://stackoverflow.com/questions/44042344/check-if-exists-phone-number-in-string)
2. [Extracting address elements from a String using NSDataDetector in Swift 3.0](https://stackoverflow.com/questions/41077174/extracting-address-elements-from-a-string-using-nsdatadetector-in-swift-3-0)
3. [正则表达式基础](http://www.jianshu.com/p/b23f04125629)