Swift-如何从REST API解码json

问题描述

我正试图从REST API快速获取GET。当我使用print语句(print(clubs))时,可以看到正确格式的预期响应。但是在VC中却给了我一个空数组。

这是与API对话的代码

extension ClubAPI {

    public enum ClubError: Error {
        case unkNown(message: String)
    }

    func getClubs(completion: @escaping ((Result<[Club],ClubError>) -> Void)) {
        let baseURL = self.configuration.baseURL
        let endPoint = baseURL.appendingPathComponent("/club")
        print(endPoint)
        API.shared.httpClient.get(endPoint) { (result) in
            switch result {
            case .success(let response):
                let clubs = (try? JSONDecoder().decode([Club].self,from: response.data)) ?? []
                print(clubs)
                completion(.success(clubs))
            case .failure(let error):
                completion(.failure(.unkNown(message: error.localizedDescription)))
            }
        }
    }

}

这是VC中的代码

private class Clubviewmodel {
    @Published private(set) var clubs = [Club]()
    @Published private(set) var error: String?

    func refresh() {
        ClubAPI.shared.getClubs { (result) in
            switch result {
            case .success(let club):
                print("We have \(club.count)")
                self.clubs = club
                print("we have \(club.count)")
            case .failure(let error):
                self.error = error.localizedDescription
            }
        }
    }
}

这是视图控制器代码(在扩展名之前)

class ClubViewController: UIViewController {
    private var clubs = [Club]()
    private var subscriptions = Set<AnyCancellable>()
    private lazy var dataSource = makeDataSource()

    enum Section {
        case main
    }
    private var errorMessage: String? {
        didSet {

        }
    }
    private let viewmodel = Clubviewmodel()
    @IBOutlet private weak var tableView: UITableView!

    override func viewDidLoad() {
        super.viewDidLoad()

        self.subscriptions = [
            self.viewmodel.$clubs.assign(to: \.clubs,on: self),self.viewmodel.$error.assign(to: \.errorMessage,on: self)
        ]

        applySnapshot(animatingDifferences: false)
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        self.viewmodel.refresh()
    }



}




extension ClubViewController {
    typealias DataSource = UITableViewDiffableDataSource<Section,Club>
    typealias Snapshot = NSDiffableDataSourceSnapshot<Section,Club>
    func applySnapshot(animatingDifferences: Bool = true) {
        // Create a snapshot object.
        var snapshot = Snapshot()
        // Add the section
        snapshot.appendSections([.main])
        // Add the player array
        snapshot.appendItems(clubs)
        print(clubs.count)

        // Tell the dataSource about the latest snapshot so it can update and animate.
        dataSource.apply(snapshot,animatingDifferences: animatingDifferences)
    }

    func makeDataSource() -> DataSource {
        let dataSource = DataSource(tableView: tableView) { (tableView,indexPath,club) -> UITableViewCell? in
            let cell = tableView.dequeueReusableCell(withIdentifier: "Clubcell",for: indexPath)
            let club = self.clubs[indexPath.row]
            print("The name is \(club.name)")
            cell.textLabel?.text = club.name
            return cell
        }
        return dataSource
    }
}

解决方法

获取clubs后,您需要将新快照应用于表视图。您当前的订阅者只需将一个值分配给clubs,仅此而已。

您可以使用sink订阅者来分配新的clubs值,然后调用applySnapshot。您需要确保这在主队列上发生,因此您可以使用receive(on:)

self.subscriptions = [
    self.viewModel.$clubs.receive(on: RunLoop.main).sink { clubs in
        self.clubs = clubs
        self.applySnapshot()
    },self.viewModel.$error.assign(to: \.errorMessage,on: self)
]