How to check validity of URL in Swift?
Swift 4 elegant solution using NSDataDetector
:
extension String {
var isValidURL: Bool {
let detector = try! NSDataDetector(types: NSTextCheckingResult.CheckingType.link.rawValue)
if let match = detector.firstMatch(in: self, options: [], range: NSRange(location: 0, length: self.utf16.count)) {
// it is a link, if the match covers the whole string
return match.range.length == self.utf16.count
} else {
return false
}
}
}
Usage:
let string = "https://www.fs.blog/2017/02/naval-ravikant-reading-decision-making/"
if string.isValidURL {
// TODO
}
Reasoning behind using NSDataDetector
instead of UIApplication.shared.canOpenURL
:
I needed a method that would detect whether the user provided an input that is an URL to something. In many cases, users don't include the http://
nor https://
URL scheme in the URL they type in - e.g., instead of "http://www.google.com"
they would type in "www.google.com"
. Without the URL scheme, the UIApplication.shared.canOpenURL
will fail to recognize the URL and will return false
. NSDataDetector
is, compared to UIApplication.shared.canOpenURL
, a rather forgiving tool (as @AmitaiB mentioned in comments) - and it can detect even URLs without the http://
scheme. This way I am able to detect a URL without having to try to add the scheme everytime when testing the string.
Sidenote - SFSafariViewController
can open only URLs with http://
/https://
. Thus, if a detected URL does not have a URL scheme specified, and you want to open the link, you will have to prepend the scheme manually.
If your goal is to verify if your application can open a URL, here is what you can do. Although safari can open the URL, the website might not exist or it might be down.
// Swift 5
func verifyUrl (urlString: String?) -> Bool {
if let urlString = urlString {
if let url = NSURL(string: urlString) {
return UIApplication.shared.canOpenURL(url as URL)
}
}
return false
}
As a side note, this does not check whether or not a URL is valid or complete. For example, a call that passes "https://" returns true.
For a swift 3 version of the accepted answer:
func verifyUrl(urlString: String?) -> Bool {
if let urlString = urlString {
if let url = URL(string: urlString) {
return UIApplication.shared.canOpenURL(url)
}
}
return false
}
Or for a more Swifty solution:
func verifyUrl(urlString: String?) -> Bool {
guard let urlString = urlString,
let url = URL(string: urlString) else {
return false
}
return UIApplication.shared.canOpenURL(url)
}
Using 'canOpenUrl' was too expensive for my use case, I found this approach to be quicker
func isStringLink(string: String) -> Bool {
let types: NSTextCheckingResult.CheckingType = [.link]
let detector = try? NSDataDetector(types: types.rawValue)
guard (detector != nil && string.characters.count > 0) else { return false }
if detector!.numberOfMatches(in: string, options: NSRegularExpression.MatchingOptions(rawValue: 0), range: NSMakeRange(0, string.characters.count)) > 0 {
return true
}
return false
}