如何在 Swift 中检查 URL 的有效性?

Posted

技术标签:

【中文标题】如何在 Swift 中检查 URL 的有效性?【英文标题】:How to check validity of URL in Swift? 【发布时间】:2015-03-20 16:12:15 【问题描述】:

尝试让应用程序启动默认浏览器到某个 URL,但前提是输入的 URL 有效,否则会显示一条消息,指出该 URL 无效。

我将如何使用 Swift 检查有效性?

【问题讨论】:

发送一个 NSURLRequest 并检查返回? 用字符串组成一个NSURL,看看是不是nil? 【参考方案1】:

这里的大部分答案都没有解决我的问题,所以我在这里发布我是如何解决的:

static func isValidDomain(domain: String?) -> Bool 
    guard let domain = domain else 
        return false
    
    // Modified version of https://***.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
    

Milan Nosáľ 的答案缺少的是,它没有解决这个特定的输入:

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

所以我只添加host 检查存在和未计划的“URL”。

【讨论】:

【参考方案2】:

如果您的目标是验证您的应用程序是否可以打开 URL,您可以执行以下操作。虽然 safari 可以打开 URL,但该网站可能不存在或已关闭。

// 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

附带说明,这检查网址是否有效或完整。例如,传递“https://”的调用返回 true。

【讨论】:

如果让 urlString = urlString, url = NSURL(string: urlString) where UIApplication.sharedApplication().canOpenURL(url) return true 当我在 swift2.0 if let url = NSURL(string: urlString) 987654322@ 上测试时没有工作 @VijaySinghRana 那是一个有效的 NSURL,这个函数不会检查 url 是否存在。回答中指出:“如果您的目标是验证您的应用程序是否可以在此处打开一个 url,那么您可以做什么”。 如果 URL 无效,此方法会在调试器中打印此日志“-canOpenURL: failed for URL:”。这个怎么去掉? 请记住,对于不以 protocol:// 开头的网址(例如 http:// 或 https://【参考方案3】:

Swift 5.1 解决方案

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)
    

【讨论】:

【参考方案4】:

2020 年,我的任务是修复在字符串中为字符串链接加下划线的方法的错误。这里的大多数答案都不能正常工作(尝试:aaa.com.bd 或 aaa.bd)这些链接应该是有效的。然后我偶然发现了这个正则表达式字符串。

参考:https://urlregex.com/

所以基于那个正则表达式

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

我们可以写一个函数。

SWIFT 5.x

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

OBJECTIVE-C(随便写,不管是不是分类)。

- (BOOL)stringIsValidURL:(NSString *)string

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

【讨论】:

不错的解决方案和使用,但是我注意到它由于区分大小写而失败:Https://google.com 或computerhope.com/JARGON.HTM 因为 JARGON.HTM 和 jargon.htm 是两个不同的 URL。但是,可以通过在测试前将字符串转换为小写来解决。【参考方案5】:

Swift 4 使用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
        
    

用法:

let string = "https://www.fs.blog/2017/02/naval-ravikant-reading-decision-making/"
if string.isValidURL 
    // TODO

使用NSDataDetector而不是UIApplication.shared.canOpenURL的原因

我需要一种方法来检测用户是否提供了作为 URL 的输入。在许多情况下,用户不会在他们输入的 URL 中包含 http://https:// URL 方案 - 例如,他们会输入 "www.google.com" 而不是 "http://www.google.com"。如果没有 URL 方案,UIApplication.shared.canOpenURL 将无法识别 URL,并将返回 false。与UIApplication.shared.canOpenURL 相比,NSDataDetector 是一个相当宽容的工具(正如 cmets 中提到的@AmitaiB)——它甚至可以检测没有http:// 方案的 URL。这样我就可以检测到一个 URL,而不必在每次测试字符串时都尝试添加方案。

旁注 - SFSafariViewController 只能打开带有 http:///https:// 的 URL。因此,如果检测到的 URL 没有指定 URL 方案,并且您想打开链接,则必须手动添加方案。

