NetworkFetcher.swift 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. //
  2. // NetworkFetcher.swift
  3. // Haneke
  4. //
  5. // Created by Hermes Pique on 9/12/14.
  6. // Copyright (c) 2014 Haneke. All rights reserved.
  7. //
  8. import UIKit
  9. extension HanekeGlobals {
  10. // It'd be better to define this in the NetworkFetcher class but Swift doesn't allow to declare an enum in a generic type
  11. public struct NetworkFetcher {
  12. public enum ErrorCode : Int {
  13. case invalidData = -400
  14. case missingData = -401
  15. case invalidStatusCode = -402
  16. }
  17. }
  18. }
  19. open class NetworkFetcher<T : DataConvertible> : Fetcher<T> {
  20. let URL : Foundation.URL
  21. public init(URL : Foundation.URL) {
  22. self.URL = URL
  23. let key = URL.absoluteString
  24. super.init(key: key)
  25. }
  26. open var session : URLSession { return URLSession.shared }
  27. var task : URLSessionDataTask? = nil
  28. var cancelled = false
  29. // MARK: Fetcher
  30. open override func fetch(failure fail: @escaping ((Error?) -> ()), success succeed: @escaping (T.Result) -> ()) {
  31. self.cancelled = false
  32. self.task = self.session.dataTask(with: self.URL) {[weak self] (data, response, error) -> Void in
  33. if let strongSelf = self {
  34. strongSelf.onReceive(data: data, response: response, error: error, failure: fail, success: succeed)
  35. }
  36. }
  37. self.task?.resume()
  38. }
  39. open override func cancelFetch() {
  40. self.task?.cancel()
  41. self.cancelled = true
  42. }
  43. // MARK: Private
  44. fileprivate func onReceive(data: Data!, response: URLResponse!, error: Error!, failure fail: @escaping ((Error?) -> ()), success succeed: @escaping (T.Result) -> ()) {
  45. if cancelled { return }
  46. let URL = self.URL
  47. if let error = error {
  48. if ((error as NSError).domain == NSURLErrorDomain && (error as NSError).code == NSURLErrorCancelled) { return }
  49. Log.debug(message: "Request \(URL.absoluteString) failed", error: error)
  50. DispatchQueue.main.async(execute: { fail(error) })
  51. return
  52. }
  53. if let httpResponse = response as? HTTPURLResponse , !httpResponse.hnk_isValidStatusCode() {
  54. let description = HTTPURLResponse.localizedString(forStatusCode: httpResponse.statusCode)
  55. self.failWithCode(.invalidStatusCode, localizedDescription: description, failure: fail)
  56. return
  57. }
  58. if !response.hnk_validateLength(ofData: data) {
  59. let localizedFormat = NSLocalizedString("Request expected %ld bytes and received %ld bytes", comment: "Error description")
  60. let description = String(format:localizedFormat, response.expectedContentLength, data.count)
  61. self.failWithCode(.missingData, localizedDescription: description, failure: fail)
  62. return
  63. }
  64. guard let value = T.convertFromData(data) else {
  65. let localizedFormat = NSLocalizedString("Failed to convert value from data at URL %@", comment: "Error description")
  66. let description = String(format:localizedFormat, URL.absoluteString)
  67. self.failWithCode(.invalidData, localizedDescription: description, failure: fail)
  68. return
  69. }
  70. DispatchQueue.main.async { succeed(value) }
  71. }
  72. fileprivate func failWithCode(_ code: HanekeGlobals.NetworkFetcher.ErrorCode, localizedDescription: String, failure fail: @escaping ((Error?) -> ())) {
  73. let error = errorWithCode(code.rawValue, description: localizedDescription)
  74. Log.debug(message: localizedDescription, error: error)
  75. DispatchQueue.main.async { fail(error) }
  76. }
  77. }