本文介绍了改进 Swift API GET 函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm working on a practice project where the iOS app prints a list of /posts from jsonplaceholder.typicode.com, and when the user selects one a detailed view controller is loaded and further information about that post is displayed (author and number of comments).

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

I've made three separate GET requests for the three different endpoints, as each of them require different return types and different parameters (or none at all).

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

I wanted to take as much code as possible that's in common between the three and put it in a new function to tidy up the class, but I feel as though I could do a lot more.

有没有办法让这些结构的返回类型更通用,用一个开关来确定将 json 响应映射到哪个?任何指导将不胜感激.

Is there a way to make the return type of these Structs more generic, with a Switch to determine which to map the json response to? Any guidance would be greatly appreciated.

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()
  }
}

推荐答案

只需利用通用的 Result 类型:

Just take advantage of the generic Result type:

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)
    }
}

这篇关于改进 Swift API GET 函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-14 07:28