【发布时间】:2015-03-20 16:12:15
【问题描述】:
尝试让应用程序启动默认浏览器到某个 URL,但前提是输入的 URL 有效,否则会显示一条消息,指出该 URL 无效。
我将如何使用 Swift 检查有效性?
【问题讨论】:
-
发送一个 NSURLRequest 并检查返回?
-
用字符串组成一个NSURL,看看是不是nil?
尝试让应用程序启动默认浏览器到某个 URL,但前提是输入的 URL 有效,否则会显示一条消息,指出该 URL 无效。
我将如何使用 Swift 检查有效性?
【问题讨论】:
这里的大部分答案都没有解决我的问题,所以我在这里发布我是如何解决的:
static func isValidDomain(domain: String?) -> Bool {
guard let domain = domain else {
return false
}
// Modified version of https://stackoverflow.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”。
【讨论】:
如果您的目标是验证您的应用程序是否可以打开 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。
【讨论】:
if let url = NSURL(string: urlString) {987654322@ 上测试时没有工作
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)
}
}
【讨论】:
2020 年,我的任务是修复在字符串中为字符串链接加下划线的方法的错误。这里的大多数答案都不能正常工作(尝试:aaa.com.bd 或 aaa.bd)这些链接应该是有效的。然后我偶然发现了这个正则表达式字符串。
所以基于那个正则表达式
"((?:http|https)://)?(?:www\\.)?[\\w\\d\\-_]+\\.\\w{2,3}(\\.\\w{2})?(/(?<=/)(?:[\\w\\d\\-./_]+)?)?"
我们可以写一个函数。
SWIFT 5.x:
extension String {
var validURL: Bool {
get {
let regEx = "((?:http|https)://)?(?:www\\.)?[\\w\\d\\-_]+\\.\\w{2,3}(\\.\\w{2})?(/(?<=/)(?:[\\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\\-_]+\\.\\w{2,3}(\\.\\w{2})?(/(?<=/)(?:[\\w\\d\\-./_]+)?)?";
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF MATCHES %@" argumentArray:@[regEx]];
return [predicate evaluateWithObject:string];
}
【讨论】:
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
NSDataDetector,因为UIApplication.shared.canOpenURL 返回false 并带有许多有效的URL(可以在SafariViewController 中打开)。我需要突出显示文本中的 URL,然后允许用户在 Safari 视图控制器中打开它们——这就是我最终得到这个解决方案的原因
这个接受的答案在我的情况下不起作用,没有数据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
}
希望对您有所帮助!
【讨论】:
适用于 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)
}
【讨论】:
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)
}
}
【讨论】:
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
}
【讨论】:
我发现这个很干净(在 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.")
}
【讨论】:
这将返回一个表示 URL 有效性的布尔值,如果传递了一个值为 nil 的可选 URL,则返回 nil。
extension URL {
var isValid: Bool {
get {
return UIApplication.shared.canOpenURL(self)
}
}
}
请注意,如果您打算使用 Safari 视图,则应测试 url.scheme == "http" || url.scheme == "https"。
【讨论】:
这是最新的 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
}
【讨论】:
对于 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
}
【讨论】:
对于 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
}
【讨论】:
在某些情况下,检查 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
【讨论】:
试试这个:
func isValid(urlString: String) -> Bool
{
if let urlComponents = URLComponents.init(string: urlString), urlComponents.host != nil, urlComponents.url != nil
{
return true
}
return false
}
这只是检查有效的 URL 组件以及主机和 url 组件是否不为零。此外,您可以将其添加到扩展文件中
【讨论】:
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 提供此功能。
【讨论】:
对于已接受答案的 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... 是(可能的)成功状态。
使用“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
}
【讨论】:
string.characters.count 变为 string.count
这不是一种正则表达式方法,但它是一种天真的方法,如果您想要一种简单且便宜的方法,它可以很好地确保存在主机和扩展:
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
}
}
仅当您想要一种快速检查客户端的方法并且您的服务器逻辑将在保存数据之前进行更严格的检查时才使用此选项。
【讨论】:
我个人的偏好是使用扩展来解决这个问题,因为我喜欢直接在字符串对象上调用该方法。
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 或您的应用程序需要的任何东西。
【讨论】:
var url:NSURL = NSURL(string: "tel://000000000000")!
if UIApplication.sharedApplication().canOpenURL(url) {
UIApplication.sharedApplication().openURL(url)
} else {
// Put your error handler code...
}
【讨论】:
您可以将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)
}
【讨论】: