Cómo validar una URL en el iPhone

90

En una aplicación para iPhone que estoy desarrollando, hay una configuración en la que puede ingresar una URL, debido a la forma y función, esta URL debe validarse en línea y fuera de línea.

Hasta ahora no he podido encontrar ningún método para validar la URL, por lo que la pregunta es;

¿Cómo valido una entrada de URL en el iPhone (Objective-C) en línea y fuera de línea?

Thizzer
fuente
Lea los comentarios a su respuesta, la validación no funciona correctamente.
Thizzer

Respuestas:

98

Gracias a esta publicación , puede evitar usar RegexKit. Aquí está mi solución (funciona para el desarrollo de iPhone con iOS> 3.0):

- (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];
}

Si desea verificar en Swift, mi solución se proporciona a continuación:

 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
    }
lefakir
fuente
7
Esto solo funciona para las URL de tipo " webr.ly " no funciona para las URL con parámetros como youtube.com/watch?v=mqgExtdNMBk
The uSeFuL
5
((http|https)://)?((\\w)*|([0-9]*)|([-|_])*)+([\\.|/]((\\w)*|([0-9]*)|([-|_])*))+".Debe hacer que http: // o https: // sea opcional.
Yeung
no funciona para google.com, www.google.com y también para //www.google.com
Revinder
las siguientes URL no funcionan en mi caso. money.cnn.com/2015/10/19/technology/apple-app-store/…
DJtiwari
@DJtiwari +1 sí, no funciona, ¿encontraste alguna solución para eso?
Hamza MHIRA
239

¿Por qué no simplemente confiar en él Foundation.framework?

Eso hace el trabajo y no requiere RegexKit:

NSURL *candidateURL = [NSURL URLWithString:candidate];
// WARNING > "test" is an URL according to RFCs, being just a path
// so you still should check scheme and all other NSURL attributes you need
if (candidateURL && candidateURL.scheme && candidateURL.host) {
  // candidate is a well-formed url with:
  //  - a scheme (like http://)
  //  - a host (like stackoverflow.com)
}

Según la documentación de Apple:

URLWithString: crea y devuelve un objeto NSURL inicializado con una cadena proporcionada.

+ (id)URLWithString:(NSString *)URLString

Parámetros

URLString: La cadena con la que inicializar el objeto NSURL. Debe cumplir con RFC 2396. Este método analiza URLString de acuerdo con las RFC 1738 y 1808.

Valor devuelto

Un objeto NSURL inicializado con URLString. Si la cadena estaba mal formada, devuelve nil.

Vincent Guerci
fuente
1
Estoy de acuerdo con algunos de los demás aquí. Esta es una solución mucho mejor que jugar con expresiones regulares. Esta debería ser la respuesta correctamente verificada.
Diego Barros
1
@MrThys: ¿Alguna posibilidad de que pueda proporcionar ejemplos de las URL mal formadas que no detecta? Sería genial saberlo ... parece una excelente solución hasta ahora.
Don Vaughn
7
@DonamiteIsTnt La URL http://www.aol.comhttp://www.nytimes.compasa esta prueba.
Aaron Brager
1
El código no buscará URL con formato incorrecto. Por ejemplo : <code> afasd </ code >, la URL seguirá
superando
39
Los doctores están equivocados. Escribe algunas pruebas: NSURLno devuelve nil cuando paso una cadena de @ "# @ # @ $ ##% $ # $ #", o @ "tp: / fdfdfsfdsf". Por lo tanto, este método será inútil para verificar URL HTTP válidas y similares.
Tony Arnold
32

En lugar de escribir sus propias expresiones regulares, confíe en las de Apple. He estado usando una categoría NSStringque se usa NSDataDetectorpara probar la presencia de un enlace dentro de una cadena. Si el rango del enlace encontrado por NSDataDetectores igual a la longitud de la cadena completa, entonces es una URL válida.

- (BOOL)isValidURL {
    NSUInteger length = [self length];
    // Empty strings should return NO
    if (length > 0) {
        NSError *error = nil;
        NSDataDetector *dataDetector = [NSDataDetector dataDetectorWithTypes:NSTextCheckingTypeLink error:&error];
        if (dataDetector && !error) {
            NSRange range = NSMakeRange(0, length);
            NSRange notFoundRange = (NSRange){NSNotFound, 0};
            NSRange linkRange = [dataDetector rangeOfFirstMatchInString:self options:0 range:range];
            if (!NSEqualRanges(notFoundRange, linkRange) && NSEqualRanges(range, linkRange)) {
                return YES;
            }
        }
        else {
            NSLog(@"Could not create link data detector: %@ %@", [error localizedDescription], [error userInfo]);
        }
    }
    return NO;
}
Antonio
fuente
Extremadamente inteligente. Ingeniería real. {Sabes, tuve un problema extraño en el que si le envío la cadena literalmente "<null>", ¡se bloquea! Nunca pude resolverlo.}
Fattie
Ah, era "<nulo>" en el hijo, ¡que Apple proporciona útilmente como NSNull! : O
Fattie
Creé una esencia en la que comencé a agregar casos de prueba para este recorte. Rellene gratis para agregar más. gist.github.com/b35097bad451c59e23b1.git
Yevhen Dubinin
26

Mi solución con Swift :

func validateUrl (stringURL : NSString) -> Bool {

    var urlRegEx = "((https|http)://)((\\w|-)+)(([.]|[/])((\\w|-)+))+"
    let predicate = NSPredicate(format:"SELF MATCHES %@", argumentArray:[urlRegEx])
    var urlTest = NSPredicate.predicateWithSubstitutionVariables(predicate)

    return predicate.evaluateWithObject(stringURL)
}

Para prueba:

var boolean1 = validateUrl("http.s://www.gmail.com")
var boolean2 = validateUrl("https:.//gmailcom")
var boolean3 = validateUrl("https://gmail.me.")
var boolean4 = validateUrl("https://www.gmail.me.com.com.com.com")
var boolean6 = validateUrl("http:/./ww-w.wowone.com")
var boolean7 = validateUrl("http://.www.wowone")
var boolean8 = validateUrl("http://www.wow-one.com")
var boolean9 = validateUrl("http://www.wow_one.com")
var boolean10 = validateUrl("http://.")
var boolean11 = validateUrl("http://")
var boolean12 = validateUrl("http://k")

Resultados:

false
false
false
true
false
false
true
true
false
false
false
Gabriel.Massana
fuente
10

utilizar este-

NSString *urlRegEx = @"http(s)?://([\\w-]+\\.)+[\\w-]+(/[\\w- ./?%&amp;=]*)?";
Vaibhav Saran
fuente
1
Lo copié simplemente para el validador de expresiones regulares de asp.net;)
Vaibhav Saran
perfecto, el único problema es que no reconocewww.google.com/+gplusname
MuhammadBassio
5

