Вопрос по работе с сетевыми запросами


#1

В проекте есть

  • протокол APIManager осуществляет запрос к серверу
  • класс APIAppManager тут создается запрос и парсинг ответа от сервера в модель
  • класс VC отдает параметры для запроса и принимает модель.

Сейчас модель принимает в Класс APIAppManager вопрос в том как передать эту ответственность в VC

APIManager
import Foundation

typealias JSONTask = URLSessionDataTask
typealias JSONCompletionHandler = (Data?, URLResponse?, Error?) -> Void

protocol JSONDecodable {
  init?(JSON: [String: AnyObject])
}

protocol FinalURLPoint {
  var baseURL: URL { get }
  var path: String { get }
  var request: URLRequest { get } 
}

enum APIResult<T> {
  case Success(T)
  case Failure(Error)
}

protocol APIManager {
  var sessionConfiguration: URLSessionConfiguration { get }
  var session: URLSession { get }
  
  func JSONTaskWith(request: URLRequest, completionHandler: @escaping JSONCompletionHandler) -> JSONTask
    
  func fetch<T: JSONDecodable>(request: URLRequest, parse: @escaping (Data) -> T?, completionHandler: @escaping (APIResult<T>) -> Void)
}

extension APIManager {
  func JSONTaskWith(request: URLRequest, completionHandler: @escaping JSONCompletionHandler) -> JSONTask {
      
      let dataTask = session.dataTask(with: request) { (data, response, error) in
                    
          guard let HTTPResponse = response as? HTTPURLResponse else {
              let userInfo = [
                NSLocalizedDescriptionKey: NSLocalizedString("Missing HTTP Response", comment: "")
                
              ]
              let error = NSError(domain: SWINetworkingErrorDomain, code: 100, userInfo: userInfo)
              
              completionHandler(nil, nil, error)
              return
          }
            
          if data == nil {
              if let error = error {
                  completionHandler(nil, HTTPResponse, error)
                  
              }
          } else {
              switch HTTPResponse.statusCode {
              case 200:
                  do {
                      completionHandler(data, HTTPResponse, nil)
                  } catch let error as NSError {
                      completionHandler(nil, HTTPResponse, error)
                  }
                  
              default:
                  print("We have got response status \(HTTPResponse.statusCode)")
              }
          }
      }
      return dataTask
  }
        
    func fetch<T>(request: URLRequest, parse: @escaping (Data) -> T?, completionHandler: @escaping (APIResult<T>) -> Void) {
        
        let dataTask = JSONTaskWith(request: request) { (json, response, error) in
                        
            DispatchQueue.main.async(execute: {
                guard let json = json else {
                    if let error = error {
                        completionHandler(.Failure(error))
                    }
                    return
                }
                
                if let value = parse(json) {
                    completionHandler(.Success(value))
                } else {
                    let error = NSError(domain: SWINetworkingErrorDomain, code: 200, userInfo: nil)
                    completionHandler(.Failure(error))
                }
                
            })
        }
        dataTask.resume()
    }
}
APIAppManager
import Foundation

enum TypeRequest: String {
    case GET = "GET"
    case POST = "POST"
    case PUT = "PUT"
    case PATCH = "PATCH"
    case DELETE = "DELETE"
}

enum Request: FinalURLPoint {
    case Deals(apiKey: String, id: String?)
    case Products(apiKey: String, id: String?)
    case Users(apiKey: String, id: String?)

    var baseURL: URL {
        return URL(string: "https://api.forecast.io")!
    }
    
    var baseURL_Plum: URL {
        return URL(string: "https://plum-app-backend.herokuapp.com")!
    }
        
    var path: String {
        switch self {
        case .Deals(let apiKey, let id):
            return "/api/v1/\(apiKey)/\(id ?? "")/"
            
        case .Products(let apiKey, let id):
            return "/api/v1/\(apiKey)/\(id ?? "")/"
            
        case .Users(let apiKey, let id):
            return "/api/v1/\(apiKey)/\(id ?? "")/"
        }
    }
    var request: URLRequest {
        let url = URL(string: path, relativeTo: baseURL_Plum)
        
        return URLRequest(url: url!)
    }
}

final class APIAppManager: APIManager {
    let token = "176c154b852f250ade2097958cce531ca78f077f"
    let sessionConfiguration: URLSessionConfiguration

    lazy var session: URLSession = {
        return URLSession(configuration: self.sessionConfiguration)
    } ()
    
    let apiKey: String
    
    init(sessionConfiguration: URLSessionConfiguration, apiKey: String) {
        self.sessionConfiguration = sessionConfiguration
        self.apiKey = apiKey
    }
    
    convenience init(apiKey: String) {
        self.init(sessionConfiguration: URLSessionConfiguration.default, apiKey: apiKey)
    }

    func fetchCurrentWeatherWith(typeRequest: TypeRequest,
                                 apiKey: String,
                                 id: String?,
                                 body: [String: Any]?,
                                 completionHandler: @escaping (APIResult<User>) -> Void) {

        var request = Request.Users(apiKey: apiKey, id: id).request
        request.httpMethod = typeRequest.rawValue
        request.addValue("application/json", forHTTPHeaderField: "content-type")
        request.addValue("Token \(token)", forHTTPHeaderField: "Authorization")
        
        if let body = body {
            let theJSONData = try? JSONSerialization.data(withJSONObject: body, options: [])
            request.httpBody = theJSONData
        }

        fetch(request: request, parse: { (data) -> User? in
            
            do {
                let user = try JSONDecoder().decode(User.self, from: data)
                return user
            } catch let error {
                print(error)
                return nil
            }
            

        }, completionHandler: completionHandler)
    }

}
VC

import UIKit

extension Encodable {

func jsonData() throws -> Data {
    let encoder = JSONEncoder()
    encoder.outputFormatting = .prettyPrinted
    encoder.dateEncodingStrategy = .iso8601
    return try encoder.encode(self)
}

}

class ViewController: UIViewController {

lazy var requestManager = APIAppManager(apiKey: "/us__ers/")
    
override func viewDidLoad() {
  super.viewDidLoad()

    requestManager.fetchCurrentWeatherWith(typeRequest: .GET, apiKey: "users",  id: "me", body: nil) { result in
                
        switch result {
        case .Success(let user):
            print("success")
            self.updateUIWith(user: user)
            
        case .Failure(let error as NSError):
            print("failure")
            let alertController = UIAlertController(title: "Unable to get data ", message: "\(error.localizedDescription)", preferredStyle: .alert)
            let okAction = UIAlertAction(title: "OK", style: .default, handler: nil)
            alertController.addAction(okAction)
            self.present(alertController, animated: true, completion: nil)
        default: break
        }
    }
}

func updateUIWith(user: User) {
    print("user in VC ", user)
}

@IBAction func patch(_ sender: Any) {

    let locationPatch = LocationPatch(id: "b209f6f0-9471-4b60-900b-9117bc94e67d")
    
    let size1 = LocationPatch(id: "845bc99c-c218-4bd2-b3d7-7d5eb7e5a746")
    let size2 = LocationPatch(id: "503e1059-7e0b-41bf-895d-3549012fbd1d")

    let userPatch = UserPatch(name: "Gabriella", location: locationPatch,  categories: nil, sizes: [size1, size2])
    
    let jsonData = try? userPatch.jsonData()
    
    let json = try? JSONSerialization.jsonObject(with: jsonData!, options: [])

    guard let params = json as? [String : Any] else { return }
    
    requestManager.fetchCurrentWeatherWith(typeRequest: .PATCH, apiKey: "users",  id: "19", body: params) { result in
        switch result {
        case .Success(let user):
            print("success")
            self.updateUIWith(user: user)

        case .Failure(let error as NSError):
            print("failure")
            let alertController = UIAlertController(title: "Unable to get data ", message: "\(error.localizedDescription)", preferredStyle: .alert)
            let okAction = UIAlertAction(title: "OK", style: .default, handler: nil)
            alertController.addAction(okAction)
            self.present(alertController, animated: true, completion: nil)
        default: break
        }
    }
}

}

Ссылка на проект

Буду рад любым советам.
Благодарю!