[Swift] バリデーションチェックは、正規表現で！

# はじめに
アプリの入力データをif文とAnd条件で数珠つなぎで判定していくとカオスになりますよね。
やはりここは、正規表現の出番ですね。

ただし、Swiftは、正規表現が扱いづらいため、
少しだけ簡単に利用できるようにします。

## Custom Operatorsを利用する

```swift:Regexp.swift
import UIKit

infix operator =~
infix operator !~

func =~(lhs: String, rhs: String) -> Bool {
    guard let regex = try? NSRegularExpression(pattern: rhs,
                                               options: NSRegularExpression.Options()) else {
                                                return false
    }

    return regex.numberOfMatches(in: lhs,
                                 options: NSRegularExpression.MatchingOptions(),
                                 range: NSRange(location: 0, length: lhs.characters.count)) > 0
}

func !~(lhs: String, rhs: String) -> Bool {
    return !(lhs=~rhs)
}
```

## 利用方法
よく使いそうなパターンをextensionに追加すると、下記のようになります。
正規表現については、ここでは触れません。

```swift:Swift+Validation.swift
import Foundation

extension String {

    func isNumeric() -> Bool{
        return (self =~ "(^$)|(^\\d*$)")
    }

    func isNumeric(length: Int) -> Bool {
        return (self =~ "(^$)|(^\\d{\(length)}$)")
    }

    func isUrl() -> Bool {
        return (self =~ "^(https?|ftp)(:\\/\\/[-_.!~*\\'()a-zA-Z0-9;\\/?:\\@&=+\\$,%#]+)$")
    }

    func isEmail() -> Bool {
        return (self =~ "^[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,4}$")
    }

    func isTelNo() -> Bool {
        return (self =~ "^\\d{2,4}-\\d{1,4}-\\d{4}$")
    }

    func isdate(format: String) -> Bool {
        return (self =~ "^\\d{4}\(format)\\d{1,2}\(format)\\d{1,2}$")
    }

    func isTime() -> Bool {
        return (self =~ "^\\d{1,2}:\\d{1,2}:\\d{1,2}$")
    }
}
```

## ユニットテスト

```swift
import XCTest
@testable import ios_Validation_demo

class ios_Validation_demoTests: XCTestCase {
    
    override func setUp() {
        super.setUp()
    }
    
    override func tearDown() {
        super.tearDown()
    }
    
    func testNumeric() {

        XCTAssertTrue("123456".isNumeric())
        XCTAssertTrue("12345".isNumeric(length: 5))

        XCTAssertFalse("1234a".isNumeric())
        XCTAssertFalse("12345".isNumeric(length: 4))
    }

    func testUrl() {

        XCTAssertTrue("http://www.yahoo.co.jp".isUrl())
        XCTAssertTrue("https://www.yahoo.co.jp".isUrl())

        XCTAssertFalse("https//www.yahoo.co.jp".isUrl())
        XCTAssertFalse("https:www.yahoo.co.jp".isUrl())
    }

    func testEmail() {

        XCTAssertTrue("rec@st-ventures.jp".isEmail())

        XCTAssertFalse("recst-ventures.jp".isEmail())
        XCTAssertFalse("rec@".isEmail())
        XCTAssertFalse("@st-ventures.jp".isEmail())
    }

    func testTelNo() {
        XCTAssertTrue("090-1234-5678".isTelNo())
        XCTAssertTrue("03-1234-5678".isTelNo())
        XCTAssertTrue("0120-1234-5678".isTelNo())

        XCTAssertFalse("1-1234-5678".isTelNo())
        XCTAssertFalse("1a-1234-5678".isTelNo())
        XCTAssertFalse("03-123a-5678".isTelNo())
        XCTAssertFalse("03-1234-5a78".isTelNo())
        XCTAssertFalse("0312345678".isTelNo())
        XCTAssertFalse("03-12345678".isTelNo())
        XCTAssertFalse("031234-5678".isTelNo())
    }

    func testDate() {
        XCTAssertTrue("2016-1-1".isdate(format:"-"))
        XCTAssertTrue("2016-12-19".isdate(format:"-"))
        XCTAssertTrue("2016/12/19".isdate(format:"/"))
        
        XCTAssertFalse("2016/12/19".isdate(format:"-"))
        XCTAssertFalse("20166-12-19".isdate(format:"-"))
        XCTAssertFalse("2016-123-19".isdate(format:"-"))
        XCTAssertFalse("2016-12-191".isdate(format:"-"))
        XCTAssertFalse("201-12-19".isdate(format:"-"))
        XCTAssertFalse("201612-19".isdate(format:"-"))
        XCTAssertFalse("2016-1219".isdate(format:"-"))
        XCTAssertFalse("201a-12-19".isdate(format:"-"))
        XCTAssertFalse("2016-1a-19".isdate(format:"-"))
        XCTAssertFalse("2016-12-1a".isdate(format:"-"))
    }

    func testTime() {
        XCTAssertTrue("20:10:10".isTime())
        XCTAssertTrue("1:1:1".isTime())

        XCTAssertFalse("10-10-10".isTime())
        XCTAssertFalse("123:10:10".isTime())
        XCTAssertFalse("12:123:10".isTime())
        XCTAssertFalse("12:12:123".isTime())
        XCTAssertFalse("1010:10".isTime())
        XCTAssertFalse("10:1010".isTime())
        XCTAssertFalse("1a:10:10".isTime())
        XCTAssertFalse("10:a0:10".isTime())
        XCTAssertFalse("10:10:1a".isTime())
    }
}
```

## まとめ
如何でしたか？
ちょっとだけ楽になりましたね。

本件について、[Github](https://github.com/stv-ekushida/ios-RegExp-demo)にアップしていますので、合わせて御覧ください。

以上です。