简体   繁体   中英

Wait for background request to finish in Alamofire and array is loaded before continuing swift

I know there is another question on here just like this but it wasn't clearly answered by anyone and there was no accepted answer. I have a method loadDetail which takes a completionHandler using the UIBackgroundFetchResult. This method is called when I select a cell to load the detailed data for that cell from an array. When this method is called I want the method its called from to wait until the detailResults array is filled before continuing. How can I do this and what am I doing wrong?

func loadDetail(urlPath: String, completionHandler: ((UIBackgroundFetchResult)     -> Void)!){
    Alamofire.request(.GET, urlPath)
        .responseJSON { _, _, result in
            switch result {
            case .Success(let data):
                let jsonObj = JSON(data)
                if let data = jsonObj["results"].arrayValue as [JSON]? {
                    //array is filled in the line below
                    self.detailResults = data
                    self.collectionView.reloadData()
                }
            case .Failure(_, let error):
                print("Request failed with error: \(error)")
            }
    }
    completionHandler(UIBackgroundFetchResult.NewData)
    print("Background Fetch Complete")
}

Here is the collectionView method that loadDetail is called from, you can see the comment where it is called pertaining to what I would like it to do.

func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
    let data = results?[indexPath.row]
    let detailData: JSON
    var testString: String = ""
    let vc: DetailBrowseViewController = self.storyboard?.instantiateViewControllerWithIdentifier("DetailBrowseViewController") as! DetailBrowseViewController

    if let titleString = data?["title"] {
        vc.titleString = titleString.stringValue
        testString = titleString.stringValue
        testString = testString.stringByReplacingOccurrencesOfString(" ", withString: "-")
        testString = testString.lowercaseString
        reusableUrl.replaceRange(Range<String.Index>(start: reusableUrl.startIndex.advancedBy(146), end: reusableUrl.startIndex.advancedBy(146)), with: testString)

        self.loadDetail(reusableUrl, completionHandler:{(UIBackgroundFetchResult) -> Void in
            //I want this collectionView method to somehow pause and wait until the detailResults array is filled and then continue
            print("success")
        })
    }

    detailData = (detailResults?[indexPath.row])!

    if let priceString = detailResults?[indexPath.row]["price"] {
        print(detailData)
        vc.priceString = priceString.stringValue
    }
    self.navigationController?.pushViewController(vc, animated: true)
}

The problem is, the loadDetail method goes straight through without waiting, causing the array to be nil when its called in the collectionView method.

As I understand, you need to call completionHandler when self.detailResults is populated with JSON data from response.

I will try to figure our the main idea: - call competitionHandler only when data was successfully parsed - move necessary code (which should wait for response) into the place where data is already loaded.

First you need to move completionHandler call to the end of the .Success case block. I think you don't need to move forward if response is fail.

So result will be:

func loadDetail(urlPath: String, completionHandler: ((UIBackgroundFetchResult)     -> Void)!){
    Alamofire.request(.GET, urlPath)
        .responseJSON { _, _, result in
            switch result {
            case .Success(let data):
                let jsonObj = JSON(data)
                if let data = jsonObj["results"].arrayValue as [JSON]? {
                    //array is filled in the line below
                    self.detailResults = data
                    self.collectionView.reloadData()
                    completionHandler(UIBackgroundFetchResult.NewData)
                }
            case .Failure(_, let error):
                print("Request failed with error: \(error)")
            }
    }

    print("Background Fetch Complete")
}

Then you need to move related code (which should wait for response) to the completionHandler like so:

func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
    let data = results?[indexPath.row]
    let detailData: JSON
    var testString: String = ""
    let vc: DetailBrowseViewController = self.storyboard?.instantiateViewControllerWithIdentifier("DetailBrowseViewController") as! DetailBrowseViewController

    if let titleString = data?["title"] {
        vc.titleString = titleString.stringValue
        testString = titleString.stringValue
        testString = testString.stringByReplacingOccurrencesOfString(" ", withString: "-")
        testString = testString.lowercaseString
        reusableUrl.replaceRange(Range<String.Index>(start: reusableUrl.startIndex.advancedBy(146), end: reusableUrl.startIndex.advancedBy(146)), with: testString)

        self.loadDetail(reusableUrl, completionHandler:{(UIBackgroundFetchResult) -> Void in
            //I want this collectionView method to somehow pause and wait until the detailResults array is filled and then continue
            print("success")
            detailData = (detailResults?[indexPath.row])!

            if let priceString = detailResults?[indexPath.row]["price"] {
                print(detailData)
                vc.priceString = priceString.stringValue
            }
            self.navigationController?.pushViewController(vc, animated: true)
        })
    }
} 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM