Swift tvOS 中的嵌套 dataTaskWithRequest

Nested dataTaskWithRequest in Swift tvOS

我是一名 C# 开发人员,正在转换为 Swift tvO,并且刚刚开始学习。我取得了一些进展,但不确定如何处理对 json 的嵌套调用。来源来自不同的提供商,所以我不能只合并查询。

如何等待内部请求完成以便 TVSeries 具有 poster_path?有没有更好的方法将节目添加到 collection,然后在另一个线程中处理海报路径加载,这样就不会延迟 UI 体验?

    func downloadTVData() {
    let url_BTV = NSURL(string: BTV_URL_BASE)!
    let request_BTV = NSURLRequest(URL: url_BTV)
    let session_BTV = NSURLSession.sharedSession()

    //get series data
    let task_BTR = session_BTV.dataTaskWithRequest(request_BTV) { (data_BTV, response_BTV, error_BTV) -> Void in
        if error_BTV != nil {
            print (error_BTV?.description)
        } else {
            do {
                let dict_BTV = try NSJSONSerialization.JSONObjectWithData(data_BTV!, options: .AllowFragments) as? Dictionary<String, AnyObject>
                if let results_BTV = dict_BTV!["results"] as? [Dictionary<String, AnyObject>]{
                    for obj_BTV in results_BTV {
                        let tvshow = TVSeries(tvDict: obj_BTV)
                        //for each tv series try to load a poster_path from secondary provider
                        if let str = obj_BTV["title"] as? String!{
                            let escapedString = str?.stringByAddingPercentEncodingWithAllowedCharacters(.URLQueryAllowedCharacterSet())!

                            if let url = NSURL(string: self.SEARCH_URL_BASE + escapedString!) {
                                let request = NSURLRequest(URL: url)
                                let session = NSURLSession.sharedSession()
                                let task = session.dataTaskWithRequest(request) { (data, response, error) -> Void in
                                    if error != nil {
                                        print (error?.description)
                                    } else {
                                        do {
                                            let dict = try NSJSONSerialization.JSONObjectWithData(data!, options: .AllowFragments) as? Dictionary<String, AnyObject>

                                            if let results = dict!["results"] as? [Dictionary<String, AnyObject>] {

                                                //iterate through the poster array
                                                for obj in results {
                                                    if let path = obj["poster_path"] as? String {
                                                        tvshow.posterPath = path
                                                        break
                                                    }
                                                }
                                            }
                                        } catch let error as NSError {
                                            print(error.description)
                                        }
                                    }
                                }
                                task.resume()
                            }
                        }
                        self.tvSeries.append(tvshow)
                    }
                    dispatch_async(dispatch_get_main_queue()){
                        self.collectionView.reloadData()
                    }
                }
            }  catch let error as NSError {
                print(error.description)
            }
        }
    }
    task_BTR.resume()
}

感谢您的帮助!

我建议将事情分成多个方法,使用回调来对操作进行排序,并利用 built-in throws 的错误处理机制。这是一个示例,并不完美,但作为起点可能会有所帮助:

class TVSeries
{
    let title: String
    var posterPath: String?

    enum Error: ErrorType {
        case MalformedJSON
    }

    init(tvDict: [String: AnyObject]) throws
    {
        guard let title = tvDict["title"] as? String else {
            throw Error.MalformedJSON
        }

        self.title = title
    }

    static func loadAllSeries(completionHandler: [TVSeries]? -> Void)
    {
        NSURLSession.sharedSession().dataTaskWithURL(NSURL(string: BTV_URL_BASE)!) { data, response, error in
            guard let data = data else {
                print(error)
                completionHandler(nil)
                return
            }

            do {
                completionHandler(try fromJSONData(data))
            }
            catch let error {
                print(error)
            }
        }.resume()
    }

    static func fromJSONData(jsonData: NSData) throws -> [TVSeries]
    {
        guard let dict = try NSJSONSerialization.JSONObjectWithData(jsonData, options: .AllowFragments) as? [String: AnyObject] else {
            throw Error.MalformedJSON
        }

        guard let results = dict["results"] as? [[String: AnyObject]] else {
            throw Error.MalformedJSON
        }

        return try results.map {
            return try TVSeries(tvDict: [=10=])
        }
    }

    func loadPosterPath(completionHandler: () -> Void)
    {
        guard let searchPath = title.stringByAddingPercentEncodingWithAllowedCharacters(.URLQueryAllowedCharacterSet()) else {
            completionHandler()
            return
        }

        let url = NSURL(string: SEARCH_URL_BASE)!.URLByAppendingPathComponent(searchPath)
        NSURLSession.sharedSession().dataTaskWithURL(url) { [weak self] data, response, error in
            defer { completionHandler() }

            guard let strongSelf = self else { return }

            guard let data = data else {
                print(error)
                return
            }

            do {
                strongSelf.posterPath = try TVSeries.posterPathFromJSONData(data)
            }
            catch let error {
                print(error)
            }
        }.resume()
    }

    static func posterPathFromJSONData(jsonData: NSData) throws -> String?
    {
        guard let dict = try NSJSONSerialization.JSONObjectWithData(jsonData, options: .AllowFragments) as? [String: AnyObject] else {
            throw Error.MalformedJSON
        }

        guard let results = dict["results"] as? [[String: AnyObject]] else {
            throw Error.MalformedJSON
        }

        for result in results {
            if let path = result["poster_path"] as? String {
                return path
            }
        }

        return nil
    }
}

您可能也值得花时间研究 RxSwift or Alamofire 之类的东西,它可以帮助您进行这些类型的 data-conversion / 排序操作。