【讨论】:

并不是说NSDataDetector 是一个相当宽容的检测工具。考虑:adobe.com/go/flex *isValidURL? true *UIApplication.shared.canOpenURL? false gskinner.com/products/spl *iVU? T *canOpen? F https://google.com *iVU? T *canOpen? T https:google.com *iVU? T *canOpen? T www.cool.com.au *iVU? T *canOpen? F http://www.cool.com.au *iVU? T *canOpen? T http://www.cool.com.au/ersdfs *iVU? T *canOpen? T http://www.cool.com.au/ersdfs?dfd=dfgd@s=1 *iVU? T *canOpen? T http://www.cool.com:81/index.html *iVU? T *canOpen? T @AmitaiB 我正在使用NSDataDetector,因为UIApplication.shared.canOpenURL 返回false 并带有许多有效的URL(可以在SafariViewController 中打开)。我需要突出显示文本中的 URL,然后允许用户在 Safari 视图控制器中打开它们——这就是我最终得到这个解决方案的原因 啊,有道理。 我认为当字符串以 http://(或任何其他有效协议)开头时,canOpen 会返回 true @amsmath 可能,但是很多用户的 URL 中没有包含协议,我需要检测用户输入文本中的 URL。我知道我必须用 dataDetector 的推理来更新答案【参考方案6】:

这个接受的答案在我的情况下不起作用,没有数据https://debug-cdn.checkit4team.com/5/image/Y29tbW9uL2RlZmF1bHRfYXZhdGFyLnBuZw==的错误网址

所以我写扩展来解决

extension String 
    var verifyUrl: Bool 
        get 
            let url = URL(string: self)

            if url == nil || NSData(contentsOf: url!) == nil 
                return false
             else 
                return true
            
        
    

使用它:

if string. verifyUrl 
    // do something

希望对您有所帮助!

【讨论】:

这可能适用于您的情况,但仅当网络连接可用时。此外,我强烈建议您不要仅仅为了验证 URL 有效而调用同步网络请求。最重要的是,从变量定义来看,它执行同步的长时间运行操作并不明显,这可能会在将来从主线程中使用时给您带来很多麻烦。请阅读developer.apple.com/documentation/foundation/nsdata/… @5keeve 在代码的哪一部分发出网络请求? @Cyber​​Mew `NSData(contentsOf: url!)` 发出一个同步(阻塞)请求,它应该用于(小)本地文件,但是当你给它基于网络的 URL 时也这样做。我提到了它,因为答案中提到的 URL 是基于网络的。我提到的文档中还有黄色的重要框,去阅读它;) 好的,谢谢你的信息,今天学到了新东西!【参考方案7】:

适用于 Swift 4.2 并具有可靠的 URL 模式匹配的版本 ...

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)

【讨论】:

不行。这个函数说“aaa.com”无效。【参考方案8】:

Helium不得不处理各种方案:

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)
    

【讨论】:

【参考方案9】:

Swift 4.2 优雅的 URL 构造和验证

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
  

用法

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

【讨论】:

【参考方案10】:

我发现这个很干净(在 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)

用法:

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

===

对于 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.")

【讨论】:

不错的解决方案!谢谢! 不适用于我的网址,例如 https://abc.com/filename.html?utm_source=xyzconnect&utm_medium=service 它是一个虚拟网址,开头有空格,因为无法粘贴实际网址。 是的,上面的逻辑只是验证,它不会转换原始字符串! @Ashok Rohit 的意思是他添加了一个空格,以便您可以阅读。我认为上面的正则表达式是无效的,因为它没有处理参数和/或强制执行不必要的子域,这不是必需的,从而使许多有效的 url 无效。例如。 example.com/…【参考方案11】:

这将返回一个表示 URL 有效性的布尔值,如果传递了一个值为 nil 的可选 URL,则返回 nil。

extension URL 

    var isValid: Bool 
        get 
            return UIApplication.shared.canOpenURL(self)
        
    


