功能回归太早

时间:2015-12-04 09:57:20

标签: swift completionhandler

有人可以解释为什么完成返回空数组吗?

功能:

import Foundation

class IMBD{
    func searchMovies(searchText:String, completion: (result: [Movies]) -> Void){

        var movies = [Movies]()

        let replacedMovieTitle = searchText.stringByReplacingOccurrencesOfString(" ", withString: "+")
        let URLString = "http://www.omdbapi.com/?s=\(replacedMovieTitle)&y=&r=json"

        let URL = NSURL(string: URLString)
        let session = NSURLSession.sharedSession()
            let task = session.dataTaskWithURL(URL!, completionHandler: {(data, response, error) -> Void in
            do{

                let jsonData = try NSJSONSerialization.JSONObjectWithData(data!, options: .MutableContainers) as! NSDictionary
                if let search = jsonData["Search"] as? [[String : AnyObject]]{
                    for hit in search{
                        guard let title = hit["Title"] as? String else{
                              print("returna title")
                            return

                        }
                        guard let year = hit["Year"] as? String else{
                            print("returna year")
                            return
                        }
                        guard let imbdID = hit["imdbID"] as? String else{
                            print("returna imbd")
                            return
                        }
                        guard let poster = hit["Poster"] as? String else{
                            print("returna poster")
                            return
                        }
                        let movie = Movies(title: title, released: year, poster: poster, imbdID: imbdID)
                           movies.append(movie)

                    }
                }

            }catch{
            }

        }).resume()
       completion(result: movies)

    }
}

电话:

imbd.searchMovies(searchtext!, completion: { (result) -> Void in
    self.movieList = result
})

1 个答案:

答案 0 :(得分:3)

你必须在dataTaskWithURL闭包内调用你的完成句柄,而不是之后。这是异步运行的,所以如果你在闭包之外调用completion,它将在异步请求有机会检索任何东西之前被调用。

另外,请记住,此闭包不会在主线程上运行,因此您可能还希望将其分配到主队列(从dataTaskWithURL内)。

例如:

class IMDB {
    func searchMovies(searchText:String, completion: (result: [Movie]?, error: NSError?) -> Void) -> NSURLSessionTask {
        var movies = [Movie]()

        let allowedCharacters = NSCharacterSet.alphanumericCharacterSet().mutableCopy() as! NSMutableCharacterSet
        allowedCharacters.addCharactersInString("-._* ")
        let replacedMovieTitle = searchText.stringByAddingPercentEncodingWithAllowedCharacters(allowedCharacters)!
            .stringByReplacingOccurrencesOfString(" ", withString: "+")
        let URLString = "http://www.omdbapi.com/?s=\(replacedMovieTitle)&y=&r=json"

        let URL = NSURL(string: URLString)
        let session = NSURLSession.sharedSession()
        let task = session.dataTaskWithURL(URL!) { data, response, error in
            guard error == nil && data != nil else {
                dispatch_async(dispatch_get_main_queue()) {
                    completion(result: nil, error: error)
                }
                return
            }

            do {
                let jsonData = try NSJSONSerialization.JSONObjectWithData(data!, options: .MutableContainers) as! NSDictionary
                if let search = jsonData["Search"] as? [[String : AnyObject]]{
                    for hit in search{
                        guard let title = hit["Title"] as? String else{
                            print("returna title")
                            continue
                        }
                        guard let year = hit["Year"] as? String else{
                            print("returna year")
                            continue
                        }
                        guard let imdbID = hit["imdbID"] as? String else{
                            print("returna imbd")
                            continue
                        }
                        guard let poster = hit["Poster"] as? String else{
                            print("returna poster")
                            continue
                        }
                        let movie = Movie(title: title, released: year, poster: poster, imdbID: imdbID)
                        movies.append(movie)
                    }
                }
                dispatch_async(dispatch_get_main_queue()) {
                    completion(result: movies, error: nil)
                }
            } catch let error as NSError {
                dispatch_async(dispatch_get_main_queue()) {
                    completion(result: nil, error: error)
                }
            }
        }
        task.resume()

        return task
    }
}

上述代码段中的其他一些更改包括:

  1. 如果出现基本网络错误(例如远程服务器关闭,无法访问互联网等),请添加guard

  2. 在检查guard值的nil语句中,而不是执行return(在这种情况下不会收集更多结果),您可能希望只是continue(即跳到下一条记录)。您通常会将guardreturn结合使用,但在这种情况下,continue可能更合适。

    坦率地说,您可能希望更进一步,考虑其中一些是否可选,而不是丢弃整个记录。值得注意的是,如果没有可用的海报,poster可能会让我感到nil。也许其他一些也应该是可选的(例如,如果电影还没有发布,可能没有发布日期吗?)。

  3. " imbd"的出现已被" imdb"。

  4. 取代
  5. Movies类已重命名为Movie(因为每个实例都是一部电影,而不是它们的集合)。

  6. 我更改了completion块以使[Movie]成为可选项并返回NSError。没有它,你就没有办法区分"找不到那个名字的标题"并且"呐喊,出了点问题"。

  7. 当我们在completion内调用dataTaskWithURL闭包时,将searchMovies调度completion调用回主队列非常有用,像上面一样。这是因为UI更新必须始终发生在主线程上,并且经常在您编写这样的例程时,您可以更新UI或模型中的结果。

    这样做并不总是必要的(您可能希望直接从后台线程调用completion并让调用searchMovies的例程手动将内容分配给主线程本身),但我经常发现让这个搜索方法只是将completion发送回主线程并完成它就很有用。

  8. 作为一种惯例,我总是在执行请求时返回NSURLSessionTask。您现在可能不需要它,但在将来某个日期,您可能希望能够取消正在进行的请求,并且对该任务的引用可能很有用。返回它并没有什么坏处,它可能很有用。

  9. 您可能应该百分之百转义您添加到网址的值。值得注意的是,&+字符的存在可能会有问题。请注意,在这种情况下,无论如何,看起来这个网站并没有适当地处理它,但是养成在查询中正确地百分比转义值的习惯是很好的。

    就个人而言,我在String扩展名中保留了这个逃避逻辑的百分比,但我想保持这个简单,所以我把它嵌入到这个方法中,但希望它能说明这个想法。