Уточнение функций Swift API GET - PullRequest
       41

Уточнение функций Swift API GET

0 голосов
/ 21 сентября 2018

Я работаю над учебным проектом, в котором приложение для iOS печатает список / публикаций с jsonplaceholder.typicode.com, и когда пользователь выбирает один, загружается контроллер подробного представления и отображается дополнительная информация об этом сообщении (автори количество комментариев).

Я сделал три отдельных запроса GET для трех разных конечных точек, поскольку для каждого из них требуются разные типы возврата и разные параметры (или их вообще нет).

Я хотел взять как можно больше кода, общего между тремя, и поместить его в новую функцию, чтобы привести в порядок класс, но я чувствую, что могу сделать гораздо больше.

Есть ли способ сделать тип возвращаемых данных этих структур более универсальным, с помощью переключателя, чтобы определить, к какому из них будет привязан ответ json?Любое руководство будет с благодарностью.

import UIKit

struct Post: Codable {
    let userId: Int
    let id: Int
    let title: String
    let body: String
}

struct Author: Codable {
    let name: String
}

struct Comment: Codable {
    let postId: Int
    let id: Int
    let name: String
    let email: String
    let body: String
}

enum Result<Value> {
    case success(Value)
    case failure(Error)
}

class APIManager {

static let sharedInstance = APIManager()

func getUrl(for path: String) -> URL {
    var urlComponents = URLComponents()
    urlComponents.scheme = "https"
    urlComponents.host = "jsonplaceholder.typicode.com"
    urlComponents.path = path

    guard let url = urlComponents.url else { fatalError("Could not create URL from components") }

    return url
}

func getPosts(completion: ((Result<[Post]>) -> Void)?) {
    let url = getUrl(for: "/posts")

    var request = URLRequest(url: url)
    request.httpMethod = "GET"

    let config = URLSessionConfiguration.default
    let session = URLSession(configuration: config)
    let task = session.dataTask(with: request) { (responseData, response, responseError) in
        DispatchQueue.main.async {
            if let error = responseError {
                completion?(.failure(error))
            } else if let jsonData = responseData {
                let decoder = JSONDecoder()

                do {
                    let posts = try decoder.decode([Post].self, from: jsonData)
                    completion?(.success(posts))
                } catch {
                    completion?(.failure(error))
                }
            } else {
                let error = NSError(domain: "", code: 0, userInfo: [NSLocalizedDescriptionKey : "Data was not retrieved from request"]) as Error
                completion?(.failure(error))
            }
        }
    }

    task.resume()
}

func getAuthor(for userId: Int, completion: ((Result<String>) -> Void)?) {
    let url = getUrl(for: "/users/\(userId)")

    var request = URLRequest(url: url)
    request.httpMethod = "GET"

    let config = URLSessionConfiguration.default
    let session = URLSession(configuration: config)
    let task = session.dataTask(with: request) { (responseData, response, responseError) in
        DispatchQueue.main.async {
            if let error = responseError {
                completion?(.failure(error))
            } else if let jsonData = responseData {
                let decoder = JSONDecoder()

                do {
                    let author = try decoder.decode(Author.self, from: jsonData)
                    completion?(.success(author.name))
                } catch {
                    completion?(.failure(error))
                }
            } else {
                let error = NSError(domain: "", code: 0, userInfo: [NSLocalizedDescriptionKey : "Data was not retrieved from request"]) as Error
                completion?(.failure(error))
            }
        }
    }

    task.resume()
}

func getComments(for postId: Int, completion: ((Result<[Comment]>) -> Void)?) {
    let url = getUrl(for: "/posts/\(postId)/comments")

    var request = URLRequest(url: url)
    request.httpMethod = "GET"

    let config = URLSessionConfiguration.default
    let session = URLSession(configuration: config)
    let task = session.dataTask(with: request) { (responseData, response, responseError) in
        DispatchQueue.main.async {
            if let error = responseError {
                completion?(.failure(error))
            } else if let jsonData = responseData {
                let decoder = JSONDecoder()

                do {
                    let comments = try decoder.decode([Comment].self, from: jsonData)
                    completion?(.success(comments))
                } catch {
                    completion?(.failure(error))
                }
            } else {
                let error = NSError(domain: "", code: 0, userInfo: [NSLocalizedDescriptionKey : "Data was not retrieved from request"]) as Error
                completion?(.failure(error))
            }
        }
    }

    task.resume()
  }
}

1 Ответ

0 голосов
/ 21 сентября 2018

Просто воспользуйтесь универсальным Result типом:

class APIManager {

    static let sharedInstance = APIManager()

    private func getUrl(for path: String) -> URL {
        var urlComponents = URLComponents()
        urlComponents.scheme = "https"
        urlComponents.host = "jsonplaceholder.typicode.com"
        urlComponents.path = path

        guard let url = urlComponents.url else { fatalError("Could not create URL from components") }

        return url
    }

    private func postsURL() -> URL { return getUrl(for: "/posts") }
    private func usersURL(for userId : Int) -> URL { return getUrl(for: "/users/\(userId)") }
    private func commentsURL(for postId : Int) -> URL { return getUrl(for: "/posts/\(postId)/comments") }

    func getPosts(completion: @escaping (Result<[Post]>) -> Void) {
        getInfo(for: postsURL(), completion: completion)
    }

    func getAuthor(for userId: Int, completion: @escaping (Result<Author>) -> Void) {
        getInfo(for: usersURL(for: userId), completion: completion)
    }

    func getComments(for postId: Int, completion: @escaping (Result<[Comment]>) -> Void) {
        getInfo(for: commentsURL(for: postId), completion: completion)
    }

    private func getInfo<T: Decodable>(for url : URL, completion: @escaping (Result<T>) -> Void) {
        let task = URLSession.shared.dataTask(with: url) { (data, _, error) in
            DispatchQueue.main.async {
                if let error = error {
                    completion(.failure(error))
                } else {
                    let decoder = JSONDecoder()
                    do {
                        let comments = try decoder.decode(T.self, from: data!)
                        completion(.success(comments))
                    } catch {
                        completion(.failure(error))
                    }
                }
            }
        }
        task.resume()
    }
}

и используйте его

let manager = APIManager.sharedInstance
manager.getAuthor(for: 1) { result in
    switch result {
    case .success(let author) : print(author.name)
    case .failure(let error) : print(error)
    }
}
...