请注意,如果您打算使用 Safari 视图,则应测试 url.scheme == "http" || url.scheme == "https"

【讨论】:

【参考方案12】:

这是最新的 Swift 4,基于 Doug Amos 答案(适用于 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

【讨论】:

【参考方案13】:

对于 Swift 4 版本

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

【讨论】:

【参考方案14】:

对于 swift 4,您可以使用:

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

【讨论】:

【参考方案15】:

在某些情况下,检查 url 是否满足 RFC 1808 就足够了。有几种方法可以做到这一点。一个例子:

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

这是因为如果 url 不符合 RFC 1808,.host 以及 .path、.fragment 和其他一些方法将返回 nil。

如果不检查,控制台日志中可能会有这种消息:

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

【讨论】:

【参考方案16】:

试试这个:

func isValid(urlString: String) -> Bool

    if let urlComponents = URLComponents.init(string: urlString), urlComponents.host != nil, urlComponents.url != nil
    
        return true
    
    return false

这只是检查有效的 URL 组件以及主机和 url 组件是否不为零。此外,您可以将其添加到扩展文件中

【讨论】:

【参考方案17】:
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.

建议仅使用此检查一次,然后重复使用。

附注感谢 Shachar 提供此功能。

【讨论】:

【参考方案18】:

对于已接受答案的 swift 3 版本:

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

或者更快捷的解决方案:

func verifyUrl(urlString: String?) -> Bool 
    guard let urlString = urlString,
          let url = URL(string: urlString) else  
        return false 
    

    return UIApplication.shared.canOpenURL(url)

【讨论】:

虽然它忠实于以前的版本,但您可以将if let..s 合并为一个检查,然后还将if 翻转为guard ... else return false ,以使@987654326 更清晰@ 是失败状态,return UIApplication... 是(可能的)成功状态。 @ReactiveRaven 是的,我同意。我按照建议添加了一个版本【参考方案19】:

使用“canOpenUrl”对我的用例来说太贵了,我发现这种方法更快

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

【讨论】:

这非常适合我在 UIApplication.shared 不可用的扩展中使用。谢谢! 对于 Swift 4:string.characters.count 变为 string.count【参考方案20】:

这不是一种正则表达式方法,但它是一种天真的方法,如果您想要一种简单且便宜的方法,它可以很好地确保存在主机和扩展:

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
    

仅当您想要一种快速检查客户端的方法并且您的服务器逻辑将在保存数据之前进行更严格的检查时才使用此选项。

【讨论】:

【参考方案21】:

我个人的偏好是使用扩展来解决这个问题,因为我喜欢直接在字符串对象上调用该方法。

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)
    

这样,它也可以通过其他用例进行扩展,例如 isValidEmailisValidName 或您的应用程序需要的任何东西。

【讨论】:

这似乎不适用于 google.com。它只需要google.com @lwdthe1 是的,正则表达式有一些漏洞。不过,重点是解决方案的架构(通过扩展来接近它)。完全可以在您的应用程序中用您自己的正则表达式替换。或者,如果你有更好的 - 改进我的答案! :)【参考方案22】:
var url:NSURL = NSURL(string: "tel://000000000000")!
if UIApplication.sharedApplication().canOpenURL(url) 
    UIApplication.sharedApplication().openURL(url)
 else 
     // Put your error handler code...

【讨论】:

【参考方案23】:

您可以将NSURL 类型(其构造函数返回一个可选类型)与if-let statement 结合使用来检查给定URL 的有效性。换句话说,利用NSURL failable initializer,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)

【讨论】:

以上是关于如何在 Swift 中检查 URL 的有效性?的主要内容,如果未能解决你的问题,请参考以下文章

如何检查url在lua中是不是有效?

如何在 Java 中检查有效的 URL?

如何检查 URL 在 Android 中是不是有效

如何检查在 iOS 的 url 中发布的用户名和密码是不是有效

swift 4.2 - 如果var具有有效的枚举值,我如何检查guard

从 URL 获取 UIView 的图像 [Swift]