ios - 错误 - 使用 Alamofire 和 AlamofireObjectMapper 的请求函数(swift - iOS)

标签 ios json swift runtime-error alamofire

首先,我是初学者,我正在尝试构建一个在 OMDB API 上搜索电影并返回电影列表(按标题搜索时)并在按 imdbID 搜索时返回特定电影的应用程序。我必须对 api 提出两种类型的请求,因为按 id 搜索的结果具有与按标题搜索相同的属性,但具有更多详细信息(需要它来显示包含从此结果列表中选择的电影的 View ) .

因此,我(在这里)被推荐使用 AlamofireObjectMapper/ObjectMapper 来做得更好。我做了这样的映射:

import Foundation
import AlamofireObjectMapper

class SearchResponse: Mappable {
    var isSuccess  : String?
    var searchArray: [Movie]?
    var searchCount: String?

    required init?(map: Map) {
    }

    func mapping(map: Map) {
        isSuccess   <- map["Response"]
        searchArray <- map["Search"]
        searchCount <- map["totalResults"]
    }
}

class Movie: Mappable {

    var posterURL  : String?
    var title      : String?
    var runtime    : String?
    var director   : String?
    var actors     : String?
    var genre      : String?
    var plot       : String?
    var production : String?
    var year       : String?
    var imdbID     : String?
    var imdbRating : String?

    required init?(map: Map) {

    }

    func mapping(map: Map) {
        posterURL  <- map["Poster"]
        title      <- map["Title"]
        runtime    <- map["Runtime"]
        director   <- map["Director"]
        actors     <- map["Actors"]
        genre      <- map["Genre"]
        plot       <- map["Plot"]
        production <- map["Production"]
        year       <- map["Year"]
        imdbID     <- map["imdbID"]
        imdbRating <- map["imdbRating"]
   }
}

我想做这样的事情:

//Get movie by title - the user will enter the title on a searchbar

let url = "https:www.omdbapi.com/?s=\(imdbTitle)"

func getMoviesByTitle (imdbTitle: String) {
    /* The Alamofire function using ObjectMapper goes here */
    switch
    case .success():
        /*Something*/
        completionHandler(???)
    case .failure():
       /*Something*/
        completionHandler(???)
}


//Get movie by ID

let url = "https:www.omdbapi.com/?i=\(imdbID)"

func getMovieByID(imdbID: String) {
    /* The Alamofire function using ObjectMapper goes here */
    if let response {
       completioHandler(???)
    } /* Something like this? */
}

我需要一些指导。当我按标题搜索电影时,它会返回一个带有响应、搜索(电影的“数组”)和总结果的 JSON。在这种情况下,我的 Movie 类只有四个映射属性(海报、标题、年份、imdbID)。

  1. 这个映射正确吗?
  2. 我如何针对每个案例提出这些请求?我的意思是,我应该返回什么? (因为我的 getMovie 函数需要一个 completionHandler,对吧?)

编辑

所以,我已经在我的 SearchTableViewController 上试过了:

import UIKit
import Alamofire
import AlamofireObjectMapper
import ObjectMapper
import Kingfisher



class SearchTableViewController: UITableViewController, UISearchResultsUpdating {


@IBOutlet var searchTableView: UITableView!

@IBAction func showResults(_ sender: Any) {
    let searchController = UISearchController(searchResultsController: nil)
    self.present(searchController, animated: true, completion: nil)
    searchController.searchBar.barTintColor = self.searchTableView.backgroundColor!
    searchController.searchResultsUpdater = self

}


var movies = [Movie]()



override func viewDidLoad() {
    super.viewDidLoad()

    searchTableView.dataSource = self
    searchTableView.delegate = self

}

func updateSearchResults(for searchController: UISearchController) {

    if let searchText = searchController.searchBar.text {

        if searchText == "" {
            return
        }

        else {
            let movieSearched: String = searchText.replacingOccurrences(of: " ", with: "_")


                // MARK: Alamofire Get by Title

                let URL = "https://www.omdbapi.com/?s=\(movieSearched)&type=movie"


                Alamofire.request(URL).responseObject{ (response: DataResponse<SearchResponse>) in

                    print("response is: \(response)")

                    switch response.result {

                    case .success(let value):
                        let searchResponse = value
                        self.movies = (searchResponse.searchArray)!
                        self.searchTableView.reloadData()

                    case .failure(let error):
                        let alert = UIAlertController(title: "Error", message: "Error 4xx / 5xx: \(error)", preferredStyle: UIAlertControllerStyle.alert)
                        alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: nil))
                        self.present(alert, animated: true, completion: nil)
                    }

                }



                DispatchQueue.main.async {
                    let spinnerActivity = MBProgressHUD.showAdded(to: self.view, animated: true)
                    spinnerActivity.label.text = "Loading";
                    spinnerActivity.detailsLabel.text = "Searching movie..."
                    spinnerActivity.isUserInteractionEnabled = false;
                }




                DispatchQueue.main.async {
                    MBProgressHUD.hide(for: self.view, animated: true)
                }



        }

    }
}


