How to check validity of URL in Swift?

UrlSwiftIos8

Url Problem Overview


Trying to make an app launch the default browser to a URL, but only if the URL entered is valid, otherwise it displays a message saying the URL is invalid.

How would I go about checking the validity using Swift?

Url Solutions


Solution 1 - Url

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.

Solution 2 - Url

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.

Solution 3 - Url

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)
}

Solution 4 - Url

I found this one clean (In Swift):

func canOpenURL(string: String?) -> Bool {
    guard let urlString = string else {return false}
    guard let url = NSURL(string: urlString) else {return false}
    if !UIApplication.sharedApplication().canOpenURL(url) {return false}
    
    //
    let regEx = "((https|http)://)((\\w|-)+)(([.]|[/])((\\w|-)+))+"
    let predicate = NSPredicate(format:"SELF MATCHES %@", argumentArray:[regEx])
    return predicate.evaluateWithObject(string)
}

Usage:

if canOpenURL("abc") {
    print("valid url.")
} else {
    print("invalid url.")
}

===

for Swift 4.1:

func canOpenURL(_ string: String?) -> Bool {
    guard let urlString = string,
        let url = URL(string: urlString)
        else { return false }
    
    if !UIApplication.shared.canOpenURL(url) { return false }
    
    let regEx = "((https|http)://)((\\w|-)+)(([.]|[/])((\\w|-)+))+"
    let predicate = NSPredicate(format:"SELF MATCHES %@", argumentArray:[regEx])
    return predicate.evaluate(with: string)
}

// Usage
if canOpenURL("abc") {
    print("valid url.")
} else {
    print("invalid url.") // This line executes
}

if canOpenURL("https://www.google.com") {
    print("valid url.") // This line executes
} else {
    print("invalid url.")
}

Solution 5 - 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
}

Solution 6 - Url

2020, I was tasked on fixing a bug of a method for underlining string links within a string. Most of the answers here don't work properly (try: aaa.com.bd or aaa.bd) These links should be valid. And then I stumbled upon the regex string for this.

Ref: https://urlregex.com/

So based on that regex

"((?:http|https)://)?(?:www\\.)?[\\w\\d\\-_]+\\.\\w{2,3}(\\.\\w{2})?(/(?<=/)(?:[\\w\\d\\-./_]+)?)?"

We can write a function.

SWIFT 5.x:

extension String {
    var validURL: Bool {
        get {
            let regEx = "((?:http|https)://)?(?:www\\.)?[\\w\\d\\-_]+\\.\\w{2,3}(\\.\\w{2})?(/(?<=/)(?:[\\w\\d\\-./_]+)?)?"
            let predicate = NSPredicate(format: "SELF MATCHES %@", argumentArray: [regEx])
            return predicate.evaluate(with: self)
        }
    }
}

OBJECTIVE-C (write this however you want, category or not).

- (BOOL)stringIsValidURL:(NSString *)string
{
    NSString *regEx = @"((?:http|https)://)?(?:www\\.)?[\\w\\d\\-_]+\\.\\w{2,3}(\\.\\w{2})?(/(?<=/)(?:[\\w\\d\\-./_]+)?)?";
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF MATCHES %@" argumentArray:@[regEx]];
    return [predicate evaluateWithObject:string];
}

Solution 7 - Url

My personal preference is to approach this with an extension, because I like to call the method directly on the string object.

extension String {

    private func matches(pattern: String) -> Bool {
        let regex = try! NSRegularExpression(
            pattern: pattern,
            options: [.caseInsensitive])
        return regex.firstMatch(
            in: self,
            options: [],
            range: NSRange(location: 0, length: utf16.count)) != nil
    }

    func isValidURL() -> Bool {
        guard let url = URL(string: self) else { return false }
        if !UIApplication.shared.canOpenURL(url) {
            return false
        }
    
        let urlPattern = "^(http|https|ftp)\\://([a-zA-Z0-9\\.\\-]+(\\:[a-zA-Z0-9\\.&amp;%\\$\\-]+)*@)*((25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9])\\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0)\\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0)\\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[0-9])|localhost|([a-zA-Z0-9\\-]+\\.)*[a-zA-Z0-9\\-]+\\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(\\:[0-9]+)*(/($|[a-zA-Z0-9\\.\\,\\?\\'\\\\\\+&amp;%\\$#\\=~_\\-]+))*$"
        return self.matches(pattern: urlPattern)
    }
}

This way it is also extensible with another use-cases, such as isValidEmail, isValidName or whatever your application requires.

Solution 8 - Url

var url:NSURL = NSURL(string: "tel://000000000000")!
if UIApplication.sharedApplication().canOpenURL(url) {
    UIApplication.sharedApplication().openURL(url)
} else {
     // Put your error handler code...
}

Solution 9 - Url

Swift 5.1 Solution

extension String {
    func canOpenUrl() -> Bool {
        guard let url = URL(string: self), UIApplication.shared.canOpenURL(url) else { return false }
        let regEx = "((https|http)://)((\\w|-)+)(([.]|[/])((\\w|-)+))+"
        let predicate = NSPredicate(format:"SELF MATCHES %@", argumentArray:[regEx])
        return predicate.evaluate(with: self)
    }
}

