ios - olvide - Cómo validar una url en el iPhone
notes app ios (21)
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
En una aplicación de 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?
¿Por qué no confiar simplemente en 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 .com)
}
De acuerdo con 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 cual inicializar el objeto NSURL. Debe cumplir con RFC 2396. Este método analiza URLString de acuerdo con RFCs 1738 y 1808.Valor de retorno
Un objeto NSURL inicializado con URLString. Si la cadena estaba mal formada, devuelve nil.
¿Querías comprobar si lo que el usuario ingresó es una URL? Puede ser tan simple como una expresión regular, por ejemplo, verificar si la cadena contiene www.
(esta es la forma en que Yahoo Messenger comprueba si el estado del usuario es un enlace o no)
Espero que ayudes
Algunas URL sin / al final no se detectan como la correcta 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
}
}
Egoístamente, sugeriría usar una instancia KSURLFormatter
para validar la entrada y convertirla en algo que NSURL
pueda manejar.
El siguiente código le permitirá encontrar las URL válidas
NSPredicate *websitePredicate = [NSPredicate predicateWithFormat:@"SELF MATCHES %@",@"^(((((h|H)(t|T){2}(p|P)s?)|((f|F)(t|T)(p|P)))://(w{3}.)?)|(w{3}.))[A-Za-z0-9]+(.[A-Za-z0-9-:;/?#_]+)+"];
if ([websitePredicate evaluateWithObject:##MY_STRING##])
{
printf"Valid"
}
para tales URLS
En lugar de escribir sus propias expresiones regulares, confíe en las de Apple. He estado usando una categoría en NSString
que usa NSDataDetector
para probar la presencia de un enlace dentro de una cadena. Si el rango del enlace encontrado por NSDataDetector
es 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;
}
Extendiendo la respuesta de @ Anthony rápidamente, escribí una categoría en String
que devuelve un NSURL
opcional. El valor de retorno es nil
si la String
no 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)
}
}
Gracias a esta publicación , puedes 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];
}
He creado una clase heredada de UITextField que puede manejar todo tipo de validación utilizando una cadena de expresiones regulares. En esto solo necesitas darles toda la secuencia de expresiones regulares en secuencia y el mensaje que quieres 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/
He encontrado que la manera más fácil de hacer esto es así:
- (BOOL)validateUrl: (NSURL *)candidate
{
NSURLRequest *req = [NSURLRequest requestWithURL:candidate];
return [NSURLConnection canHandleRequest:req];
}
La respuesta aprobada es incorrecta. Tengo una URL con un "-" y la validación falla.
La solución de Lefakir tiene un problema. Su expresión regular no puede coincidir con " http://instagram.com/p/4Mz3dTJ-ra/ ". El componente Url tiene un carácter numérico y literal combinado. Su expresión regular falla tales urls.
Aquí está mi mejora.
"(http|https)://((//w)*|([0-9]*)|([-|_])*)+([//.|/]((//w)*|([0-9]*)|([-|_])*)+)+(/)?(//?.*)?"
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 la 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
Por extraño que parezca, realmente no encontré una solución que fuera muy simple, pero aún así hice un buen trabajo para manejar los enlaces http
/ https
.
Tenga en cuenta que ESTA NO ES LA SOLUCIÓN PERFECTA, pero funcionó para los casos a continuación. En resumen, la expresión regular comprueba si la URL comienza con http://
o https://
, luego verifica al menos 1 carácter, luego busca un punto y luego 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 URL: https://mathiasbynens.be/demo/url-regex
Puede usar esto si no quiere http
o https
o www
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
Tweeked la respuesta de Vaibhav para apoyar los enlaces de G +:
NSString *urlRegEx = @"http(s)?://([//w-]+//.)+[//w-]+(/[//w-//+ ./?%&=]*)?";
utilizar esta-
NSString *urlRegEx = @"http(s)?://([//w-]+//.)+[//w-]+(/[//w- ./?%&=]*)?";
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, compruebo si el elemento coincidente es igual a subjectString y, si ese es el caso, el URL es válido :)
Corrígeme si mi expresión regular es incorrecta;)
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!
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)
}