5

In my Swift iOS project, I want to check whether is the valid url or not before requesting to server. I did earlier in Objective C code to check many elements like presence of www, http, https, :, etc to validate whether the right url or not. Do we have anything similar in Swift code?

I am expecting like this Obj C method.

 - (BOOL) validateUrl: (NSString *) candidate {
    NSString *urlRegEx =
    @"(http|https)://((\\w)*|([0-9]*)|([-|_])*)+([\\.|/]((\\w)*|([0-9]*)|([-|_])*))+";
    NSPredicate *urlTest = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", urlRegEx]; 
    return [urlTest evaluateWithObject:candidate];
}

Please suggest.

Stella
  • 1,728
  • 5
  • 41
  • 95

13 Answers13

10

Swift 4.x

This Covers all types of validation like Subdomain, with or without HTTP / HTTPS

func isValidUrl(url: String) -> Bool {
    let urlRegEx = "^(https?://)?(www\\.)?([-a-z0-9]{1,63}\\.)*?[a-z0-9][-a-z0-9]{0,61}[a-z0-9]\\.[a-z]{2,6}(/[-\\w@\\+\\.~#\\?&/=%]*)?$"
    let urlTest = NSPredicate(format:"SELF MATCHES %@", urlRegEx)
    let result = urlTest.evaluate(with: url)
    return result
}

Hope this works for you.

Ashu
  • 3,373
  • 38
  • 34
  • 1
    Tested this code with Swift 5, unfortunately `stackoverflow.com.com` returns `true`, while `129.0.0.1` and `ftp://129.0.0.1` return `false.` – Neph Jul 26 '19 at 09:15
4

You can use the method stringByAddingPercentEscapesUsingEncoding to replace any special characters your link may have and then you can check if your link is a valid url using NSURL(string:) as follow:

let link =  "http://www.yourUrl.com".stringByRemovingPercentEncoding!.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)!

if let checkedUrl = NSURL(string: link) {
    // you can use checkedUrl here
}

Note: NSURL(string:) is only for weblinks. If you would like to create a NSURL for a local resource file you need to use NSURL(fileURLWithPath:)

Leo Dabus
  • 229,809
  • 59
  • 489
  • 571
3

Try this.

func validateUrl (urlString: String?) -> Bool {
    let urlRegEx = "(http|https)://((\\w)*|([0-9]*)|([-|_])*)+([\\.|/]((\\w)*|([0-9]*)|([-|_])*))+"
    return NSPredicate(format: "SELF MATCHES %@", urlRegEx).evaluateWithObject(urlString)
}
swathy krishnan
  • 916
  • 9
  • 22
3

All the answers so far seem to involve regex, which seems potentially error prone (easy to miss something on our end). Another option is just creating the url object, and leaving the validation to the Foundation framework.

func validateURL(urlString: String) -> Bool {
    let url = URL(string: urlString)
    return url.host != nil && url.scheme != nil /* and whatever other checks you want */
}

If the URL couldn't be parsed, the host, scheme, etc. will be nil.

Ryan
  • 4,425
  • 2
  • 23
  • 12
2

Swift 5

As none of the above solutions worked for me in Swift 5, I ended up writing my own REGEX for swift 5. This covers all types of validation including PORT

func validateURL(url: String) -> Bool {
        let regex = "http[s]?://(([^/:.[:space:]]+(.[^/:.[:space:]]+)*)|([0-9](.[0-9]{3})))(:[0-9]+)?((/[^?#[:space:]]+)([^#[:space:]]+)?(#.+)?)?"
        let test = NSPredicate(format:"SELF MATCHES %@", regex)
        let result = test.evaluate(with: url)
        return result
 }
Vittal Pai
  • 3,317
  • 25
  • 36
  • This indeed works with ports (e.g. `http://127.0.0.1:8080/test/test`) but only if the URL starts with "http" or "https" (it fails for e.g. `http://---`, which shouldn't be valid). It doesn't work with e.g. `stackoverflow.com` or `www.stackoverflow.com`, even though they are both valid. I tried to replace the beginning (`http[s]?://`) with `((http|https|ftp)://)?` but then even `---`is valid, while e.g. `127.0..0.1` isn't (which is correct). – Neph Apr 21 '23 at 11:24
2

this simple extension make life easy

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

}

Awais Mobeen
  • 733
  • 11
  • 19
1

I have a problem with the highest rated answer here. I think it is plain wrong for two reasons:

