アプリにURLのデフォルトのブラウザーを起動させようとしますが、入力されたURLが有効な場合のみ、URLが無効であるというメッセージが表示されます。
Swiftを使用して有効性を確認するにはどうすればよいですか?
ここでアプリケーションがURLを開くことができるかどうかを確認することが目的の場合は、次のようにします。サファリはURLを開くことができますが、Webサイトが存在しないか、ダウンしている可能性があります。
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.sharedApplication().canOpenURL(url)
}
}
return false
}
Swift 4NSDataDetector
を使用したエレガントなソリューション:
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
}
UIApplication.shared.canOpenURL
の代わりにNSDataDetector
を使用する理由:
ユーザーが何かへのURLである入力を提供したかどうかを検出するメソッドが必要でした。多くの場合、ユーザーは、入力するURLにhttp://
もhttps://
のURLスキームも含めません。たとえば、"http://www.google.com"
の代わりに"www.google.com"
を入力します。 URLスキームがないと、UIApplication.shared.canOpenURL
はURLを認識できず、false
を返します。 NSDataDetector
は、「」と比較すると、かなり寛容なツールです(@AmitaiBがコメントで言及しているように)-また、http://
スキームがなくてもURLを検出できます。この方法で、文字列をテストするときに毎回スキームを追加しようとせずにURLを検出できます。
サイドノート-SFSafariViewController
は、http://
/https://
のURLのみを開くことができます。したがって、検出されたURLにURLスキームが指定されておらず、リンクを開く場合は、スキームを手動で追加する必要があります。
承認済み回答の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)
}
私はこれをきれいに見つけました(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.")
}
「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
}
var url:NSURL = NSURL(string: "tel://000000000000")!
if UIApplication.sharedApplication().canOpenURL(url) {
UIApplication.sharedApplication().openURL(url)
} else {
// Put your error handler code...
}
文字列オブジェクトでメソッドを直接呼び出すのが好きなので、私の個人的な好みは、拡張機能でこれにアプローチすることです。
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
などの別のユースケースや、アプリケーションに必要なもので拡張することもできます。
これは、URLの有効性のブール値を返します。nilの値を持つオプションのURLが渡された場合はnilを返します。
extension URL {
var isValid: Bool {
get {
return UIApplication.shared.canOpenURL(self)
}
}
}
Safariビューを使用する場合は、url.scheme == "http" || url.scheme == "https"
をテストする必要があることに注意してください。
NSURL
タイプ(コンストラクターがオプションのタイプを返す)を if-let
ステートメント と組み合わせて使用して、特定のURLの有効性を確認できます。言い換えると、Swiftの主要な機能であるNSURL
failable initializer を使用します。
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 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.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
}
場合によっては、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
これは正規表現によるアプローチではありませんが、単純で安価なアプローチが必要な場合は、ホストと拡張機能があることを確認するのに適した単純なアプローチです。
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 {
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 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
}
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)
}
これは、最新のSwift 4に基づいています。これはDoug Amos answer(for 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
}
これを試して:
func isValid(urlString: String) -> Bool
{
if let urlComponents = URLComponents.init(string: urlString), urlComponents.Host != nil, urlComponents.url != nil
{
return true
}
return false
}
これは、有効なURLコンポーネントを確認し、Hostおよびurlコンポーネントがnilでないかどうかを確認するだけです。また、これを拡張機能ファイルに追加するだけです
ヘリウム さまざまなスキームに対処する必要があります:
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)
}
}