Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Rolling your own Network Stack

Rolling your own Network Stack

using the power of URLSession, Codable, protocols, and generics in Swift

glenna

May 27, 2019
Tweet

More Decks by glenna

Other Decks in Programming

Transcript

  1. Rolling your own Network Stack using the power of URLSession,

    Codable, protocols, and generics in Swift Glenna Buford @glennersboofy
  2. Agenda • But why? • Brief showing of my naïve

    approach • Introduce: • Well, this would spoil it all :P 5 Rolling your own network stack by @glennersboofy
  3. Agenda • But why? • Brief showing of my naïve

    approach 13 Rolling your own network stack by @glennersboofy
  4. Setup let bookListsURL: String = "https://api.nytimes.com/svc/books/v3/lists/names?api-key=\(APIKeys.nyt)" let url: URL =

    URL(string: bookListsURL)! var urlRequest: URLRequest = URLRequest(url: url) urlRequest.httpMethod = HTTPMethod.get.rawValue 16 Rolling your own network stack by @glennersboofy
  5. Setup let dataTask = URLSession.shared.dataTask(with: urlRequest) { (data, response, error)

    in guard error == nil else { //Handle error //Show user something return } guard let data: Data = data else { return } //do stuff with the new data } dataTask.resume() 19 Rolling your own network stack by @glennersboofy
  6. let bookListsURL: String = "https://api.nytimes.com/svc/books/v3/lists/names?api-key=\(APIKeys.nyt)" let url: URL = URL(string:

    bookListsURL)! var urlRequest: URLRequest = URLRequest(url: url) urlRequest.httpMethod = HTTPMethod.get.rawValue let dataTask = URLSession.shared.dataTask(with: urlRequest) { (data, response, error) in guard error == nil else { //Handle error //Show user something return } guard let data: Data = data else { return } //do stuff with the new data } dataTask.resume() 20 Rolling your own network stack by @glennersboofy
  7. Agenda • But why? • Brief showing of my naïve

    approach • Introduce: 22 Rolling your own network stack by @glennersboofy
  8. Agenda • But why? • Brief showing of my naïve

    approach • Introduce: • APIRouter 23 Rolling your own network stack by @glennersboofy
  9. APIRouter protocol APIRouter { var urlRequest: URLRequest { get }

    } 24 Rolling your own network stack by @glennersboofy
  10. enum NYTAPIRouter: APIRouter { case bestSellerCategories var urlRequest: URLRequest {

    let urlString: String = "\(host)\(route)?api-key=\(APIKeys.nyt)" let url: URL = URL(string: urlString)! var urlRequest: URLRequest = URLRequest(url: url) urlRequest.httpMethod = httpMethod.rawValue urlRequest.httpBody = body urlRequest.addValue("application/json", forHTTPHeaderField: "Accept") return urlRequest } ... } 25 Rolling your own network stack by @glennersboofy
  11. enum NYTAPIRouter: APIRouter { ... private var route: String {

    switch self { case .bestSellerCategories: return "lists/names" } } private var httpMethod: HTTPMethod { switch self { case .bestSellerCategories: return .get } } ... } 26 Rolling your own network stack by @glennersboofy
  12. let urlRequest: URLRequest = NYTAPIRouter.bestSellerCategories.urlRequest let dataTask = URLSession.shared.dataTask(with: urlRequest)

    { (data, response, error) in guard error == nil else { // Handle error return } guard let data: Data = data else { return } // Do some stuff with that data } dataTask.resume() 29 Rolling your own network stack by @glennersboofy
  13. Agenda • But why? • Brief showing of my naïve

    approach • Introduce: • APIRouter 32 Rolling your own network stack by @glennersboofy
  14. Agenda • But why? • Brief showing of my naïve

    approach • Introduce: • APIRouter • Rudimentary NetworkService and NetworkOperation 33 Rolling your own network stack by @glennersboofy
  15. NetworkService struct NetworkService { static let shared: NetworkService = NetworkService()

    let operationQueue: OperationQueue = OperationQueue() func add(_ operation: Operation & NetworkOperationProtocol) { operation.operationState = .ready operationQueue.addOperation(operation) } } 34 Rolling your own network stack by @glennersboofy
  16. class NetworkOperation: Operation, NetworkOperationProtocol { private let request: URLRequest private

    let completion: ((Result<Data?, Error>) -> Void)? init(with apiRouter: APIRouter, completion: ((Result<Data?, Error>) -> Void)? = nil) { self.request = apiRouter.urlRequest self.completion = completion operationState = .none } private func performRequest() { ... } // MARK: Operation overrides ... // MARK: NetworkOperationProtocol ... } 36 Rolling your own network stack by @glennersboofy
  17. private func performRequest() { let task = URLSession.shared.dataTask(with: request) {

    [weak self] (data, response, error) in guard let self = self else { return } if let error: Error = error { self.completion?(Result.failure(error)) } else { self.completion?(Result.success(data)) } } task.resume() } 37 Rolling your own network stack by @glennersboofy
  18. ⚠ protocol NetworkOperationProtocol: class { var operationState: OperationState { get

    set } } enum OperationState: String { case none case ready = "isReady" case executing = "isExecuting" case finished = "isFinished" } 38 Rolling your own network stack by @glennersboofy
  19. let bestSellersCategories: NYTAPIRouter = NYTAPIRouter.bestSellerCategories let op: NetworkOperation = NetworkOperation(with:

    bestSellersCategories) { result in switch result { case .success(let data): if let data: Data = data { // Do some stuff with that data } case .failure(let error): // oh no. } } NetworkService.shared.add(op) 39 Rolling your own network stack by @glennersboofy
  20. Agenda • But why? • Brief showing of my naïve

    approach • Introduce: • APIRouter • Rudimentary NetworkService and NetworkOperation 42 Rolling your own network stack by @glennersboofy
  21. Agenda • But why? • Brief showing of my naïve

    approach • Introduce: • APIRouter • Rudimentary NetworkService and NetworkOperation • Generic-ify everything! 43 Rolling your own network stack by @glennersboofy
  22. class NetworkOperation<T>: Operation, NetworkOperationProtocol where T: Decodable { ... private

    let completion: ((Result<T?, Error>) -> Void)? ... private func performRequest() { ... } } 45 Rolling your own network stack by @glennersboofy
  23. private func performRequest() { let task = URLSession.shared.dataTask(with: request) {

    [weak self] (data, response, error) in guard let self = self else { return } if let error: Error = error { self.completion?(Result.failure(error)) } else { guard let data: Data = data else { self.completion?(Result.success(nil)) return } do { let decoded: T = try JSONDecoder().decode(T.self, from: data) self.completion?(Result.success(decoded)) } catch { self.completion?(Result.failure(error)) } } } task.resume() }
  24. let bestSellersCategories: NYTAPIRouter = NYTAPIRouter.bestSellerCategories let op = NetworkOperation<[BestSellersCategory]>(with: bestSellerCategories)

    { result in switch result { case .success(let bestSellerList): // Woo! we have some usable data now case .failure(let error): // wah wah } } NetworkService.shared.add(op) 47 Rolling your own network stack by @glennersboofy
  25. Agenda • But why? • Brief showing of my naïve

    approach • Introduce: • APIRouter • Rudimentary NetworkService and NetworkOperation • Generic-ify everything! 49 Rolling your own network stack by @glennersboofy
  26. Levelling up • request adapter • retrier • use a

    custom URLSession in your NetworkService • add custom decode options • multiple APIRouters 51 Rolling your own network stack by @glennersboofy