First, the stringByAddingPercentExcapedUsingEncoding will actually double encode valid URLs, thus making them invalid or at least different than the original input. If the input string is already a correctly encoded URL then you should just use that.

For example, http://foo.com/My%20Stuff/ will become http://foo.com/My%2520Stuff/ which is not the same. If you feed that to NSURL, it will actually decode it to a URL with a literal % in it.

Second, NSURL is known to be very bad at recognizing bad input. For example it happily returns an non-nil result when you give it something like x/3:()foo/blah which is clearly malformed and not a valid URL.

I think it is best to use a regular expression. I don't think there is anything in Foundation that can properly validate URLs.

Stefan Arentz
  • 34,311
  • 8
  • 67
  • 88
1

I've found that this is pretty easy to swift

func validateUrl() -> Bool {
  let urlRegEx = "((\\w)*|([0-9]*)|([-|_])*)+([\\.|/]((\\w)*|([0-9]*)|([-|_])*))+"
  return NSPredicate(format: "SELF MATCHES %@", urlRegEx).evaluate(with: self)
}

I find this easy. If you put as an extension or just in the class you can say

func fooBar(textField: UITextField) {   
   if textField == someTextField {
      if textField.text?.validateUrl() == true {
         print("true")
      } else {
         print("false")
      }
   }
}

Very similar to other answers tho, and years late

This doesn't require the http part, I feel like most users wouldn't bother typing in the http or https like http://www.google.com but instead just google.com or www.google.com

Obviously you could go the extra mile and check if they did or did not put the http(s) and if you really want to add it add it yourself..or not

RubberDucky4444
  • 2,330
  • 5
  • 38
  • 70
1

For Swift 5.0

func validateUrl (urlString: String?) -> Bool {
        let urlRegEx = "(http|https)://((\\w)*|([0-9]*)|([-|_])*)+([\\.|/]((\\w)*|([0-9]*)|([-|_])*))+"
        return NSPredicate(format: "SELF MATCHES %@", urlRegEx).evaluate(with: urlString)
    }
Uday Babariya
  • 1,031
  • 1
  • 13
  • 31
0

I suggest using regular expression like this. The pattern below is correct for url.

let regex = NSRegularExpression(pattern: "/^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$/",options: nil, error: nil)!

if regex.firstMatchInString(searchTerm!, options: nil, range: NSMakeRange(0, searchTerm!.length)) != nil {
    println("not valid url ")
}
Wain
  • 118,658
  • 15
  • 128
  • 151
  • 1
    Is this compiling properly? It is not compiling this code. Build error in the 1st line - "Consecutive statements on a line must be seperated by ;" – Stella Mar 17 '15 at 17:50
0

I have another solution. In Swift 3

import Foundation
import UIKit

extension String {

    var canOpenURL : Bool {

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

It can be used as

if textField == someTextField {

    if (self.textField.text?.canOpenURL)! {
        //URL valid
    }else {
       //URL invalid
    }
}
Lineesh K Mohan
  • 1,702
  • 14
  • 18
0
private var cancellbleSet: Set<AnyCancellable> = []

 func validateUrl1() {
    $url1
        .receive(on: RunLoop.main)
        .dropFirst()
        .sink { [self] in
            if $0 != "" {
                let pattern = "^(https?://)?(www\\.)?([-a-z0-9]{1,63}\\.)*?[a-z0-9][-a-z0-9]{0,61}[a-z0-9]\\.[a-z]{2,6}(/[-\\w@\\+\\.~#\\?&/=%]*)?$"
                let regex : Regex  = pattern.r!
                if regex.matches($0) {
                    isUrl1Valid = true
                } else {
                    isUrl1Valid = false
                }
            }
        }
        .store(in: &cancellbleSet)
}

Then you can call it inside the view model init, like this

 override init() {
    super.init()
    validateUrl1()
}
-1

You could use Regex to validate URL Format

(https?://(www.)?)?[-a-zA-Z0-9@:%._+~#=]{2,256}.[a-z]{2,6}b([-a-zA-Z0-9@:%_+.~#?&//=]*)

SWIFT 4:

func validateURL (urlString: String) -> Bool {
    let urlRegEx = "(https?://(www.)?)?[-a-zA-Z0-9@:%._+~#=]{2,256}.[a-z]{2,6}b([-a-zA-Z0-9@:%_+.~#?&//=]*)"
    let urlTest = NSPredicate(format:"SELF MATCHES %@", urlRegEx)
    return urlTest.evaluate(with: urlString)
}
Lal Krishna
  • 15,485
  • 6
  • 64
  • 84