Resolví el problema usando RegexKit y construí una expresión regular rápida para validar una URL;

NSString *regexString = @"(http|https)://((\\w)*|([0-9]*)|([-|_])*)+([\\.|/]((\\w)*|([0-9]*)|([-|_])*))+";
NSString *subjectString = brandLink.text;
NSString *matchedString = [subjectString stringByMatching:regexString];

Luego verifico si matchedString es igual a subjectString y si ese es el caso, la url es válida :)

Corrígeme si mi expresión regular es incorrecta;)

Thizzer
fuente
Puede que esté equivocado, pero creo que la expresión regular no valida las URL con cadenas de consulta o anclajes con nombre.
hpique
Puede hacer que la parte del prefijo sea opcional reemplazando (http | https): // con ((http | https): //) * pero esto permitiría una gran variedad de direcciones URL
Thizzer
4

Por extraño que parezca, realmente no encontré una solución aquí que fuera muy simple, pero aún así hice un buen trabajo para manejar http/ httpsenlaces.

Tenga en cuenta que ESTA NO ES una solución perfecta, pero funcionó para los casos siguientes. En resumen, la expresión regular prueba si la URL comienza con http://o https://, luego verifica al menos 1 carácter, luego busca un punto y luego nuevamente verifica al menos 1 carácter. No se permiten espacios.

+ (BOOL)validateLink:(NSString *)link
{
    NSString *regex = @"(?i)(http|https)(:\\/\\/)([^ .]+)(\\.)([^ \n]+)";
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", regex];
    return [predicate evaluateWithObject:link];
}

Probado VÁLIDO contra estas URL:

@"HTTP://FOO.COM",
@"HTTPS://FOO.COM",
@"http://foo.com/blah_blah",
@"http://foo.com/blah_blah/",
@"http://foo.com/blah_blah_(wikipedia)",
@"http://foo.com/blah_blah_(wikipedia)_(again)",
@"http://www.example.com/wpstyle/?p=364",
@"https://www.example.com/foo/?bar=baz&inga=42&quux",
@"http://✪df.ws/123",
@"http://userid:[email protected]:8080",
@"http://userid:[email protected]:8080/",
@"http://[email protected]",
@"http://[email protected]/",
@"http://[email protected]:8080",
@"http://[email protected]:8080/",
@"http://userid:[email protected]",
@"http://userid:[email protected]/",
@"http://142.42.1.1/",
@"http://142.42.1.1:8080/",
@"http://➡.ws/䨹",
@"http://⌘.ws",
@"http://⌘.ws/",
@"http://foo.com/blah_(wikipedia)#cite-",
@"http://foo.com/blah_(wikipedia)_blah#cite-",
@"http://foo.com/unicode_(✪)_in_parens",
@"http://foo.com/(something)?after=parens",
@"http://☺.damowmow.com/",
@"http://code.google.com/events/#&product=browser",
@"http://j.mp",
@"http://foo.bar/?q=Test%20URL-encoded%20stuff",
@"http://مثال.إختبار",
@"http://例子.测试",
@"http://उदाहरण.परीक्षा",
@"http://-.~_!$&'()*+,;=:%40:80%2f::::::@example.com",
@"http://1337.net",
@"http://a.b-c.de",
@"http://223.255.255.254"

Probado NO VÁLIDO contra estas URL:

@"",
@"foo",
@"ftp://foo.com",
@"ftp://foo.com",
@"http://..",
@"http://..",
@"http://../",
@"//",
@"///",
@"http://##/",
@"http://.www.foo.bar./",
@"rdar://1234",
@"http://foo.bar?q=Spaces should be encoded",
@"http:// shouldfail.com",
@":// should fail"

Fuente de las URL: https://mathiasbynens.be/demo/url-regex

kgaidis
fuente
3

Puedes usar esto si no quieres httpo httpsowww

NSString *urlRegEx = @"^(http(s)?://)?((www)?\.)?[\w]+\.[\w]+";

ejemplo

- (void) testUrl:(NSString *)urlString{
    NSLog(@"%@: %@", ([self isValidUrl:urlString] ? @"VALID" : @"INVALID"), urlString);
}

- (void)doTestUrls{
    [self testUrl:@"google"];
    [self testUrl:@"google.de"];
    [self testUrl:@"www.google.de"];
    [self testUrl:@"http://www.google.de"];
    [self testUrl:@"http://google.de"];
}

Salida:

INVALID: google
VALID: google.de
VALID: www.google.de
VALID: http://www.google.de
VALID: http://google.de
Hiren
fuente
Esto parece muy interesante. ¿Es 100% a prueba de balas?
Supertecnoboff
3

La solución de Lefakir tiene un problema. Su expresión regular no puede coincidir con " http://instagram.com/p/4Mz3dTJ-ra/ ". El componente de URL tiene un carácter numérico y literal combinado. Su expresión regular falla en tales URL.

Aquí está mi mejora.