Solution 10 - Url

For Swift 4 version

static func isValidUrl (urlString: String?) -> Bool {
    if let urlString = urlString {
        if let url = URL(string: urlString) {
            return UIApplication.shared.canOpenURL(url)
        }
    }
    return false
}

Solution 11 - Url

You can use the NSURL type (whose constructor returns an optional type) combined with an if-let statement to check the validity of a given URL. In other words, make use of the NSURL failable initializer, a key feature of Swift:

let stringWithPossibleURL: String = self.textField.text // Or another source of text

if let validURL: NSURL = NSURL(string: stringWithPossibleURL) {
    // Successfully constructed an NSURL; open it
    UIApplication.sharedApplication().openURL(validURL)
} else {
    // Initialization failed; alert the user
    let controller: UIAlertController = UIAlertController(title: "Invalid URL", message: "Please try again.", preferredStyle: .Alert)
    controller.addAction(UIAlertAction(title: "OK", style: .Default, handler: nil))
    
    self.presentViewController(controller, animated: true, completion: nil)
}

Solution 12 - Url

extension String {    
    func isStringLink() -> Bool {
        let types: NSTextCheckingResult.CheckingType = [.link]
        let detector = try? NSDataDetector(types: types.rawValue)
        guard (detector != nil && self.characters.count > 0) else { return false }
        if detector!.numberOfMatches(in: self, options: NSRegularExpression.MatchingOptions(rawValue: 0), range: NSMakeRange(0, self.characters.count)) > 0 {
            return true
        }
        return false
    }
}

//Usage
let testURL: String = "http://www.google.com"
if testURL.isStringLink() {
    //Valid!
} else {
    //Not valid.
}

> It's advised to use this check only once and then reuse.

P.S. Credits to Shachar for this function.

Solution 13 - Url

In some cases it can be enough to check that the url satisfies RFC 1808. There are several ways to do this. One example:

if let url = URL(string: urlString), url.host != nil {
  // This is a correct url
}

This is because .host, as well as .path, .fragment and a few other methods would return nil if url doesn't conform to RFC 1808.

If you don't check, you might have this kind of messages in console log:

Task <DF46917D-1A04-4E76-B54E-876423224DF7>.<72> finished with error - code: -1002

Solution 14 - Url

This will return a boolean for a URL's validity, or nil if an optional URL with a value of nil is passed.

extension URL {
    
    var isValid: Bool {
        get {
            return UIApplication.shared.canOpenURL(self)
        }
    }
    
}

Note that, if you plan to use a Safari view, you should test url.scheme == "http" || url.scheme == "https".

Solution 15 - Url

Swift 4.2 Elegant URL construction with verification

import Foundation
import UIKit

extension URL {

  init?(withCheck string: String?) {
    let regEx = "((https|http)://)((\\w|-)+)(([.]|[/])((\\w|-)+))+"
    guard
        let urlString = string,
        let url = URL(string: urlString),
        NSPredicate(format: "SELF MATCHES %@", argumentArray: [regEx]).evaluate(with: urlString),
        UIApplication.shared.canOpenURL(url)
        else {
            return nil
    }

    self = url
  }
}

Usage

var imageUrl: URL? {
    if let url = URL(withCheck: imageString) {
        return url
    }
    if let url = URL(withCheck: image2String) {
        return url
    }
    return nil
}

Solution 16 - Url

This isn't a regex approach, but it is a naive one that works well for making sure there is a host and an extension if you want a simple and inexpensive approach:

extension String {
    var isValidUrlNaive: Bool {
        var domain = self
        guard domain.count > 2 else {return false}
        guard domain.trim().split(" ").count == 1 else {return false}
        if self.containsString("?") {
            var parts = self.splitWithMax("?", maxSplit: 1)
            domain = parts[0]
        }
        return domain.split(".").count > 1
    }
}

Use this only if you want a quick way to check on the client side and you have server logic that will do a more rigorous check before saving the data.

Solution 17 - Url

Try this:

func isValid(urlString: String) -> Bool
{
    if let urlComponents = URLComponents.init(string: urlString), urlComponents.host != nil, urlComponents.url != nil
    {
        return true
    }
    return false
}

This simply checks for valid URL components and if the host and url components are not nil. Also, you can just add this to an extensions file

Solution 18 - Url

For swift 4 you can use:

class func verifyUrl (urlString: String?) -> Bool {
    //Check for nil
    if let urlString = urlString {
        // create NSURL instance
        if let url = URL(string: urlString) {
            // check if your application can open the NSURL instance
            return UIApplication.shared.canOpenURL(url)
        }
    }
    return false
}

Solution 19 - Url

Helium having to deal with various schemes:

struct UrlHelpers {
    // Prepends `http://` if scheme isn't `https?://` unless "file://"
    static func ensureScheme(_ urlString: String) -> String {
        if !(urlString.lowercased().hasPrefix("http://") || urlString.lowercased().hasPrefix("https://")) {
            return urlString.hasPrefix("file://") ? urlString : "http://" + urlString
        } else {
            return urlString
        }
    }

    // https://mathiasbynens.be/demo/url-regex
    static func isValid(urlString: String) -> Bool {
        // swiftlint:disable:next force_try
        if urlString.lowercased().hasPrefix("file:"), let url = URL.init(string: urlString) {
            return FileManager.default.fileExists(atPath:url.path)
        }

        let regex = try! NSRegularExpression(pattern: "^(https?://)[^\\s/$.?#].[^\\s]*$")
        return (regex.firstMatch(in: urlString, range: urlString.nsrange) != nil)
    }
}

Solution 20 - Url

Version that works with Swift 4.2 and has reliable URL pattern matching ...

func matches(pattern: String) -> Bool
{
	do
	{
		let regex = try NSRegularExpression(pattern: pattern, options: [.caseInsensitive])
		return regex.firstMatch(in: self, options: [], range: NSRange(location: 0, length: utf16.count)) != nil
	}
	catch
	{
		return false
	}
}


func isValidURL() -> Bool
{
	guard let url = URL(string: self) else { return false }
	if !UIApplication.shared.canOpenURL(url) { return false }

	let urlPattern = "(http|ftp|https):\\/\\/([\\w+?\\.\\w+])+([a-zA-Z0-9\\~\\!\\@\\#\\$\\%\\^\\&\\*\\(\\)_\\-\\=\\+\\\\\\/\\?\\.\\:\\;\\'\\,]*)?"
	return self.matches(pattern: urlPattern)
}

Solution 21 - Url

This is for latest Swift 4, based on Doug Amos answer (for swift 3)

public static func verifyUrl (urlString: String?) -> Bool {
    //Check for nil
    if let urlString = urlString {
        // create NSURL instance
        if let url = NSURL(string: urlString) {
            // check if your application can open the NSURL instance
            return UIApplication.shared.canOpenURL(url as URL)
        }
    }
    return false
}

Solution 22 - Url

Most of the answer here doesn't address my issue so I'm posting here how I resolved it:

static func isValidDomain(domain: String?) -> Bool {
    guard let domain = domain else {
        return false
    }
    // Modified version of https://stackoverflow.com/a/49072718/2304737
    let detector = try! NSDataDetector(types: NSTextCheckingResult.CheckingType.link.rawValue)
    let domainWithScheme = "https://\(domain)"
    if let url = URL(string: domainWithScheme),
       let match = detector.firstMatch(in: domainWithScheme, options: [], range: NSRange(location: 0, length: domainWithScheme.utf16.count)) {
        // it is a link, if the match covers the whole string
        return match.range.length == domainWithScheme.utf16.count && url.host != nil
    } else {
        return false
    }
}

What lacks on Milan Nosáľ's answer is, it doesn't address this particular input:

https://#view-?name=post&post.post_id=519&message.message_id=349

So I just add host check existence and unschemed "URL".

Solution 23 - Url

This accepted answer doesn't work in my case with wrong url without data https://debug-cdn.checkit4team.com/5/image/Y29tbW9uL2RlZmF1bHRfYXZhdGFyLnBuZw==

So I write extension to solve

extension String {
    var verifyUrl: Bool {
        get {
            let url = URL(string: self)
            
            if url == nil || NSData(contentsOf: url!) == nil {
                return false
            } else {
                return true
            }
        }
    }
}

Use it:

if string. verifyUrl {
    // do something
}

Hope this help!

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionClintonView Question on Stackoverflow
Solution 1 - Url3366784View Answer on Stackoverflow
Solution 2 - UrlMilan NosáľView Answer on Stackoverflow
Solution 3 - UrlDoug AmosView Answer on Stackoverflow
Solution 4 - UrlAshokView Answer on Stackoverflow
Solution 5 - UrlShacharView Answer on Stackoverflow
Solution 6 - UrlGlenn PosadasView Answer on Stackoverflow
Solution 7 - UrlMichalView Answer on Stackoverflow
Solution 8 - UrlChetan PrajapatiView Answer on Stackoverflow
Solution 9 - UrlMark WilsonView Answer on Stackoverflow
Solution 10 - UrlMoamen Abd ElmageedView Answer on Stackoverflow
Solution 11 - UrlAstroCBView Answer on Stackoverflow
Solution 12 - UrlHemangView Answer on Stackoverflow
Solution 13 - UrlVitaliiView Answer on Stackoverflow
Solution 14 - UrlCartoonChessView Answer on Stackoverflow
Solution 15 - UrlmaslovsaView Answer on Stackoverflow
Solution 16 - Urllwdthe1View Answer on Stackoverflow
Solution 17 - UrlBryan CimoView Answer on Stackoverflow
Solution 18 - UrlPaoloView Answer on Stackoverflow
Solution 19 - UrlslashlosView Answer on Stackoverflow
Solution 20 - UrlBadmintonCatView Answer on Stackoverflow
Solution 21 - UrlShefy Gur-aryView Answer on Stackoverflow
Solution 22 - Urlmr5View Answer on Stackoverflow
Solution 23 - UrlQuyen Anh NguyenView Answer on Stackoverflow