我正在尝试制作一个简单的应用程序,显示一个包含API值的列表。问题是,我不能像以前的项目那样使用同样的方式。我收到一条错误消息:
failure(Swift.DecodingError.typeMismatch(Swift.Array<Any>, Swift.DecodingError.Context(codingPath: [], debugDescription: "Expected to decode Array<Any> but found a dictionary instead.", underlyingError: nil)))
这是我的代码:
- ViewController
import UIKit
import Combine
class PokedexViewController: UIViewController {
var subscriptions = Set<AnyCancellable>()
private var pokedexListVM = PokedexListViewModel()
var collectionView: UICollectionView?
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemBackground
setupCollectionView()
}
private func setupCollectionView() {
let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
layout.sectionInset = UIEdgeInsets(top: 5, left: 5, bottom: 0, right: 5)
layout.itemSize = CGSize(width: view.bounds.size.width, height: 60)
collectionView = UICollectionView(frame: self.view.frame, collectionViewLayout: layout)
collectionView?.register(PokedexCollectionViewCell.self, forCellWithReuseIdentifier: PokedexCollectionViewCell.identifier)
collectionView?.delegate = self
collectionView?.dataSource = self
view.addSubview(collectionView ?? UICollectionView())
//self.view = view
setupViewModel()
}
private func setupViewModel() {
pokedexListVM.$pokemons
.receive(on: RunLoop.main)
.sink(receiveValue: { [weak self] _ in
self?.collectionView!.reloadData()
}).store(in: &subscriptions)
let stateHandler: (PokedexListViewModelState) -> Void = { state in
switch state {
case .loading:
print("loading")
case .finishLoading:
print("finish")
case .error:
print("error")
}
}
pokedexListVM.$state
.receive(on: RunLoop.main)
.sink(receiveValue: stateHandler)
.store(in: &subscriptions)
}
}
extension PokedexViewController: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return pokedexListVM.pokemons.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell(
withReuseIdentifier: PokedexCollectionViewCell.identifier,
for: indexPath
) as? PokedexCollectionViewCell else {
return UICollectionViewCell()
}
cell.configure(with: "(pokedexListVM.pokemons[indexPath.row].name)")
return cell
}
}
- ViewModel
import Foundation
import Combine
enum PokedexListViewModelState {
case loading
case finishLoading
case error
}
class PokedexListViewModel: ObservableObject {
@Published private(set) var pokemons: [Pokedex.Results] = []
@Published private(set) var state: PokedexListViewModelState = .loading
private var subscriptions = Set<AnyCancellable>()
private var url = "https://pokeapi.co/api/v2/pokemon?offset=0&limit=898"
//private let url = "https://jsonplaceholder.typicode.com/posts/"
init() {
loadPokemons()
}
private func loadPokemons() {
state = .loading
let valueHandler: ([Pokedex.Results]) -> Void = { [weak self] items in
self?.pokemons = items
}
let completionHandler: (Subscribers.Completion<Error>) -> Void = { [weak self] completion in
switch completion {
case .failure:
self?.state = .error
print(completion)
case .finished:
self?.state = .finishLoading
}
}
URLSession.shared.dataTaskPublisher(for: URL(string: url)!)
.map{ $0.data }
.decode(type: [Pokedex.Results].self, decoder: JSONDecoder())
.sink(receiveCompletion: completionHandler, receiveValue: valueHandler)
.store(in: &subscriptions)
}
}
- 型号
import Foundation
struct Pokedex: Codable {
struct Results: Codable {
let name: String
let url: String
}
var results: [Results]
var count: Int
var next: String
var previous: String?
}
我从该链接使用pokeAPIV2:https://pokeapi.co/api/v2/pokemon?offset=0&限制=898
{
"count": 1154,
"next": "https://pokeapi.co/api/v2/pokemon?offset=3&limit=3",
"previous": null,
"results": [
{
"name": "bulbasaur",
"url": "https://pokeapi.co/api/v2/pokemon/1/"
},
{
"name": "ivysaur",
"url": "https://pokeapi.co/api/v2/pokemon/2/"
},
{
"name": "venusaur",
"url": "https://pokeapi.co/api/v2/pokemon/3/"
}
]
}
我想使用";name";API的数据。我注意到的一件事是API从";{〃和前面的从"["开始。
您的模型应该是这样的:
import Foundation
struct Pokedex: Codable {
var results: [Results]
var count: Int
var next: String
var previous: String?
}
struct Results: Codable {
let name: String
let url: String
}
你的视图模型应该是这样的:
URLSession.shared.dataTaskPublisher(for: URL(string: url)!)
.map{ $0.data }
**.decode(type: Pokedex.self, decoder: JSONDecoder())**
.sink(receiveCompletion: completionHandler, receiveValue: valueHandler)
.store(in: &subscriptions)