"(http|https)://((\\w)*|([0-9]*)|([-|_])*)+([\\.|/]((\\w)*|([0-9]*)|([-|_])*)+)+(/)?(\\?.*)?"
cubierto
fuente
2

Encontré que la forma más fácil de hacer esto es así:

- (BOOL)validateUrl: (NSURL *)candidate
{
    NSURLRequest *req = [NSURLRequest requestWithURL:candidate];
    return [NSURLConnection canHandleRequest:req];
}
julianwyz
fuente
Lo he estado usando por un tiempo y parece funcionar bien. También parece funcionar bien con los nuevos TLD ( namecheap.com/domains/new-tlds/explore.aspx ).
julianwyz
Esto es inutil. Si su URL es una cadena no válida, se bloqueará al crear un NSURL, por lo que se podría verificar en lugar de esto. Y aun así usa API antigua.
Legoless
@Legoless podría usar un intento ... ¿solucionarlo?
COBB
1

La respuesta aprobada es incorrecta. Tengo una URL con un "-" y la validación falla.

Leander
fuente
1

Tweeked la respuesta de Vaibhav para admitir enlaces de G +:

NSString *urlRegEx = @"http(s)?://([\\w-]+\\.)+[\\w-]+(/[\\w-\\+ ./?%&amp;=]*)?";

MuhammadBassio
fuente
1

Algunas URL sin / al final no se detectan como correctas en las soluciones anteriores. Entonces esto podría ser útil.

  extension String {
    func isValidURL() -> Bool{
        let length:Int = self.characters.count
        var err:NSError?
        var dataDetector:NSDataDetector? = NSDataDetector()
        do{
            dataDetector = try NSDataDetector(types: NSTextCheckingType.Link.rawValue)
        }catch{
            err = error as NSError
        }
        if dataDetector != nil{
            let range = NSMakeRange(0, length)
            let notFoundRange = NSRange(location: NSNotFound, length: 0)
            let linkRange = dataDetector?.rangeOfFirstMatchInString(self, options: NSMatchingOptions.init(rawValue: 0), range: range)
            if !NSEqualRanges(notFoundRange, linkRange!) && NSEqualRanges(range, linkRange!){
                return true
            }
        }else{
            print("Could not create link data detector: \(err?.localizedDescription): \(err?.userInfo)")
        }

        return false
    }
}
Mistry Pratik
fuente
1

Validación de URL en Swift

Detalles

Xcode 8.2.1, Swift 3

Código

enum URLSchemes: String

import Foundation

enum URLSchemes: String {
    case http = "http://", https = "https://", ftp = "ftp://", unknown = "unknown://"

    static func detectScheme(urlString: String) -> URLSchemes {

        if URLSchemes.isSchemeCorrect(urlString: urlString, scheme: .http) {
            return .http
        }
        if URLSchemes.isSchemeCorrect(urlString: urlString, scheme: .https) {
            return .https
        }
        if URLSchemes.isSchemeCorrect(urlString: urlString, scheme: .ftp) {
            return .ftp
        }
        return .unknown
    }

    static func getAllSchemes(separetedBy separator: String) -> String {
        return "\(URLSchemes.http.rawValue)\(separator)\(URLSchemes.https.rawValue)\(separator)\(URLSchemes.ftp.rawValue)"
    }

    private static func isSchemeCorrect(urlString: String, scheme: URLSchemes) -> Bool {
        if urlString.replacingOccurrences(of: scheme.rawValue, with: "") == urlString {
            return false
        }
        return true
    }
}

cadena de extensión

import Foundation

extension String {

