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