I am using a instance of UIWebView to process some text and color it correctly, it gives the result as HTML but rather than displaying it in the UIWebView I want to display it using Core Text with a NSAttributedString.
I am able to create and draw the NSAttributedString but I am unsure how I can convert and map the HTML into the attributed string.
I understand that under Mac OS X NSAttributedString has a initWithHTML: method but this was a Mac only addition and is not available for iOS.
I also know that there is a similar question to this but it had no answers, I though I would try again and see whether anyone has created a way to do this and if so, if they could share it.
In iOS 7, UIKit added an initWithData:options:documentAttributes:error: method which can initialize an NSAttributedString using HTML, eg:
[[NSAttributedString alloc] initWithData:[htmlString dataUsingEncoding:NSUTF8StringEncoding]
options:#{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType,
NSCharacterEncodingDocumentAttribute: #(NSUTF8StringEncoding)}
documentAttributes:nil error:nil];
In Swift:
let htmlData = NSString(string: details).data(using: String.Encoding.unicode.rawValue)
let options = [NSAttributedString.DocumentReadingOptionKey.documentType:
NSAttributedString.DocumentType.html]
let attributedString = try? NSMutableAttributedString(data: htmlData ?? Data(),
options: options,
documentAttributes: nil)
There is a work-in-progress open source addition to NSAttributedString by Oliver Drobnik at Github. It uses NSScanner for HTML parsing.
Creating an NSAttributedString from HTML must be done on the main thread!
Update: It turns out that NSAttributedString HTML rendering depends on WebKit under the hood, and must be run on the main thread or it will occasionally crash the app with a SIGTRAP.
New Relic crash log:
Below is an updated thread-safe Swift 2 String extension:
extension String {
func attributedStringFromHTML(completionBlock:NSAttributedString? ->()) {
guard let data = dataUsingEncoding(NSUTF8StringEncoding) else {
print("Unable to decode data from html string: \(self)")
return completionBlock(nil)
}
let options = [NSDocumentTypeDocumentAttribute : NSHTMLTextDocumentType,
NSCharacterEncodingDocumentAttribute: NSNumber(unsignedInteger:NSUTF8StringEncoding)]
dispatch_async(dispatch_get_main_queue()) {
if let attributedString = try? NSAttributedString(data: data, options: options, documentAttributes: nil) {
completionBlock(attributedString)
} else {
print("Unable to create attributed string from html string: \(self)")
completionBlock(nil)
}
}
}
}
Usage:
let html = "<center>Here is some <b>HTML</b></center>"
html.attributedStringFromHTML { attString in
self.bodyLabel.attributedText = attString
}
Output:
Swift initializer extension on NSAttributedString
My inclination was to add this as an extension to NSAttributedString rather than String. I tried it as a static extension and an initializer. I prefer the initializer which is what I've included below.
Swift 4
internal convenience init?(html: String) {
guard let data = html.data(using: String.Encoding.utf16, allowLossyConversion: false) else {
return nil
}
guard let attributedString = try? NSAttributedString(data: data, options: [.documentType: NSAttributedString.DocumentType.html, .characterEncoding: String.Encoding.utf8.rawValue], documentAttributes: nil) else {
return nil
}
self.init(attributedString: attributedString)
}
Swift 3
extension NSAttributedString {
internal convenience init?(html: String) {
guard let data = html.data(using: String.Encoding.utf16, allowLossyConversion: false) else {
return nil
}
guard let attributedString = try? NSMutableAttributedString(data: data, options: [NSAttributedString.DocumentReadingOptionKey.documentType: NSAttributedString.DocumentType.html], documentAttributes: nil) else {
return nil
}
self.init(attributedString: attributedString)
}
}
Example
let html = "<b>Hello World!</b>"
let attributedString = NSAttributedString(html: html)
This is a String extension written in Swift to return a HTML string as NSAttributedString.
extension String {
func htmlAttributedString() -> NSAttributedString? {
guard let data = self.dataUsingEncoding(NSUTF16StringEncoding, allowLossyConversion: false) else { return nil }
guard let html = try? NSMutableAttributedString(data: data, options: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType], documentAttributes: nil) else { return nil }
return html
}
}
To use,
label.attributedText = "<b>Hello</b> \u{2022} babe".htmlAttributedString()
In the above, I have purposely added a unicode \u2022 to show that it renders unicode correctly.
A trivial: The default encoding that NSAttributedString uses is NSUTF16StringEncoding (not UTF8!).
Swift 3.0 Xcode 8 Version
func htmlAttributedString() -> NSAttributedString? {
guard let data = self.data(using: String.Encoding.utf16, allowLossyConversion: false) else { return nil }
guard let html = try? NSMutableAttributedString(data: data, options: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType], documentAttributes: nil) else { return nil }
return html
}
Made some modification on Andrew's solution and update the code to Swift 3:
This code now use UITextView as self and able to inherit its original font, font size and text color
Note: toHexString() is extension from here
extension UITextView {
func setAttributedStringFromHTML(_ htmlCode: String, completionBlock: #escaping (NSAttributedString?) ->()) {
let inputText = "\(htmlCode)<style>body { font-family: '\((self.font?.fontName)!)'; font-size:\((self.font?.pointSize)!)px; color: \((self.textColor)!.toHexString()); }</style>"
guard let data = inputText.data(using: String.Encoding.utf16) else {
print("Unable to decode data from html string: \(self)")
return completionBlock(nil)
}
DispatchQueue.main.async {
if let attributedString = try? NSAttributedString(data: data, options: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType], documentAttributes: nil) {
self.attributedText = attributedString
completionBlock(attributedString)
} else {
print("Unable to create attributed string from html string: \(self)")
completionBlock(nil)
}
}
}
}
Example usage:
mainTextView.setAttributedStringFromHTML("<i>Hello world!</i>") { _ in }
Swift 4
NSAttributedString convenience initializer
Without extra guards
throws error
extension NSAttributedString {
convenience init(htmlString html: String) throws {
try self.init(data: Data(html.utf8), options: [
.documentType: NSAttributedString.DocumentType.html,
.characterEncoding: String.Encoding.utf8.rawValue
], documentAttributes: nil)
}
}
Usage
UILabel.attributedText = try? NSAttributedString(htmlString: "<strong>Hello</strong> World!")
Using of NSHTMLTextDocumentType is slow and it is hard to control styles. I suggest you to try my library which is called Atributika. It has its own very fast HTML parser. Also you can have any tag names and define any style for them.
Example:
let str = "<strong>Hello</strong> World!".style(tags:
Style("strong").font(.boldSystemFont(ofSize: 15))).attributedString
label.attributedText = str
You can find it here https://github.com/psharanda/Atributika
The only solution you have right now is to parse the HTML, build up some nodes with given point/font/etc attributes, then combine them together into an NSAttributedString. It's a lot of work, but if done correctly, can be reusable in the future.
Swift 3:
Try this:
extension String {
func htmlAttributedString() -> NSAttributedString? {
guard let data = self.data(using: String.Encoding.utf16, allowLossyConversion: false) else { return nil }
guard let html = try? NSMutableAttributedString(
data: data,
options: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType],
documentAttributes: nil) else { return nil }
return html
}
}
And for using:
let str = "<h1>Hello bro</h1><h2>Come On</h2><h3>Go sis</h3><ul><li>ME 1</li><li>ME 2</li></ul> <p>It is me bro , remember please</p>"
self.contentLabel.attributedText = str.htmlAttributedString()
The above solution is correct.
[[NSAttributedString alloc] initWithData:[htmlString dataUsingEncoding:NSUTF8StringEncoding]
options:#{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType,
NSCharacterEncodingDocumentAttribute: #(NSUTF8StringEncoding)}
documentAttributes:nil error:nil];
But the app wioll crash if you are running it on ios 8.1,2 or 3.
To avoid the crash what you can do is : run this in a queue. So that it always be on main thread.
Add this Extension and then use Text. after using this code we can use
custom size of our text.
extension Text {
init(html htmlString: String,
raw: Bool = false,
size: CGFloat? = nil,
fontFamily: String = "-apple-system") {
let fullHTML: String
if raw {
fullHTML = htmlString
} else {
var sizeCss = ""
if let size = size {
sizeCss = "font-size: \(size)px;"
}
fullHTML = """
<!doctype html>
<html>
<head>
<style>
body {
font-family: \(fontFamily);
\(sizeCss)
}
</style>
</head>
<body>
\(htmlString)
</body>
</html>
"""
}
let attributedString: NSAttributedString
if let data = fullHTML.data(using: .unicode),
let attrString = try? NSAttributedString(data: data,
options: [.documentType: NSAttributedString.DocumentType.html],
documentAttributes: nil) {
attributedString = attrString
} else {
attributedString = NSAttributedString()
}
self.init(attributedString)
}
init(_ attributedString: NSAttributedString) {
self.init("")
attributedString.enumerateAttributes(in: NSRange(location: 0, length: attributedString.length), options: []) { (attrs, range, _) in
let string = attributedString.attributedSubstring(from: range).string
var text = Text(string)
if let font = attrs[.font] as? UIFont {
text = text.font(.init(font))
}
if let color = attrs[.foregroundColor] as? UIColor {
text = text.foregroundColor(Color(color))
}
if let kern = attrs[.kern] as? CGFloat {
text = text.kerning(kern)
}
if #available(iOS 14.0, *) {
if let tracking = attrs[.tracking] as? CGFloat {
text = text.tracking(tracking)
}
}
if let strikethroughStyle = attrs[.strikethroughStyle] as? NSNumber, strikethroughStyle != 0 {
if let strikethroughColor = (attrs[.strikethroughColor] as? UIColor) {
text = text.strikethrough(true, color: Color(strikethroughColor))
} else {
text = text.strikethrough(true)
}
}
if let underlineStyle = attrs[.underlineStyle] as? NSNumber,
underlineStyle != 0 {
if let underlineColor = (attrs[.underlineColor] as? UIColor) {
text = text.underline(true, color: Color(underlineColor))
} else {
text = text.underline(true)
}
}
if let baselineOffset = attrs[.baselineOffset] as? NSNumber {
text = text.baselineOffset(CGFloat(baselineOffset.floatValue))
}
self = self + text
}
}
}
The built in conversion always sets the text color to UIColor.black, even if you pass an attributes dictionary with .forgroundColor set to something else. To support DARK mode on iOS 13, try this version of the extension on NSAttributedString.
extension NSAttributedString {
internal convenience init?(html: String) {
guard
let data = html.data(using: String.Encoding.utf16, allowLossyConversion: false) else { return nil }
let options : [DocumentReadingOptionKey : Any] = [
.documentType: NSAttributedString.DocumentType.html,
.characterEncoding: String.Encoding.utf8.rawValue
]
guard
let string = try? NSMutableAttributedString(data: data, options: options,
documentAttributes: nil) else { return nil }
if #available(iOS 13, *) {
let colour = [NSAttributedString.Key.foregroundColor: UIColor.label]
string.addAttributes(colour, range: NSRange(location: 0, length: string.length))
}
self.init(attributedString: string)
}
}
Here is the Swift 5 version of Mobile Dan's answer:
public extension NSAttributedString {
convenience init?(_ html: String) {
guard let data = html.data(using: .unicode) else {
return nil
}
try? self.init(data: data, options: [.documentType: NSAttributedString.DocumentType.html], documentAttributes: nil)
}
}
Helpful Extensions
Inspired by this thread, a pod, and Erica Sadun's ObjC example in iOS Gourmet Cookbook p.80, I wrote an extension on String and on NSAttributedString to go back and forth between HTML plain-strings and NSAttributedStrings and vice versa -- on GitHub here, which I have found helpful.
The signatures are (again, full code in a Gist, link above):
extension NSAttributedString {
func encodedString(ext: DocEXT) -> String?
static func fromEncodedString(_ eString: String, ext: DocEXT) -> NSAttributedString?
static func fromHTML(_ html: String) -> NSAttributedString? // same as above, where ext = .html
}
extension String {
func attributedString(ext: DocEXT) -> NSAttributedString?
}
enum DocEXT: String { case rtfd, rtf, htm, html, txt }
honoring font family, dynamic font I've concocted this abomination:
extension NSAttributedString
{
convenience fileprivate init?(html: String, font: UIFont? = Font.dynamic(style: .subheadline))
{
guard let data = html.data(using: String.Encoding.utf8, allowLossyConversion: true) else {
var totalString = html
/*
https://stackoverflow.com/questions/32660748/how-to-use-apples-new-san-francisco-font-on-a-webpage
.AppleSystemUIFont I get in font.familyName does not work
while -apple-system does:
*/
var ffamily = "-apple-system"
if let font = font {
let lLDBsucks = font.familyName
if !lLDBsucks.hasPrefix(".appleSystem") {
ffamily = font.familyName
}
totalString = "<style>\nhtml * {font-family: \(ffamily) !important;}\n </style>\n" + html
}
guard let data = totalString.data(using: String.Encoding.utf8, allowLossyConversion: true) else {
return nil
}
assert(Thread.isMainThread)
guard let attributedText = try? NSAttributedString(data: data, options: [.documentType: NSAttributedString.DocumentType.html, .characterEncoding: String.Encoding.utf8.rawValue], documentAttributes: nil) else {
return nil
}
let mutable = NSMutableAttributedString(attributedString: attributedText)
if let font = font {
do {
var found = false
mutable.beginEditing()
mutable.enumerateAttribute(NSAttributedString.Key.font, in: NSMakeRange(0, attributedText.length), options: NSAttributedString.EnumerationOptions(rawValue: 0)) { (value, range, stop) in
if let oldFont = value as? UIFont {
let newsize = oldFont.pointSize * 15 * Font.scaleHeruistic / 12
let newFont = oldFont.withSize(newsize)
mutable.addAttribute(NSAttributedString.Key.font, value: newFont, range: range)
found = true
}
}
if !found {
// No font was found - do something else?
}
mutable.endEditing()
// mutable.addAttribute(.font, value: font, range: NSRange(location: 0, length: mutable.length))
}
self.init(attributedString: mutable)
}
}
alternatively you can use the versions this was derived from and set
font on UILabel after setting attributedString
this will clobber the size and boldness encapsulated in the attributestring though
kudos for reading through all the answers up to here.
You are a very patient man woman or child.
A function to convert html to Attributed NSAttributedString that will adapt dynamic size + adapt accessibility for the text.
static func convertHtml(string: String?) -> NSAttributedString? {
guard let string = string else {return nil}
guard let data = string.data(using: .utf8) else {
return nil
}
do {
let attrStr = try NSAttributedString(data: data,
options: [.documentType: NSAttributedString.DocumentType.html, .characterEncoding: String.Encoding.utf8.rawValue],
documentAttributes: nil)
let range = NSRange(location: 0, length: attrStr.length)
let str = NSMutableAttributedString(attributedString: attrStr)
str.enumerateAttribute(NSAttributedString.Key.font, in: NSMakeRange(0, str.length), options: .longestEffectiveRangeNotRequired) {
(value, range, stop) in
if let font = value as? UIFont {
let userFont = UIFontDescriptor.preferredFontDescriptor(withTextStyle: .title2)
let pointSize = userFont.withSize(font.pointSize)
let customFont = UIFont.systemFont(ofSize: pointSize.pointSize)
let dynamicText = UIFontMetrics.default.scaledFont(for: customFont)
str.addAttribute(NSAttributedString.Key.font,
value: dynamicText,
range: range)
}
}
str.addAttribute(NSAttributedString.Key.underlineStyle, value: 0, range: range)
return NSAttributedString(attributedString: str.attributedSubstring(from: range))
} catch {}
return nil
}
To use:
let htmToStringText = convertHtml(string: html)
self.bodyTextView.isEditable = false
self.bodyTextView.isAccessibilityElement = true
self.bodyTextView.adjustsFontForContentSizeCategory = true
self.bodyTextView.attributedText = htmToStringText
self.bodyTextView.accessibilityAttributedLabel = htmToStringText
In my application, there are a few textfields with decimal keyboard input. So I need a function to validate the number.
func valueCheck(check: Double) -> Double{
let myRegex = "^(?:|0|[1-9]\\d*)(?:\\.\\d*)?$"
if check != nil && let match = check.rangeOfString(myRegex, options: .RegularExpressionSearch){
return check
}else{
return 0.0
}
}
If the number is not nil or invalid such as a few dots, then return the number. If the number is nil or invalid then return 0.0
I want to use regex but I have no idea how to use it in swift. Any help appreciated.
class func regexMatch(source:String,regexStr:String) -> Bool{
let regex: NSRegularExpression?
do{
try regex = NSRegularExpression(
pattern: regexStr,
options: .CaseInsensitive)
}catch{
return false
}
if let matches = regex?.matchesInString(source,
options: NSMatchingOptions(rawValue: 0),
range: NSMakeRange(0, source.characters.count)) {
return matches.count > 0
} else {
return false
}
}
Swift 2
import Foundation
func valueCheck(d: Double) -> Double {
var result = 0.0
do {
let regex = try NSRegularExpression(pattern: "^(?:|0|[1-9]\\d*)(?:\\.\\d*)?$", options: [])
let results = regex.matchesInString(String(d), options:[], range: NSMakeRange(0, String(d).characters.count))
if results.count > 0 {result = d}
} catch let error as NSError {
print("invalid regex: \(error.localizedDescription)")
}
return result
}
Swift 3
import Foundation
func valueCheck(_ d: Double) -> Double {
var result = 0.0
do {
let regex = try RegularExpression(pattern: "^(?:|0|[1-9]\\d*)(?:\\.\\d*)?$", options: [])
let results = regex.matches(in: String(d), options: [], range: NSMakeRange(0, String(d).characters.count))
if results.count > 0 {result = d}
} catch let error as NSError {
print("invalid regex: \(error.localizedDescription)")
}
return result
}
am working on a weather app and i want to generate the image according to the degrees number witch is in a string so how can i extract the number from string
this is the string:
Mostly dry. Very mild (max 19ºC on Sat afternoon, min 15ºC on Sunnight). Wind will be generally light.
#IBAction func weatherButton(sender: UIButton) {
let url = NSURL(string: "http://www.weather-forecast.com/locations/" + cityNameTextField.text.stringByReplacingOccurrencesOfString(" ", withString: "-") + "/forecasts/latest")
if url != nil {
let task = NSURLSession.sharedSession().dataTaskWithURL(url!){ (data, response, error) in
var urlError = false
var weather = ""
if error == nil {
var urlContent = NSString(data: data, encoding: NSUTF8StringEncoding)
var urlContentArray = urlContent!.componentsSeparatedByString("<span class=\"phrase\">")
if urlContentArray.count > 0 {
var weatherArray = urlContentArray[1].componentsSeparatedByString("</span>")
weather = weatherArray[0] as! String
weather = weather.stringByReplacingOccurrencesOfString("°", withString: "º")
println(weather)
}
else {
urlError = true
}
}
else {
urlError = true
}
dispatch_async(dispatch_get_main_queue()) {
if urlError == true {
self.showError()
}
else {
self.weatherFact.text = weather
}
}
}
task.resume()
}
else {
showError()
}
}
Another solution using NSRegularExpression.
The result is an Array of the numbers and the regex considers also temperatures below zero
For Swift 1.2:
let string = "Mostly dry. Very mild (max -19ºC on Sat afternoon, min 15ºC on Sunnight). Wind will be generally light."
let regex = NSRegularExpression(pattern: "-?[0-9]{1,3}", options: NSRegularExpressionOptions(), error: nil)
if let matches = regex?.matchesInString(string, options: NSMatchingOptions(), range: NSRange(location:0, length:count(string))) {
let degrees = matches.map {return (string as NSString).substringWithRange($0.range).toInt()! }
println(degrees) // -> [-19, 15]
}
For Swift 2.0:
let string = "Mostly dry. Very mild (max -19ºC on Sat afternoon, min 15ºC on Sunnight). Wind will be generally light."
do {
let regex = try NSRegularExpression(pattern: "-?[0-9]{1,3}", options: NSRegularExpressionOptions())
let matches = regex.matchesInString(string, options: NSMatchingOptions(), range: NSRange(location: 0, length: string.characters.count))
let degrees = matches.map {Int((string as NSString).substringWithRange($0.range))!}
print(degrees) // -> [-19, 15]
}
catch {
print("NSRegularExpression threw error: \(error)")
}
I try to get URLs in text. So, before, I used such an expression:
let re = NSRegularExpression(pattern: "https?:\\/.*", options: nil, error: nil)!
But I had a problem when a user input URLs with Capitalized symbols (like Http://Google.com, it doesn't match it).
I tried:
let re = NSRegularExpression(pattern: "(h|H)(t|T)(t|T)(p|P)s?:\\/.*", options: nil, error: nil)!
But nothing happened.
You turn off case sensitivity using an i inline flag in regex, see Foundation Framework Reference for more information on available regex features.
(?ismwx-ismwx)
Flag settings. Change the flag settings. Changes apply to the portion of the pattern following the setting. For example, (?i) changes to a case insensitive match.The flags are defined in Flag Options.
For readers:
Matching an URL inside larger texts is already a solved problem, but for this case, a simple regex like
(?i)https?://(?:www\\.)?\\S+(?:/|\\b)
will do as OP requires to match only the URLs that start with http or https or HTTPs, etc.
Swift 4
1. Create String extension
import Foundation
extension String {
var isValidURL: Bool {
guard !contains("..") else { return false }
let head = "((http|https)://)?([(w|W)]{3}+\\.)?"
let tail = "\\.+[A-Za-z]{2,3}+(\\.)?+(/(.)*)?"
let urlRegEx = head+"+(.)+"+tail
let urlTest = NSPredicate(format:"SELF MATCHES %#", urlRegEx)
return urlTest.evaluate(with: trimmingCharacters(in: .whitespaces))
}
}
2. Usage
"www.google.com".isValidURL
Try this - http?://([-\w\.]+)+(:\d+)?(/([\w/_\.]*(\?\S+)?)?)?
let pattern = "http?://([-\\w\\.]+)+(:\\d+)?(/([\\w/_\\.]*(\\?\\S+)?)?)?"
var matches = [String]()
do {
let regex = try NSRegularExpression(pattern: pattern, options: NSRegularExpressionOptions(rawValue: 0))
let nsstr = text as NSString
let all = NSRange(location: 0, length: nsstr.length)
regex.enumerateMatchesInString(text, options: NSMatchingOptions.init(rawValue: 0), range: all, usingBlock: { (result, flags, _) in
matches.append(nsstr.substringWithRange(result!.range))
})
} catch {
return [String]()
}
return matches
Make an exension of string
extension String {
var isAlphanumeric: Bool {
return rangeOfString( "^[wW]{3}+.[a-zA-Z]{3,}+.[a-z]{2,}", options: .RegularExpressionSearch) != nil
}
}
call using like this
"www.testsite.edu".isAlphanumeric // true
"flsd.testsite.com".isAlphanumeric //false
My complex solution for Swift 5.x
ViewController:
private func loadUrl(_ urlString: String) {
guard let url = URL(string: urlString) else { return }
let request = URLRequest(url: url)
webView.load(request)
}
UISearchBarDelegate:
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
guard let text = searchBar.text else { return }
if !text.isUrl() {
let finalUrl = String(format: "%#%#", "https://www.google.com/search?q=", text)
loadUrl(finalUrl)
return
}
if text.starts(with: "https://") || text.starts(with: "http://") {
loadUrl(text)
return
}
let finalUrl = String(format: "%#%#", "https://", text)
loadUrl(finalUrl)
}
String extension:
extension String {
func isUrl() -> Bool {
guard !contains("..") else { return false }
let regex = "((http|https)://)?([(w|W)]{3}+\\.)?+(.)+\\.+[A-Za-z]{2,3}+(\\.)?+(/(.)*)?"
let urlTest = NSPredicate(format:"SELF MATCHES %#", regex)
return urlTest.evaluate(with: trimmingCharacters(in: .whitespaces))
}
}
I have written this function in String Extension and can't figure out the error.
func isEmail() -> Bool {
let regex = NSRegularExpression(pattern: "^[A-Z0-9._%+-]+#[A-Z0-9.-]+\\.[A-Z]{2,4}$", options: [.CaseInsensitive])
return regex.firstMatchInString(self, options: nil, range: NSMakeRange(0, characters.count)) != nil
}
The error is:
Call can throw, but it is not marked with 'try' and the error is not handled
NSRegularExpression(pattern:) throws an error if the pattern is invalid. In your case, the pattern is fixed, so an invalid pattern
would be a programming error.
This is a use-case for the "forced-try" expression with try!:
extension String {
func isEmail() -> Bool {
let regex = try! NSRegularExpression(pattern: "^[A-Z0-9._%+-]+#[A-Z0-9.-]+\\.[A-Z]{2,4}$",
options: [.CaseInsensitive])
return regex.firstMatchInString(self, options:[],
range: NSMakeRange(0, utf16.count)) != nil
}
}
try! disables the error propagation so that the method does not
throw an error (which the caller has to catch). It will abort with a
runtime exception if the pattern is invalid, which helps to find
programming errors early.
Note also that NSRange() counts the length of NSString, i.e. the number of UTF-16 code points, so
characters.count should be utf16.count, otherwise it might crash
e.g. with Emoji characters.
That's because that initializer can now throw an exception, so you need to try to call it and be prepared to catch the exception. You can do this by adding try before the initializer and annotating your method with throws.
extension String {
func isEmail() throws -> Bool {
let regex = try NSRegularExpression(pattern: "^[A-Z0-9._%+-]+#[A-Z0-9.-]+\\.[A-Z]{2,4}$", options: [.CaseInsensitive])
return regex.firstMatchInString(self, options: NSMatchingOptions(rawValue: 0), range: NSMakeRange(0, characters.count)) != nil
}
}
Then when you want to call the method, do it from within a do block and catch the error that comes out.
do {
try "person#email.com".isEmail()
} catch {
print(error)
}
Note: I've also updated your regex.firstMatchInString call to reflect the fact that the options parameter can no longer take a nil value.
If you don't like try catch :
extension String {
func matchPattern(patStr:String)->Bool {
var isMatch:Bool = false
do {
let regex = try NSRegularExpression(pattern: patStr, options: [.CaseInsensitive])
let result = regex.firstMatchInString(self, options: NSMatchingOptions(rawValue: 0), range: NSMakeRange(0, characters.count))
if (result != nil)
{
isMatch = true
}
}
catch {
isMatch = false
}
return isMatch
}
}
check string is correct email format :
let emailInput:String = "checkthis#gmail.com"
if (emailInput.matchPattern("^[A-Z0-9._%+-]+#[A-Z0-9.-]+\\.[A-Z]{2,4}$"))
{
print("this is e-mail!")
}
You could use string.rangeOfString, and specify the option to .RegularExpressionSearch. It's simple.
func isEmail(email: String) -> Bool {
return email.rangeOfString("^[A-Z0-9a-z._%+-]+#[A-Za-z0-9.-]+\\.[A-Za-z]{2,6}$", options: .RegularExpressionSearch) != nil
}