    var isUrl: Bool {

        // for http://regexr.com checking
        // (?:(?:https?|ftp):\/\/)(?:xn--)?(?:\S+(?::\S*)?@)?(?:(?!10(?:\.\d{1,3}){3})(?!127(?:\.\d{1,3}){3})(?!169\.254(?:\.\d{1,3}){2})(?!192\.168(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]+-?)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]+-?)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[#-z\u00a1-\uffff]{2,})))(?::\d{2,5})?(?:\/[^\s]*)?

        let schemes = URLSchemes.getAllSchemes(separetedBy: "|").replacingOccurrences(of: "://", with: "")
        let regex = "(?:(?:\(schemes)):\\/\\/)(?:xn--)?(?:\\S+(?::\\S*)?@)?(?:(?!10(?:\\.\\d{1,3}){3})(?!127(?:\\.\\d{1,3}){3})(?!169\\.254(?:\\.\\d{1,3}){2})(?!192\\.168(?:\\.\\d{1,3}){2})(?!172\\.(?:1[6-9]|2\\d|3[0-1])(?:\\.\\d{1,3}){2})(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}(?:\\.(?:[1-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))|(?:(?:[a-z\\u00a1-\\uffff0-9]+-?)*[a-z\\u00a1-\\uffff0-9]+)(?:\\.(?:[a-z\\u00a1-\\uffff0-9]+-?)*[a-z\\u00a1-\\uffff0-9]+)*(?:\\.(?:[#-z\\u00a1-\\uffff]{2,})))(?::\\d{2,5})?(?:\\/[^\\s]*)?"


        let regularExpression = try! NSRegularExpression(pattern: regex, options: [])
        let range = NSRange(location: 0, length: self.characters.count)
        let matches = regularExpression.matches(in: self, options: [], range: range)
        for match in matches {
            if range.location == match.range.location && range.length == match.range.length {
                return true
            }
        }
        return false
    }

    var toURL: URL? {

        let urlChecker: (String)->(URL?) = { url_string in
            if url_string.isUrl, let url = URL(string: url_string) {
                return url
            }
            return nil
        }

        if !contains(".") {
            return nil
        }

        if let url = urlChecker(self) {
            return url
        }

        let scheme = URLSchemes.detectScheme(urlString: self)
        if scheme == .unknown {
            let newEncodedString = URLSchemes.http.rawValue + self
            if let url = urlChecker(newEncodedString) {
                return url
            }
        }

        return nil
    }
}

Uso

 func tests() {

    chekUrl(urlString:"http://example.com")
    chekUrl(urlString:"https://example.com")
    chekUrl(urlString:"http://example.com/dir/file.php?var=moo")
    chekUrl(urlString:"http://xn--h1aehhjhg.xn--d1acj3b")
    chekUrl(urlString:"http://www.example.com/wpstyle/?p=364")
    chekUrl(urlString:"http://-.~_!$&'()*+,;=:%40:80%2f::::::@example.com")
    chekUrl(urlString:"http://example.com")
    chekUrl(urlString:"http://xn--d1acpjx3f.xn--p1ai")
    chekUrl(urlString:"http://xn--74h.damowmow.com/")
    chekUrl(urlString:"ftp://example.com:129/myfiles")
    chekUrl(urlString:"ftp://user:[email protected]:21/file/dir")
    chekUrl(urlString:"ftp://ftp.example.com:2828/asdah%20asdah.gif")
    chekUrl(urlString:"http://142.42.1.1:8080/")
    chekUrl(urlString:"http://142.42.1.1/")
    chekUrl(urlString:"http://userid:[email protected]:8080")
    chekUrl(urlString:"http://[email protected]")
    chekUrl(urlString:"http://[email protected]:8080")
    chekUrl(urlString:"http://foo.com/blah_(wikipedia)#cite-1")
    chekUrl(urlString:"http://foo.com/(something)?after=parens")

    print("\n----------------------------------------------\n")

    chekUrl(urlString:".")
    chekUrl(urlString:" ")
    chekUrl(urlString:"")
    chekUrl(urlString:"-/:;()₽&@.,?!'{}[];'<>+_)(*#^%$")
    chekUrl(urlString:"localhost")
    chekUrl(urlString:"yandex.")
    chekUrl(urlString:"коряга")
    chekUrl(urlString:"http:///a")
    chekUrl(urlString:"ftps://foo.bar/")
    chekUrl(urlString:"rdar://1234")
    chekUrl(urlString:"h://test")
    chekUrl(urlString:":// should fail")
    chekUrl(urlString:"http://-error-.invalid/")
    chekUrl(urlString:"http://.www.example.com/")
}

