forked from tectronics/swift-magic-pills
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathURLExtensions.swift
108 lines (92 loc) · 3.41 KB
/
URLExtensions.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
import Foundation
public extension URL {
func appendingFragment(_ fragment: String?) -> URL {
var components = URLComponents(url: self, resolvingAgainstBaseURL: true) ?? URLComponents()
components.fragment = fragment
return components.url ?? self
}
func appendingItems(items: [URLQueryItem]) throws -> URL {
guard var components = URLComponents(url: self, resolvingAgainstBaseURL: true) else {
throw MagicError.badRequest
}
components.queryItems = items
guard let urlWithQuery = components.url else {
throw MagicError.badRequest
}
return urlWithQuery
}
func lowercased() -> URL {
let string = absoluteString.lowercased()
return string.urlValue!
}
var isMailTo: Bool {
guard scheme == "mailto" else { return false }
return removingQuery.absoluteString.replacingOccurrences(of: "mailto:", with: "").isValidEmail
}
var isClickToCall: Bool {
guard scheme == "tel" else { return false }
return absoluteString.replacingOccurrences(of: "tel:", with: "").isValidForPhoneDialer
}
/// Is the value is a ClickToCall URL, returns the phone number
var clickToCallDestination: String? {
guard isClickToCall else { return nil }
guard let scheme = scheme else { return nil }
if absoluteString.hasPrefix("\(scheme):\\") {
return absoluteString.removing(prefix: "\(scheme):\\")
}
if absoluteString.hasPrefix("\(scheme):") {
return absoluteString.removing(prefix: "\(scheme):")
}
return nil
}
/// Is the value is a MailTo URL, returns the email
var mailToDestination: String? {
guard isMailTo else { return nil }
guard let scheme = scheme else { return nil }
if absoluteString.hasPrefix("\(scheme):\\") {
return removingQuery.absoluteString.removing(prefix: "\(scheme):\\")
}
if absoluteString.hasPrefix("\(scheme):") {
return removingQuery.absoluteString.removing(prefix: "\(scheme):")
}
return nil
}
/// Return the same url without query params
var removingQuery: URL {
if var components = URLComponents(string: absoluteString) {
components.query = nil
return components.url ?? self
}
return self
}
/// Retrieve the resource of the url (or the absolute string if not available)
var resourceSpecifier: String {
(self as NSURL).resourceSpecifier ?? absoluteString
}
func checkIsReachableOnInternet() async -> Bool {
var request = URLRequest(url: self)
request.httpMethod = "HEAD"
let session = URLSession(configuration: .ephemeral)
defer { session.invalidateAndCancel() }
guard let (_, response) = try? await session.data(for: request),
let httpResponse = response as? HTTPURLResponse,
httpResponse.statusCode == 200 else {
return false
}
return true
}
}
extension URL: Comparable {
public static func < (lhs: URL, rhs: URL) -> Bool {
lhs.absoluteString < rhs.absoluteString
}
}
public extension [URL] {
func filterUnreachableUrls() async -> [URL] {
var result = [URL]()
for url in self where await url.checkIsReachableOnInternet() {
result.append(url)
}
return result
}
}