// MARK: - Table view data source

override func numberOfSections(in tableView: UITableView) -> Int {
    // #warning Incomplete implementation, return the number of sections
    return 1
}

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    // #warning Incomplete implementation, return the number of rows
    return movies.count
}


override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "SearchCellIdentifier", for: indexPath) as! SearchTableViewCell

    let movie = movies[indexPath.row]

    let imgStg: String = movie.posterURL!
    let imgURL: URL? = URL(string: imgStg)
    let imgSrc = ImageResource(downloadURL: imgURL!, cacheKey: imgStg)

    cell.titleLabel.text = movie.title
    cell.yearLabel.text = movie.year


    cell.posterImageView.layer.cornerRadius = cell.posterImageView.frame.size.width/2
    cell.posterImageView.clipsToBounds = true

    //image cache with KingFisher
    cell.posterImageView.kf.setImage(with: imgSrc)



    return cell
}


/*
// Override to support conditional editing of the table view.
override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
    // Return false if you do not want the specified item to be editable.
    return true
}
*/

/*
// Override to support editing the table view.
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
    if editingStyle == .delete {
        // Delete the row from the data source
        tableView.deleteRows(at: [indexPath], with: .fade)
    } else if editingStyle == .insert {
        // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
    }    
}
*/

/*
// Override to support rearranging the table view.
override func tableView(_ tableView: UITableView, moveRowAt fromIndexPath: IndexPath, to: IndexPath) {

}
*/

/*
// Override to support conditional rearranging of the table view.
override func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool {
    // Return false if you do not want the item to be re-orderable.
    return true
}
*/

/*
// MARK: - Navigation

// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    // Get the new view controller using segue.destinationViewController.
    // Pass the selected object to the new view controller.
}
*/

}

搜索一直有效,直到我键入 4 个字符...直到键入第 3 个字符,表格 View 会实时显示结果,但当我键入第 4 个字符时,应用程序崩溃了。错误是这样的:

error with self.movies = (searchResponse.searchArray)! line

最佳答案

您遇到的错误是由于强制解包(!)。 searchResponse.searchArray 可能在您键入第 4 个字符后返回空值。

您的 movies var 应该是可选的 - 这意味着它可能是 nil

var movies:[Movie]?

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    if let safeMovies = movies {
        return safeMovies.count
    } else {
        return 0 //no movies were returned.  you could eventually show an error here
    }
}

在您的 Alamofire responseObject 闭包中(仅显示更新的部分)

case .success(let value):
    let searchResponse = value
    self.movies = searchResponse.searchArray
    self.searchTableView.reloadData()

关于您的 updateSearchResults 方法的一些额外想法。您可以使用 guard 来展开和检查 searchText,这样您就不需要大量的 else { } 语句。您还应该在 Alamofire 完成后删除您的 MBProgressHUD 叠加层,否则您会同时显示和隐藏。

func updateSearchResults(for searchController: UISearchController) {

    guard let searchText = searchController.searchBar.text, searchText != "" else {
        return
    }

    let movieSearched: String = searchText.replacingOccurrences(of: " ", with: "_")
    // MARK: Alamofire Get by Title
    let URL = "https://www.omdbapi.com/?s=\(movieSearched)&type=movie"
    Alamofire.request(URL).responseObject{ (response: DataResponse<SearchResponse>) in
        print("response is: \(response)")
        DispatchQueue.main.async {
            MBProgressHUD.hide(for: self.view, animated: true)
        }
        switch response.result {
        case .success(let value):
            let searchResponse = value
            self.movies = (searchResponse.searchArray)!
            self.searchTableView.reloadData()

        case .failure(let error):
            let alert = UIAlertController(title: "Error", message: "Error 4xx / 5xx: \(error)", preferredStyle: UIAlertControllerStyle.alert)
            alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: nil))
            self.present(alert, animated: true, completion: nil)
        }
    }

    DispatchQueue.main.async {
        let spinnerActivity = MBProgressHUD.showAdded(to: self.view, animated: true)
        spinnerActivity.label.text = "Loading";
        spinnerActivity.detailsLabel.text = "Searching movie..."
        spinnerActivity.isUserInteractionEnabled = false;
    }
}

关于ios - 错误 - 使用 Alamofire 和 AlamofireObjectMapper 的请求函数(swift - iOS),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43623796/

相关文章:

ios - 我在将 uiview 当前位置设置为 uiview 默认位置时遇到问题?

swift - Obj C 桥接头似乎并不重要?

swift - 错误域=NSOSStatusErrorDomain 代码=-12780\"(null)\"

objective-c - 通过指针枚举 NSString 字符

objective-c - 对类和对象 iVar 感到困惑

javascript - 如何使用 Ramda 更新 JSON 中任何级别的键值?

php - 使用 PHP 从 MySQL 结果输出复杂的 JSON

ios - 我可以在多个 tableView 中使用单个原型(prototype)单元格吗?

ios - 相对 NSDate 与结果 TheDayBeforeYesterday,Yesterady,Today,Tomorrow

android - 使用 int 变量获取 JSONObject 中的第一个对象