func chekUrl(urlString: String) {
    var result = ""
    if urlString.isUrl {
        result += "url: "
    } else {
        result += "not url: "
    }
    result += "\"\(urlString)\""
    print(result)
}

Resultado

ingrese la descripción de la imagen aquí

Vasily Bodnarchuk
fuente
1

C objetivo

- (BOOL)validateUrlString:(NSString*)urlString
{
    if (!urlString)
    {
        return NO;
    }

    NSDataDetector *linkDetector = [NSDataDetector dataDetectorWithTypes:NSTextCheckingTypeLink error:nil];

    NSRange urlStringRange = NSMakeRange(0, [urlString length]);
    NSMatchingOptions matchingOptions = 0;

    if (1 != [linkDetector numberOfMatchesInString:urlString options:matchingOptions range:urlStringRange])
    {
        return NO;
    }

    NSTextCheckingResult *checkingResult = [linkDetector firstMatchInString:urlString options:matchingOptions range:urlStringRange];

    return checkingResult.resultType == NSTextCheckingTypeLink && NSEqualRanges(checkingResult.range, urlStringRange);
}

¡Espero que esto ayude!

Dharmesh Mansata
fuente
0

¿Quisiste comprobar si lo que el usuario ingresó es una URL? Puede ser tan simple como una expresión regular, por ejemplo, la comprobación de si la cadena contiene www.(esta es la forma en que los cheques Yahoo Messenger Si el estado del usuario es un enlace o no)
la esperanza de que la ayuda

phunehehe
fuente
0

Egoístamente, sugeriría usar una KSURLFormatterinstancia para validar la entrada y convertirla en algo que NSURLpueda manejar.

Mike Abdullah
fuente
¿Hay algo de esto en iOS?
capikaw
Debería funcionar bien en iOS. Si no es así, arréglelo y envíeme una solicitud de extracción, o presente un problema
Mike Abdullah
0

He creado una clase heredada de UITextField que puede manejar todo tipo de validación usando una cadena de expresiones regulares. En esto, solo necesita darles toda la cadena de expresiones regulares en secuencia y su mensaje que desea mostrar cuando la validación falla. Puedes consultar mi blog para obtener más información, realmente te ayudará.

http://dhawaldawar.wordpress.com/2014/06/11/uitextfield-validation-ios/

Dhawal Dawar
fuente
0

Extendiendo la respuesta de @ Anthony a swift, escribí una categoría en la Stringque devuelve un opcional NSURL. El valor de retorno es nilsi Stringno se puede validar como una URL.

import Foundation

// A private global detector variable which can be reused.
private let detector = try! NSDataDetector(types: NSTextCheckingType.Link.rawValue)

extension String {
  func URL() -> NSURL? {
    let textRange = NSMakeRange(0, self.characters.count)
    guard let URLResult = detector.firstMatchInString(self, options: [], range: textRange) else {
      return nil
    }

    // This checks that the whole string is the detected URL. In case
    // you don't have such a requirement, you can remove this code
    // and return the URL from URLResult.
    guard NSEqualRanges(URLResult.range, textRange) else {
      return nil
    }

    return NSURL(string: self)
  }
}
Ayush Goel
fuente
0
func checkValidUrl(_ strUrl: String) -> Bool {
    let urlRegEx: String = "(http|https)://((\\w)*|([0-9]*)|([-|_])*)+([\\.|/]((\\w)*|([0-9]*)|([-|_])*))+"
    let urlTest = NSPredicate(format: "SELF MATCHES %@", urlRegEx)
    return urlTest.evaluate(with: strUrl)
}
Urvish Modi
fuente