我正在开发一个练习项目,其中 iOS 应用程序打印来自 jsonplaceholder.typicode.com 的 /posts 列表,当用户选择一个时,会加载详细的视图控制器,并显示有关该帖子的更多信息(作者和编号)评论)。
我为三个不同的端点发出了三个单独的 GET 请求,因为每个端点都需要不同的返回类型和不同的参数(或根本不需要)。
我想尽可能多地采用这三者之间的共同代码并将其放入一个新函数中来整理类,但我觉得我可以做更多的事情。
有没有办法让这些 Struct 的返回类型更加通用,并使用 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()
}
}
只需利用通用
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)
}
}