MusicBrainz API UITableView 中未显示数据

MusicBrainz API Data not showing in UITableView

只是想问一下我的 API 调用是否正确,因为我的 TableView 中没有显示数据(我是 UIKit 的新手)。我按照 this tutorial 进行了调整以适应我需要的 URL。

我认为问题在于我如何进行 API 调用,因为我的数组仍然是空的?我相信这是一个 REST API 所以这可能是问题所在吗?

我也在以编程方式执行此操作,因此主故事板是空白的。

模型数据:

import Foundation

class ArtistSearchModelData{
   // var artists = [Artists]()

    public func loadArtists(searchTerm: String, completionHandler: @escaping([Artists]) -> Void){
        guard let url = URL(string: "https://musicbrainz.org/ws/2/artist/?query=artist:\(searchTerm)") else {
           print("URL is invalid")
            return
        }
        
        let dataTask = URLSession.shared.dataTask(with: url){ data, response, error in
           if let data = data {
                if let artistResponse = try? JSONDecoder().decode(ArtistResponse.self, from: data){
                    completionHandler(artistResponse.artists ?? [])
                }
                return
            }
            
            guard let httpResponse = response as? HTTPURLResponse, (200...299).contains(httpResponse.statusCode) else{
                print("Error with response: \(response)")
                return
            }
            
            if let error = error {
                print("Error Thrown : \(error)")
                return
            }
        }

        dataTask.resume()
    }
}

视图控制器:

import UIKit

class ViewController: UIViewController {

    let tableView = UITableView()
    var safeArea: UILayoutGuide!
    var artists: [Artists]?
 
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        
        safeArea = view.layoutMarginsGuide

        setUpTable()
        setUpNavigation()
  
    }
    
    func setUpTable(){
        
        ArtistSearchModelData().loadArtists (searchTerm: "Fred"){ [weak self] (artists) in
              self?.artists = artists
              
              DispatchQueue.main.async{
                self?.tableView.reloadData()
              }
        }
        
        view.addSubview(tableView)
                
        //populate with data
        tableView.delegate = self
        tableView.dataSource = self
        tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
        
        //turn off autoresizing
        tableView.translatesAutoresizingMaskIntoConstraints = false
        
        //Layout Configs
        tableView.topAnchor.constraint(equalTo: safeArea.topAnchor).isActive = true
        tableView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
        tableView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
        tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
       
    }
    
    func setUpNavigation(){
        navigationItem.title = "Artists"
        self.navigationController?.navigationBar.barTintColor = .gray
        self.navigationController?.navigationBar.isTranslucent = false
        self.navigationController?.navigationBar.titleTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor.white]
    }

}

extension ViewController: UITableViewDataSource, UITableViewDelegate{
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        if let artistsCount = artists {
            return artistsCount.count
        }else{
            return 0
        }
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
        cell.textLabel?.text = artists![indexPath.row].id
        return cell
    }
    
    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return 100.0
    }
}

型号:

import Foundation

//http://musicbrainz.org/ws/2/artist/?query=artist:SEARCHTERM

struct ArtistResponse: Codable{
    let artists : [Artists]
}

struct Artists: Codable{
    let id: String
    let type: String
    let name: String
    let country: String
    let disambiguation: String
}

应用程序代表:

import UIKit

@main
class AppDelegate: UIResponder, UIApplicationDelegate {



    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.
        return true
    }

    // MARK: UISceneSession Lifecycle

    func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
        // Called when a new scene session is being created.
        // Use this method to select a configuration to create the new scene with.
        return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
    }

    func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
        // Called when the user discards a scene session.
        // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
        // Use this method to release any resources that were specific to the discarded scenes, as they will not return.
    }


}

场景代表:

import UIKit

class SceneDelegate: UIResponder, UIWindowSceneDelegate {

    var window: UIWindow?


    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
        // If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
        // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
        guard let _ = (scene as? UIWindowScene) else { return }
    }

    func sceneDidDisconnect(_ scene: UIScene) {
        // Called as the scene is being released by the system.
        // This occurs shortly after the scene enters the background, or when its session is discarded.
        // Release any resources associated with this scene that can be re-created the next time the scene connects.
        // The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead).
    }

    func sceneDidBecomeActive(_ scene: UIScene) {
        // Called when the scene has moved from an inactive state to an active state.
        // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
    }

    func sceneWillResignActive(_ scene: UIScene) {
        // Called when the scene will move from an active state to an inactive state.
        // This may occur due to temporary interruptions (ex. an incoming phone call).
    }

    func sceneWillEnterForeground(_ scene: UIScene) {
        // Called as the scene transitions from the background to the foreground.
        // Use this method to undo the changes made on entering the background.
    }

    func sceneDidEnterBackground(_ scene: UIScene) {
        // Called as the scene transitions from the foreground to the background.
        // Use this method to save data, release shared resources, and store enough scene-specific state information
        // to restore the scene back to its current state.
    }


}

由于某些值变为空且未处理,请更新可编码模型并检查其是否有效:

struct ArtistResponse: Codable{
    let artists : [Artist]
}

struct Artist: Codable{
    let id: String?
    let type: String?
    let name: String?
    let country: String?
    let disambiguation: String?
}