NSPredicateを使うと@を全角半角区別してくれない
emailのvalidationメソッド
業務中、emailのvalidationメソッドをいじることになって知った話
下記のようなコードでemailのvalidationチェックを正規表現を行なっていたのだが、 @
が全角でも true
が返ってきてしまうことが発覚した。(emailの正規表現に関しては正確ではないので、そこは雰囲気で)
func isValidEmail(string: String) -> Bool { let emailReg = "[A-Za-z0-9._%+-]+[A-Za-z0-9]+@[A-Za-z0-9]+[A-Za-z0-9.-]+\.[A-Za-z]{2,6}" let predicateMail = NSPredicate(format:"SELF MATCHES %@", emailReg) return predicateMail.evaluate(with: string) }
Androidでは全角半角区別されていた
同じプロジェクトのAndroid側では似た処理を行なっていたが、 @
が半角の時のみvalidとして扱われていたので、その振る舞いに疑問を覚え、Discord上のios-developers-japanコミュニティに質問投げた。
NSPredicate
使ってることもあって、iOSでもmacOSでももはや @
は全角半角区別されない仕様なのかと思っていた。
SwiftならNSRegularExpressionがある
そもそも NSPredicate
を前任が使って書いていたり、自分がiOS上で正規表現を使ったことなくて知識が乏しく気づけなかったのだが、NSRegularExpression
クラスというのがあるのを知らなかった。(見たことある気もするけど、覚えてない)
んでこいつを使えば、下記のように判定はできるということを教えてもらった。
// loveeさんから提示のコード let stringA = "123@456" let stringB = "123@456" let rangeA = stringA.range(of: "@[0-9]", options: .regularExpression, range: nil, locale: nil) //.some let rangeB = stringB.range(of: "@[0-9]", options: .regularExpression, range: nil, locale: nil) //nil
// omochimetaruさんから提示のコード func isValidEmailFormat2(string: String) -> Bool { let emailReg = "[a-zA-Z0-9._%+-/:~$^()&!']+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,4}" // valid regexp let regexp = try! NSRegularExpression.init(pattern: emailReg, options: []) let nsString = string as NSString let matchRet = regexp.firstMatch(in: string, options: [], range: NSRange.init(location: 0, length: nsString.length)) return matchRet != nil }
また、NSPredicate
よりNSRegularExpression
の方が標準的だろうというお話も出た。
最終的には…
NSRegularExpression
を使えば、@
の全角半角の判別を正常にできるのがわかったわけだが、諸事情(時間的と引き継いだコードで振る舞いを変えづらい)により、文字列がascii文字かの判定を別途足し方式でvalidationを行なった。
var email1 = "hoge@gmail.com" email1.canBeConverted(to: .ascii) // true var email2 = "hoge@gmail.com" email2.canBeConverted(to: .ascii) // false