Как проверить правильность URL-адреса в Swift?

Попытка заставить приложение запустить браузер по умолчанию для URL-адреса, но только если введенный URL-адрес действителен, в противном случае отображается сообщение о том, что URL-адрес недействителен.

Как я могу проверить правильность использования Swift?

Ответы

Ответ 1

Если ваша цель - проверить, может ли ваше приложение открыть URL-адрес, вот что вы можете сделать. Хотя сафари может открыть Url, веб-сайт может не существовать или он может быть недоступен.

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
}

Ответ 2

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 использования NSDataDetector вместо UIApplication.shared.canOpenURL:

Мне нужен был метод, который бы определял, предоставил ли пользователь ввод, который является URL-адресом для чего-либо. Во многих случаях пользователи не включают схему URL http:// или https:// в URL, который они вводят - например, вместо "http://www.google.com" они вводят "www.google.com". Без схемы URL UIApplication.shared.canOpenURL не сможет распознать URL и вернет false. NSDataDetector, по сравнению с '', довольно простительный инструмент (как упомянуто в комментариях @AmitaiB) - и он может обнаруживать даже URL без схемы http://. Таким образом, я могу обнаружить URL, не пытаясь добавлять схему каждый раз при тестировании строки.

Sidenote - SFSafariViewController может открывать только URL с http:///https://. Таким образом, если для обнаруженного URL-адреса не указана схема URL-адреса, и вы хотите открыть ссылку, вам придется предварительно добавить схему вручную.

Ответ 3

Для быстрой версии 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)
}

Ответ 4

Я нашел этот чистый (In 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.")
}

Ответ 5

Использование "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
}

Ответ 6

var url:NSURL = NSURL(string: "tel://000000000000")!
if UIApplication.sharedApplication().canOpenURL(url) {
    UIApplication.sharedApplication().openURL(url)
} else {
     // Put your error handler code...
}

Ответ 7

Мое личное предпочтение - приблизиться к этому с расширением, потому что мне нравится называть метод непосредственно на строковый объект.

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 или независимо от того, что требуется вашему приложению.

Ответ 8

Вы можете использовать тип NSURL (конструктор которого возвращает необязательный тип) в сочетании с оператором if-let для проверки правильности заданного 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)
}

Ответ 9

Для версии 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
}

Ответ 10

Это вернет логическое значение для валидности URL или nil, если передан необязательный URL со значением nil.

extension URL {

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

}

Обратите внимание, что если вы планируете использовать представление Safari, вам следует протестировать url.scheme == "http" || url.scheme == "https" url.scheme == "http" || url.scheme == "https".

Ответ 11

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
}

Ответ 12

Это не подход с регулярным выражением, но он наивный, который хорошо работает, чтобы убедиться, что есть хост и расширение, если вы хотите простой и недорогой подход:

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

Используйте это, только если вам нужен быстрый способ проверки на стороне клиента, и у вас есть логика сервера, которая проведет более строгую проверку перед сохранением данных.

Ответ 13

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.
}

Он рекомендовал использовать эту проверку только один раз, а затем повторно использовать.

PS Кредиты для Shachar для этой функции.

Ответ 14

В некоторых случаях этого может быть достаточно, чтобы проверить, что URL-адрес удовлетворяет RFC 1808. Существует несколько способов сделать это. Один пример:

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

Это связано с тем, что.host, а также.path,.fragment и несколько других методов будут возвращать нуль, если URL-адрес не соответствует RFC 1808.

Если вы не проверите, у вас могут быть такие сообщения в журнале консоли:

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

Ответ 15

Попробуй это:

func isValid(urlString: String) -> Bool
{
    if let urlComponents = URLComponents.init(string: urlString), urlComponents.host != nil, urlComponents.url != nil
    {
        return true
    }
    return false
}

Это просто проверяет действительные компоненты URL, и если компоненты хоста и URL-адреса не ноль. Кроме того, вы можете просто добавить это в файл расширений

Ответ 16

Для быстрого 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
}

Ответ 17

Это для последней версии Swift 4, основанной на ответе Дуга Амоса (для быстрого 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
}

Ответ 18

Гелию приходится иметь дело с различными схемами:

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)
    }
}

Ответ 19

Версия, которая работает с 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)
}

Ответ 20

Этот принятый ответ не работает в моем случае с неправильным URL без данных 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
}

Надеюсь, это поможет!