Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code

Side by Side Diff: FavIcon/FavIcon.swift

Issue 29664569: Favicon: Issue 6245 - SwiftLinted project files (Closed)
Patch Set: Removed the sorted_imports rule and reversed the import calls to be sorted alphabetically Created Feb. 19, 2018, 10:38 a.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff | Download patch
« no previous file with comments | « FavIcon/DetectedIcon.swift ('k') | FavIcon/IconExtraction.swift » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // 1 //
2 // FavIcon 2 // FavIcon
3 // Copyright © 2016 Leon Breedt 3 // Copyright © 2016 Leon Breedt
4 // 4 //
5 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // Licensed under the Apache License, Version 2.0 (the "License");
6 // you may not use this file except in compliance with the License. 6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at 7 // You may obtain a copy of the License at
8 // 8 //
9 // http://www.apache.org/licenses/LICENSE-2.0 9 // http://www.apache.org/licenses/LICENSE-2.0
10 // 10 //
11 // Unless required by applicable law or agreed to in writing, software 11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS, 12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and 14 // See the License for the specific language governing permissions and
15 // limitations under the License. 15 // limitations under the License.
16 // 16 //
17 17
18 // swiftlint:disable file_length
19
18 import Foundation 20 import Foundation
19 21
20 #if os(iOS) 22 #if os(iOS)
21 import UIKit 23 import UIKit
22 /// Alias for the iOS image type (`UIImage`). 24 /// Alias for the iOS image type (`UIImage`).
23 public typealias ImageType = UIImage 25 public typealias ImageType = UIImage
24 #elseif os(OSX) 26 #elseif os(OSX)
25 import Cocoa 27 import Cocoa
26 /// Alias for the OS X image type (`NSImage`). 28 /// Alias for the OS X image type (`NSImage`).
27 public typealias ImageType = NSImage 29 public typealias ImageType = NSImage
28 #endif 30 #endif
29 31
30 /// Represents the result of attempting to download an icon. 32 /// Represents the result of attempting to download an icon.
31 public enum IconDownloadResult { 33 public enum IconDownloadResult {
32 34
33 /// Download successful. 35 /// Download successful.
34 /// 36 ///
35 /// - parameter image: The `ImageType` for the downloaded icon. 37 /// - parameter image: The `ImageType` for the downloaded icon.
36 case success(image: ImageType) 38 case success(image: ImageType)
37 39
38 /// Download failed for some reason. 40 /// Download failed for some reason.
39 /// 41 ///
40 /// - parameter error: The error which can be consulted to determine the roo t cause. 42 /// - parameter error: The error which can be consulted to determine the roo t cause.
41 case failure(error: Error) 43 case failure(error: Error)
42 44
43 } 45 }
44 46
45 /// Responsible for detecting all of the different icons supported by a given si te. 47 /// Responsible for detecting all of the different icons supported by a given si te.
46 @objc public final class FavIcon : NSObject { 48 @objc
49 public final class FavIcon: NSObject {
47 50
48 // swiftlint:disable function_body_length 51 // swiftlint:disable function_body_length
49 52
50 /// Scans a base URL, attempting to determine all of the supported icons tha t can 53 /// Scans a base URL, attempting to determine all of the supported icons tha t can
51 /// be used for favicon purposes. 54 /// be used for favicon purposes.
52 /// 55 ///
53 /// It will do the following to determine possible icons that can be used: 56 /// It will do the following to determine possible icons that can be used:
54 /// 57 ///
55 /// - Check whether or not `/favicon.ico` exists. 58 /// - Check whether or not `/favicon.ico` exists.
56 /// - If the base URL returns an HTML page, parse the `<head>` section and c heck for `<link>` 59 /// - If the base URL returns an HTML page, parse the `<head>` section and c heck for `<link>`
57 /// and `<meta>` tags that reference icons using Apple, Microsoft and Goog le 60 /// and `<meta>` tags that reference icons using Apple, Microsoft and Goog le
58 /// conventions. 61 /// conventions.
59 /// - If _Web Application Manifest JSON_ (`manifest.json`) files are referen ced, or 62 /// - If _Web Application Manifest JSON_ (`manifest.json`) files are referen ced, or
60 /// _Microsoft browser configuration XML_ (`browserconfig.xml`) files 63 /// _Microsoft browser configuration XML_ (`browserconfig.xml`) files
61 /// are referenced, download and parse them to check if they reference ico ns. 64 /// are referenced, download and parse them to check if they reference ico ns.
62 /// 65 ///
63 /// All of this work is performed in a background queue. 66 /// All of this work is performed in a background queue.
64 /// 67 ///
65 /// - parameter url: The base URL to scan. 68 /// - parameter url: The base URL to scan.
66 /// - parameter completion: A closure to call when the scan has completed. T he closure will be call 69 /// - parameter completion: A closure to call when the scan has completed. T he closure will be call
67 /// on the main queue. 70 /// on the main queue.
68 @objc public static func scan(_ url: URL, completion: @escaping ([DetectedIc on], [String:String]) -> Void) { 71 @objc
72 public static func scan(_ url: URL, completion: @escaping ([DetectedIcon], [ String: String]) -> Void) {
69 let queue = DispatchQueue(label: "org.bitserf.FavIcon", attributes: []) 73 let queue = DispatchQueue(label: "org.bitserf.FavIcon", attributes: [])
70 var icons: [DetectedIcon] = [] 74 var icons: [DetectedIcon] = []
71 var additionalDownloads: [URLRequestWithCallback] = [] 75 var additionalDownloads: [URLRequestWithCallback] = []
72 let urlSession = urlSessionProvider() 76 let urlSession = urlSessionProvider()
73 var meta: [String:String] = [:] 77 var meta: [String: String] = [:]
74 78
75 let downloadHTMLOperation = DownloadTextOperation(url: url, session: url Session) 79 let downloadHTMLOperation = DownloadTextOperation(url: url, session: url Session)
76 let downloadHTML = urlRequestOperation(downloadHTMLOperation) { result i n 80 let downloadHTML = urlRequestOperation(downloadHTMLOperation) { result i n
77 if case let .textDownloaded(actualURL, text, contentType) = result { 81 if case let .textDownloaded(actualURL, text, contentType) = result {
78 if contentType == "text/html" { 82 if contentType == "text/html" {
79 let document = HTMLDocument(string: text) 83 let document = HTMLDocument(string: text)
80 84
81 let htmlIcons = extractHTMLHeadIcons(document, baseURL: actu alURL) 85 let htmlIcons = extractHTMLHeadIcons(document, baseURL: actu alURL)
82 let htmlMeta = examineHTMLMeta(document, baseURL: actualURL) 86 let htmlMeta = examineHTMLMeta(document, baseURL: actualURL)
83 queue.sync { 87 queue.sync {
84 icons.append(contentsOf: htmlIcons) 88 icons.append(contentsOf: htmlIcons)
85 meta = htmlMeta 89 meta = htmlMeta
86 } 90 }
87 91
88 for manifestURL in extractWebAppManifestURLs(document, baseU RL: url) { 92 for manifestURL in extractWebAppManifestURLs(document, baseU RL: url) {
89 let downloadOperation = DownloadTextOperation(url: manif estURL, 93 let downloadOperation = DownloadTextOperation(url: manif estURL,
90 se ssion: urlSession) 94 session: u rlSession)
91 let download = urlRequestOperation(downloadOperation) { result in 95 let download = urlRequestOperation(downloadOperation) { result in
92 if case .textDownloaded(_, let manifestJSON, _) = re sult { 96 if case .textDownloaded(_, let manifestJSON, _) = re sult {
93 let jsonIcons = extractManifestJSONIcons( 97 let jsonIcons = extractManifestJSONIcons(
94 manifestJSON, 98 manifestJSON,
95 baseURL: actualURL 99 baseURL: actualURL
96 ) 100 )
97 queue.sync { 101 queue.sync {
98 icons.append(contentsOf: jsonIcons) 102 icons.append(contentsOf: jsonIcons)
99 } 103 }
100 } 104 }
(...skipping 16 matching lines...) Expand all
117 icons.append(contentsOf: xmlIcons) 121 icons.append(contentsOf: xmlIcons)
118 } 122 }
119 } 123 }
120 } 124 }
121 additionalDownloads.append(download) 125 additionalDownloads.append(download)
122 } 126 }
123 } 127 }
124 } 128 }
125 } 129 }
126 130
127
128 let favIconURL = URL(string: "/favicon.ico", relativeTo: url as URL)!.ab soluteURL 131 let favIconURL = URL(string: "/favicon.ico", relativeTo: url as URL)!.ab soluteURL
129 let checkFavIconOperation = CheckURLExistsOperation(url: favIconURL, ses sion: urlSession) 132 let checkFavIconOperation = CheckURLExistsOperation(url: favIconURL, ses sion: urlSession)
130 let checkFavIcon = urlRequestOperation(checkFavIconOperation) { result i n 133 let checkFavIcon = urlRequestOperation(checkFavIconOperation) { result i n
131 if case let .success(actualURL) = result { 134 if case let .success(actualURL) = result {
132 queue.sync { 135 queue.sync {
133 icons.append(DetectedIcon(url: actualURL, type: .classic)) 136 icons.append(DetectedIcon(url: actualURL, type: .classic))
134 } 137 }
135 } 138 }
136 } 139 }
137 140
138 let touchIconURL = URL(string: "/apple-touch-icon.png", relativeTo: url as URL)!.absoluteURL 141 let touchIconURL = URL(string: "/apple-touch-icon.png", relativeTo: url as URL)!.absoluteURL
139 let checkTouchIconOperation = CheckURLExistsOperation(url: touchIconURL, session: urlSession) 142 let checkTouchIconOperation = CheckURLExistsOperation(url: touchIconURL, session: urlSession)
140 let checkTouchIcon = urlRequestOperation(checkTouchIconOperation) { resu lt in 143 let checkTouchIcon = urlRequestOperation(checkTouchIconOperation) { resu lt in
141 if case let .success(actualURL) = result { 144 if case let .success(actualURL) = result {
142 queue.sync { 145 queue.sync {
143 icons.append(DetectedIcon(url: actualURL, type: .appleIOSWebClip, width: 60, height: 60)) 146 icons.append(DetectedIcon(url: actualURL, type: .appleIOSWebClip, width: 60, height: 60))
144 } 147 }
145 } 148 }
146 } 149 }
147 150
(...skipping 12 matching lines...) Expand all
160 } 163 }
161 } 164 }
162 // swiftlint:enable function_body_length 165 // swiftlint:enable function_body_length
163 166
164 /// Downloads an array of detected icons in the background. 167 /// Downloads an array of detected icons in the background.
165 /// 168 ///
166 /// - parameter icons: The icons to download. 169 /// - parameter icons: The icons to download.
167 /// - parameter completion: A closure to call when all download tasks have 170 /// - parameter completion: A closure to call when all download tasks have
168 /// results available (successful or otherwise). The closure 171 /// results available (successful or otherwise). The closure
169 /// will be called on the main queue. 172 /// will be called on the main queue.
170 @objc public static func download(_ icons: [DetectedIcon], completion: @esca ping ([ImageType]) -> Void) { 173 @objc
174 public static func download(_ icons: [DetectedIcon], completion: @escaping ( [ImageType]) -> Void) {
171 let urlSession = urlSessionProvider() 175 let urlSession = urlSessionProvider()
172 let operations: [DownloadImageOperation] = 176 let operations: [DownloadImageOperation] =
173 icons.map { DownloadImageOperation(url: $0.url, session: urlSession) } 177 icons.map { DownloadImageOperation(url: $0.url, session: urlSession) }
174 178
175 executeURLOperations(operations) { results in 179 executeURLOperations(operations) { results in
176 let downloadResults: [ImageType] = results.flatMap { result in 180 let downloadResults: [ImageType] = results.flatMap { result in
177 switch result { 181 switch result {
178 case .imageDownloaded(_, let image): 182 case .imageDownloaded(_, let image):
179 return image; 183 return image
180 case .failed(_): 184 case .failed:
181 return nil; 185 return nil
182 default: 186 default:
183 return nil; 187 return nil
184 } 188 }
185 } 189 }
186 190
187 DispatchQueue.main.async { 191 DispatchQueue.main.async {
188 completion(downloadResults) 192 completion(downloadResults)
189 } 193 }
190 } 194 }
191 } 195 }
192 196
193 /// Downloads all available icons by calling `scan(url:)` to discover the av ailable icons, and then 197 /// Downloads all available icons by calling `scan(url:)` to discover the av ailable icons, and then
194 /// performing background downloads of each icon. 198 /// performing background downloads of each icon.
195 /// 199 ///
196 /// - parameter url: The URL to scan for icons. 200 /// - parameter url: The URL to scan for icons.
197 /// - parameter completion: A closure to call when all download tasks have r esults available 201 /// - parameter completion: A closure to call when all download tasks have r esults available
198 /// (successful or otherwise). The closure will be c alled on the main queue. 202 /// (successful or otherwise). The closure will be c alled on the main queue.
199 @objc public static func downloadAll(_ url: URL, completion: @escaping ([Ima geType]) -> Void) { 203 @objc
200 scan(url) { icons, meta in 204 public static func downloadAll(_ url: URL, completion: @escaping ([ImageType ]) -> Void) {
205 scan(url) { icons, _ in
201 download(icons, completion: completion) 206 download(icons, completion: completion)
202 } 207 }
203 } 208 }
204 209
205 /// Downloads the most preferred icon, by calling `scan(url:)` to discover a vailable icons, and then choosing 210 /// Downloads the most preferred icon, by calling `scan(url:)` to discover a vailable icons, and then choosing
206 /// the most preferable available icon. If both `width` and `height` are sup plied, the icon closest to the 211 /// the most preferable available icon. If both `width` and `height` are sup plied, the icon closest to the
207 /// preferred size is chosen. Otherwise, the largest icon is chosen, if dime nsions are known. If no icon 212 /// preferred size is chosen. Otherwise, the largest icon is chosen, if dime nsions are known. If no icon
208 /// has dimensions, the icons are chosen by order of their `DetectedIconType ` enumeration raw value. 213 /// has dimensions, the icons are chosen by order of their `DetectedIconType ` enumeration raw value.
209 /// 214 ///
210 /// - parameter url: The URL to scan for icons. 215 /// - parameter url: The URL to scan for icons.
211 /// - parameter width: The preferred icon width, in pixels, or `nil`. 216 /// - parameter width: The preferred icon width, in pixels, or `nil`.
212 /// - parameter height: The preferred icon height, in pixels, or `nil`. 217 /// - parameter height: The preferred icon height, in pixels, or `nil`.
213 /// - parameter completion: A closure to call when the download task has pro duced results. The closure will 218 /// - parameter completion: A closure to call when the download task has pro duced results. The closure will
214 /// be called on the main queue. 219 /// be called on the main queue.
215 /// - throws: An appropriate `IconError` if downloading was not successful. 220 /// - throws: An appropriate `IconError` if downloading was not successful.
216 @objc public static func downloadPreferred(_ url: URL, 221 @objc
222 public static func downloadPreferred(_ url: URL,
217 width: Int, 223 width: Int,
218 height: Int, 224 height: Int,
219 completion: @escaping (ImageType?) -> V oid) { 225 completion: @escaping (ImageType?) -> V oid) {
220 scan(url) { icons, meta in 226 scan(url) { icons, _ in
221 guard let icon = chooseIcon(icons, width: width, height: height) els e { 227 guard let icon = chooseIcon(icons, width: width, height: height) els e {
222 DispatchQueue.main.async { 228 DispatchQueue.main.async {
223 completion(ImageType()); 229 completion(ImageType())
224 } 230 }
225 return 231 return
226 } 232 }
227 233
228 let urlSession = urlSessionProvider() 234 let urlSession = urlSessionProvider()
229 235
230 let operations = [DownloadImageOperation(url: icon.url, session: url Session)] 236 let operations = [DownloadImageOperation(url: icon.url, session: url Session)]
231 executeURLOperations(operations) { results in 237 executeURLOperations(operations) { results in
232 let downloadResults: [ImageType] = results.flatMap { result in 238 let downloadResults: [ImageType] = results.flatMap { result in
233 switch result { 239 switch result {
234 case let .imageDownloaded(_, image): 240 case let .imageDownloaded(_, image):
235 return image; 241 return image
236 case .failed(_): 242 case .failed:
237 return nil; 243 return nil
238 default: 244 default:
239 return nil; 245 return nil
240 } 246 }
241 } 247 }
242 248
243 DispatchQueue.main.async { 249 DispatchQueue.main.async {
244 completion(downloadResults.first) 250 completion(downloadResults.first)
245 } 251 }
246 } 252 }
247 } 253 }
248 } 254 }
249 255
250 @objc public static func chooseLargestIconSmallerThan(_ icons: [DetectedIcon], width: Int, height: Int) -> DetectedIcon? { 256 @objc
251 var filteredIcons = icons; 257 public static func chooseLargestIconSmallerThan(_ icons: [DetectedIcon], wid th: Int, height: Int) -> DetectedIcon? {
252 if (width > 0 && height > 0) { 258 var filteredIcons = icons
253 filteredIcons = icons.filter { (icon) -> Bool in 259 if width > 0 && height > 0 {
260 filteredIcons = icons.filter { icon -> Bool in
254 if let iconWidth = icon.width, 261 if let iconWidth = icon.width,
255 let iconHeight = icon.height { 262 let iconHeight = icon.height {
256 return iconWidth <= width && iconHeight <= height; 263 return iconWidth <= width && iconHeight <= height
257 } else { return true; } 264 } else { return true; }
258 } 265 }
259 } 266 }
260 return chooseIcon(filteredIcons, width: 0, height: 0); 267 return chooseIcon(filteredIcons, width: 0, height: 0)
261 268
262 } 269 }
263 270
264 @objc public static func choseIconLargerThan(_ icons: [DetectedIcon], width: I nt, height: Int) -> DetectedIcon? { 271 @objc
265 var filteredIcons = icons; 272 public static func choseIconLargerThan(_ icons: [DetectedIcon], width: Int, height: Int) -> DetectedIcon? {
266 if (width > 0 && height > 0) { 273 var filteredIcons = icons
267 filteredIcons = icons.filter { (icon) -> Bool in 274 if width > 0 && height > 0 {
275 filteredIcons = icons.filter { icon -> Bool in
268 if let iconWidth = icon.width, 276 if let iconWidth = icon.width,
269 let iconHeight = icon.height { 277 let iconHeight = icon.height {
270 return iconWidth >= width && iconHeight >= height; 278 return iconWidth >= width && iconHeight >= height
271 } else { return true; } 279 } else { return true; }
272 } 280 }
273 } 281 }
274 return chooseIcon(filteredIcons, width: 0, height: 0); 282 return chooseIcon(filteredIcons, width: 0, height: 0)
275
276 } 283 }
277 // MARK: Test hooks 284 // MARK: Test hooks
278 285
279 typealias URLSessionProvider = () -> URLSession 286 typealias URLSessionProvider = () -> URLSession
287
280 @objc static var urlSessionProvider: URLSessionProvider = FavIcon.createDefa ultURLSession 288 @objc static var urlSessionProvider: URLSessionProvider = FavIcon.createDefa ultURLSession
281 289
282 // MARK: Internal 290 // MARK: Internal
283 291
284 @objc static func createDefaultURLSession() -> URLSession { 292 @objc
293 static func createDefaultURLSession() -> URLSession {
285 return URLSession.shared 294 return URLSession.shared
286 } 295 }
287 296
288 /// Helper function to choose an icon to use out of a set of available icons . If preferred 297 /// Helper function to choose an icon to use out of a set of available icons . If preferred
289 /// width or height is supplied, the icon closest to the preferred size is c hosen. If no 298 /// width or height is supplied, the icon closest to the preferred size is c hosen. If no
290 /// preferred width or height is supplied, the largest icon (if known) is ch osen. 299 /// preferred width or height is supplied, the largest icon (if known) is ch osen.
291 /// 300 ///
292 /// - parameter icons: The icons to choose from. 301 /// - parameter icons: The icons to choose from.
293 /// - parameter width: The preferred icon width. 302 /// - parameter width: The preferred icon width.
294 /// - parameter height: The preferred icon height. 303 /// - parameter height: The preferred icon height.
295 /// - returns: The chosen icon, or `nil`, if `icons` is empty. 304 /// - returns: The chosen icon, or `nil`, if `icons` is empty.
296 static func chooseIcon(_ icons: [DetectedIcon], width: Int? = nil, height: I nt? = nil) -> DetectedIcon? { 305 static func chooseIcon(_ icons: [DetectedIcon], width: Int? = nil, height: I nt? = nil) -> DetectedIcon? {
297 guard icons.count > 0 else { return nil } 306 guard icons.count > 0 else { return nil }
298 307
299 let iconsInPreferredOrder = icons.sorted { left, right in 308 let iconsInPreferredOrder = icons.sorted { left, right in
300 if width! > 0 || height! > 0 { 309 if width! > 0 || height! > 0 {
301 let preferredWidth = width, preferredHeight = height, 310 let preferredWidth = width, preferredHeight = height,
302 widthLeft = left.width, heightLeft = left.height, 311 widthLeft = left.width, heightLeft = left.height,
303 widthRight = right.width, heightRight = right.height; 312 widthRight = right.width, heightRight = right.height
304 // Which is closest to preferred size? 313 // Which is closest to preferred size?
305 let deltaA = abs(widthLeft! - preferredWidth!) * abs(heightLeft! - preferredHeight!) 314 let deltaA = abs(widthLeft! - preferredWidth!) * abs(heightLeft! - preferredHeight!)
306 let deltaB = abs(widthRight! - preferredWidth!) * abs(heightRigh t! - preferredHeight!) 315 let deltaB = abs(widthRight! - preferredWidth!) * abs(heightRigh t! - preferredHeight!)
307 return deltaA < deltaB 316 return deltaA < deltaB
308 } else { 317 } else {
309 if let areaLeft = left.area, let areaRight = right.area { 318 if let areaLeft = left.area, let areaRight = right.area {
310 // Which is larger? 319 // Which is larger?
311 return areaRight < areaLeft 320 return areaRight < areaLeft
312 } 321 }
313 } 322 }
314 323
315 if left.area != nil { 324 if left.area != nil {
316 // Only A has dimensions, prefer it. 325 // Only A has dimensions, prefer it.
317 return true 326 return true
318 } 327 }
319 if right.area != nil { 328 if right.area != nil {
320 // Only B has dimensions, prefer it. 329 // Only B has dimensions, prefer it.
321 return false 330 return false
322 } 331 }
323 332
324 // Neither has dimensions, order by enum value 333 // Neither has dimensions, order by enum value
325 return left.type.rawValue < right.type.rawValue 334 return left.type.rawValue < right.type.rawValue
326 } 335 }
327 336
328 return iconsInPreferredOrder.first! 337 return iconsInPreferredOrder.first!
329 } 338 }
330 339
331 fileprivate override init () { 340 private override init () {
332 } 341 }
333 } 342 }
334 343
335 /// Enumerates errors that can be thrown while detecting or downloading icons. 344 /// Enumerates errors that can be thrown while detecting or downloading icons.
336 enum IconError: Error { 345 enum IconError: Error {
337 /// The base URL specified is not a valid URL. 346 /// The base URL specified is not a valid URL.
338 case invalidBaseURL 347 case invalidBaseURL
339 /// At least one icon to must be specified for downloading. 348 /// At least one icon to must be specified for downloading.
340 case atLeastOneOneIconRequired 349 case atLeastOneOneIconRequired
341 /// Unexpected response when downloading 350 /// Unexpected response when downloading
342 case invalidDownloadResponse 351 case invalidDownloadResponse
343 /// No icons were detected, so nothing could be downloaded. 352 /// No icons were detected, so nothing could be downloaded.
344 case noIconsDetected 353 case noIconsDetected
345 } 354 }
346 355
347 extension FavIcon { 356 extension FavIcon {
348 /// Convenience overload for `scan(url:completion:)` that takes a `String` 357 /// Convenience overload for `scan(url:completion:)` that takes a `String`
349 /// instead of a `URL` as the URL parameter. Throws an error if the URL is n ot a valid URL. 358 /// instead of a `URL` as the URL parameter. Throws an error if the URL is n ot a valid URL.
350 /// 359 ///
351 /// - parameter url: The base URL to scan. 360 /// - parameter url: The base URL to scan.
352 /// - parameter completion: A closure to call when the scan has completed. T he closure will be called 361 /// - parameter completion: A closure to call when the scan has completed. T he closure will be called
353 /// on the main queue. 362 /// on the main queue.
354 /// - throws: An `IconError` if the scan failed for some reason. 363 /// - throws: An `IconError` if the scan failed for some reason.
355 @objc public static func scan(_ url: String, completion: @escaping ([Detecte dIcon], [String:String]) -> Void) throws { 364 @objc
365 public static func scan(_ url: String, completion: @escaping ([DetectedIcon] , [String: String]) -> Void) throws {
356 guard let url = URL(string: url) else { throw IconError.invalidBaseURL } 366 guard let url = URL(string: url) else { throw IconError.invalidBaseURL }
357 scan(url, completion: completion) 367 scan(url, completion: completion)
358 } 368 }
359 369
360 /// Convenience overload for `downloadAll(url:completion:)` that takes a `St ring` 370 /// Convenience overload for `downloadAll(url:completion:)` that takes a `St ring`
361 /// instead of a `URL` as the URL parameter. Throws an error if the URL is n ot a valid URL. 371 /// instead of a `URL` as the URL parameter. Throws an error if the URL is n ot a valid URL.
362 /// 372 ///
363 /// - parameter url: The URL to scan for icons. 373 /// - parameter url: The URL to scan for icons.
364 /// - parameter completion: A closure to call when all download tasks have r esults available 374 /// - parameter completion: A closure to call when all download tasks have r esults available
365 /// (successful or otherwise). The closure will be c alled on the main queue. 375 /// (successful or otherwise). The closure will be c alled on the main queue.
366 /// - throws: An `IconError` if the scan or download failed for some reason. 376 /// - throws: An `IconError` if the scan or download failed for some reason.
367 @objc public static func downloadAll(_ url: String, completion: @escaping ([ ImageType]) -> Void) throws { 377 @objc
378 public static func downloadAll(_ url: String, completion: @escaping ([ImageT ype]) -> Void) throws {
368 guard let url = URL(string: url) else { throw IconError.invalidBaseURL } 379 guard let url = URL(string: url) else { throw IconError.invalidBaseURL }
369 downloadAll(url, completion: completion) 380 downloadAll(url, completion: completion)
370 } 381 }
371 382
372 /// Convenience overload for `downloadPreferred(url:width:height:completion: )` that takes a `String` 383 /// Convenience overload for `downloadPreferred(url:width:height:completion: )` that takes a `String`
373 /// instead of a `URL` as the URL parameter. Throws an error if the URL is n ot a valid URL. 384 /// instead of a `URL` as the URL parameter. Throws an error if the URL is n ot a valid URL.
374 /// 385 ///
375 /// - parameter url: The URL to scan for icons. 386 /// - parameter url: The URL to scan for icons.
376 /// - parameter width: The preferred icon width, in pixels, or `nil`. 387 /// - parameter width: The preferred icon width, in pixels, or `nil`.
377 /// - parameter height: The preferred icon height, in pixels, or `nil`. 388 /// - parameter height: The preferred icon height, in pixels, or `nil`.
378 /// - parameter completion: A closure to call when the download task has pro duced a result. The closure will 389 /// - parameter completion: A closure to call when the download task has pro duced a result. The closure will
379 /// be called on the main queue. 390 /// be called on the main queue.
380 /// - throws: An appropriate `IconError` if downloading failed for some reas on. 391 /// - throws: An appropriate `IconError` if downloading failed for some reas on.
381 @objc public static func downloadPreferred(_ url: String, 392 @objc
393 public static func downloadPreferred(_ url: String,
382 width: Int, 394 width: Int,
383 height: Int, 395 height: Int,
384 completion: @escaping (ImageType?) -> V oid) throws { 396 completion: @escaping (ImageType?) -> V oid) throws {
385 guard let url = URL(string: url) else { throw IconError.invalidBaseURL } 397 guard let url = URL(string: url) else { throw IconError.invalidBaseURL }
386 downloadPreferred(url, width: width, height: height, completion: complet ion) 398 downloadPreferred(url, width: width, height: height, completion: complet ion)
387 } 399 }
388 } 400 }
389 401
390 extension DetectedIcon { 402 extension DetectedIcon {
391 /// The area of a detected icon, if known. 403 /// The area of a detected icon, if known.
392 var area: Int? { 404 var area: Int? {
393 if let width = width, let height = height { 405 if let width = width, let height = height {
394 return width * height 406 return width * height
395 } 407 }
396 return nil 408 return nil
397 } 409 }
398 } 410 }
399
400
OLDNEW
« no previous file with comments | « FavIcon/DetectedIcon.swift ('k') | FavIcon/IconExtraction.swift » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld