【问题标题】:Refining Swift API GET Functions精炼 Swift API GET 函数
【发布时间】:2018-09-21 15:43:20
【问题描述】:

我正在做一个练习项目,iOS 应用程序打印来自 jsonplaceholder.typicode.com 的 /posts 列表,当用户选择一个时,会加载详细的视图控制器并显示有关该帖子的更多信息(作者和 cmets 的数量)。

我已经为三个不同的端点发出了三个单独的 GET 请求,因为它们中的每一个都需要不同的返回类型和不同的参数(或者根本不需要)。

我想尽可能多地采用这三者之间的共同代码并将其放入一个新函数中来整理类,但我觉得我可以做更多的事情。

有没有办法让这些结构的返回类型更通用,用一个 Switch 来确定将 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()
  }
}

【问题讨论】:

  • 您可以只创建一个函数来获取数据和另一个函数来解析 switch case。我认为这会对你有所帮助。
  • @EssamMohamed 谢谢!我会用路径值确定哪个 Switch 案例,还是有更好的方法来使用枚举?
  • 您可以使用您的案例创建枚举并将枚举值传递给解析函数。

标签: ios swift api struct codable


【解决方案1】:

只需利用通用的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)
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-02-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-02-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多