错误-使用Alamofire和AlamofireObjectMapper的请求功能(Swift-iOS) [英] Error - Resquest function with Alamofire and AlamofireObjectMapper (swift - iOS)

查看:78
本文介绍了错误-使用Alamofire和AlamofireObjectMapper的请求功能(Swift-iOS)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

首先,我是一个初学者,我正在尝试构建一个应用程序,该应用程序可使用OMDB API搜索电影并返回电影列表(按标题搜索时),并在通过imdbID搜索时返回特定电影。我必须对api进行两种类型的请求,因为按ID搜索的结果与按标题搜索具有相同的属性,但有更多详细信息(需要显示从此结果列表中选择的电影的视图)

First of all, I'm a beginner and I'm trying to build an app that search a movie on OMDB API and return a list of movie (when searched by title) and return an specific movie when searched by imdbID. I have to make two types of request for the api, because the result for search by id have the same atributes that the search by title have but with more details (need this to show a view with a selected movie from this list of results).

因此,我(在这里)建议我使用AlamofireObjectMapper / ObjectMapper来做得更好。我确实是这样映射的:

So, it was recommended to me (here) to use AlamofireObjectMapper/ObjectMapper to do it better. I did mapped like this:

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? */
}

我需要一些指导。当我按标题搜索电影时,它返回一个带有Response,Search(电影的数组)和totalResults的JSON。在这种情况下,我的Movie类只有四个映射属性(Poster,Title,Year,imdbID)。

I need some guide. When I search a movie by Title it returns an JSON with Response, Search("array" of movies) and totalResults. In this case, my Movie class only have four of those mapping attributes (Poster, Title, Year, imdbID).


  1. 此映射对吗?

  2. 如何针对每种情况提出这些请求?我的意思是,我应该返回什么? (因为我的getMovie函数需要一个completionHandler,对吗?)



EDIT



因此,我在SearchTableViewController上尝试过此操作:

EDIT

So, I've tried this on my 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个字符,表格视图才会实时显示结果,但是当我键入4个字符时,应用程序将崩溃。错误是这样的:

The search is working, until I type 4 characters... Until the 3th character typed, the table view show in real time the results, but when I type the 4th, the app crashs. The error is this:

推荐答案

您收到的错误是由于强制展开(!)造成的。键入第四个字符后, searchResponse.searchArray 可能返回空。

The error you are getting is due to force unwrapping (the !). searchResponse.searchArray is probably returning empty once you type the 4th character.

您的电影 var应该是可选的-意味着它可能为 nil

Your movies var should be optional - meaning it could potentially be 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中关闭(仅显示更新的片段)

In your Alamofire responseObject closure (just showing the updated piece)

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






关于 updateSearchResults 方法的一些其他想法。您可以使用 guard 来解开并检查searchText,这样就不需要庞大的else {}语句。一旦Alamofire完成,您还应该删除MBProgressHUD叠加层,否则将同时显示然后隐藏。


A few additional thoughts on your updateSearchResults method. You can use guard to unwrap and check searchText, so that you don't need a huge else { } statement. You should also remove your MBProgressHUD overlay once Alamofire completes, otherwise you are showing and then hiding at the same time.

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;
    }
}

这篇关于错误-使用Alamofire和AlamofireObjectMapper的请求功能(Swift-iOS)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