Created
September 16, 2019 04:41
-
-
Save someoneAnyone/fc4fa9cbf51c7566dfd03eeee0846966 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// URL Validator.swift | |
// NightscouterSwiftUI | |
// | |
// Created by Peter Ina on 9/15/19. | |
// Copyright © 2019 Peter Ina. All rights reserved. | |
// | |
import Foundation | |
import Combine | |
enum URLValidatorError: Error { | |
case empty(String) | |
case onlyPrefix(String) | |
case containsWhitespace(String) | |
case couldNotCreateURL(String) | |
case serverError(String) | |
} | |
public extension URL { | |
static let networkActivityPublisher = PassthroughSubject<Bool, Never>() | |
static let isValidURLPublisher = PassthroughSubject<Bool, Never>() | |
fileprivate static func validateURL(string: String) throws -> URL { | |
// Ignore Nils & Empty Strings | |
if (string.isEmpty || string.count < 3 ) | |
{ | |
isValidURLPublisher.send(false) | |
throw URLValidatorError.empty("Url String was empty or less than 3 characters.") | |
} | |
// Ignore prefixes (including partials) | |
let prefixes = ["http://www.", "https://www.", "www."] | |
for prefix in prefixes | |
{ | |
if ((prefix.range(of: string, options: .caseInsensitive, range: nil, locale: nil)) != nil) { | |
isValidURLPublisher.send(false) | |
throw URLValidatorError.onlyPrefix("Url String was prefix only") | |
} | |
} | |
var formattedUrlString = string.replacingOccurrences(of: " ", with: "") | |
// Check that URL already contains required 'http://' or 'https://', prepend if it does not | |
if (!formattedUrlString.hasPrefix("http://") && !formattedUrlString.hasPrefix("https://")) | |
{ | |
formattedUrlString = "https://"+string | |
} | |
guard let finalURL = URL(string: formattedUrlString) else { | |
isValidURLPublisher.send(false) | |
throw URLValidatorError.couldNotCreateURL("Url could not be created.") | |
} | |
isValidURLPublisher.send(true) | |
return finalURL | |
} | |
static func testURLPublisher(string: String) -> AnyPublisher<URL?, Never> { | |
let validatedURL = try? validateURL(string: string) | |
guard let urlToCheck = validatedURL else { | |
return Just(nil).eraseToAnyPublisher() | |
} | |
var request = URLRequest(url: urlToCheck) | |
request.httpMethod = "HEAD" | |
let publisher = URLSession.shared.dataTaskPublisher(for: request) | |
.handleEvents(receiveSubscription: { _ in | |
networkActivityPublisher.send(true) | |
}, receiveCompletion: { _ in | |
networkActivityPublisher.send(false) | |
}, receiveCancel: { | |
networkActivityPublisher.send(false) | |
}) | |
.tryMap { data, response -> URL? in | |
// URL Responded - Check Status Code | |
guard let urlResponse = response as? HTTPURLResponse, ((urlResponse.statusCode >= 200 && urlResponse.statusCode < 400) || urlResponse.statusCode == 405) else { | |
throw URLValidatorError.serverError("Could not find the a servr at: \(urlToCheck)") | |
} | |
return urlResponse.url?.absoluteURL | |
} | |
.catch { err in | |
return Just(nil) | |
} | |
.eraseToAnyPublisher() | |
return publisher | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class NewSiteViewModel: ObservableObject { | |
@Published var validatedURL: URL? | |
//@Published var secretKey: String? | |
@Published var urlString: String = "" | |
@Published var isValidURL: Bool = false | |
private var cancellable = Set<AnyCancellable>() | |
init() { | |
$urlString | |
.throttle(for: 0.5, scheduler: DispatchQueue(label: "Validator"), latest: true) | |
.removeDuplicates() | |
.map { string -> AnyPublisher<URL?, Never> in | |
return URL.testURLPublisher(string: string) | |
} | |
.switchToLatest() | |
.receive(on: RunLoop.main) | |
.sink { recievedURL in | |
guard let url = recievedURL else { | |
self.validatedURL = nil | |
self.isValidURL = false | |
return | |
} | |
self.validatedURL = url | |
self.isValidURL = true | |
} | |
.store(in: &cancellable) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment