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

Powered by Google App Engine
This is Rietveld