如何在 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.0if 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 在代码的哪一部分发出网络请求? @CyberMew `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\\.&%\\$\\-]+)*@)*((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\\.\\,\\?\\'\\\\\\+&%\\$#\\=~_\\-]+))*$"
return self.matches(pattern: urlPattern)
这样,它也可以通过其他用例进行扩展,例如 isValidEmail
、isValidName
或您的应用程序需要的任何东西。
【讨论】:
这似乎不适用于 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 的有效性?的主要内容,如果未能解决你的问题,请参考以下文章
如何检查在 iOS 的 url 中发布的用户名和密码是不是有效