I am new in programming, but I usually I have no issue to make request to get JSON data from server.
I have tried some similar thread in here: Alamofire network calls not being run in background thread but the solution doesn't solve my problem!
so I want to implement pagination, so when the user reach bottom of the screen, then I will make a request to the server.
but unfortunately, even though I can get the JSON data from server, but it will freeze the app. it seems that this is not performed in the background (still on the main thread).
but I don't understand why. I assume that if I use Alamofire to make a request, it will automatically perform it asynchronously. because usually I make a request on viewDidLoad
or viewWillAppear
and my app never freezing.
here is the code triggered when the scroll view reach the bottom:
extension HomeVC : UIScrollViewDelegate {
func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
let currentOffset = scrollView.contentOffset.y
let maximumOffset = scrollView.contentSize.height - scrollView.frame.size.height
if maximumOffset - currentOffset <= 10.0 {
// load more product data when reaching the bottom of main scroll view
pageNumberTracker += 1
SVProgressHUD.show(withStatus: "Please Wait")
Product.getProducts(searchType: "newest", pageNumber: pageNumberTracker, categoryID: selectedCategoryID) { (errorWhileMakingRequest, errorMessageFromServer, products) in
if errorWhileMakingRequest != nil || errorMessageFromServer != nil {
SVProgressHUD.dismiss()
return
}
guard let products = products else {self.activityIndicator.stopAnimating(); return}
self.thirdProducts += products
self.updateLocalDataToBeTheSameAsRealmDatabase()
self.setThirdListProductCollectionViewHeight()
self.thirdListProductCollectionView.reloadData()
SVProgressHUD.dismiss()
}
}
}
}
here is the getProduct()
method :
static func getProducts(searchType: String, pageNumber: Int = 0, categoryID: Int = 0, completion: @escaping(_ errorWhileMakingRequest: Error?, _ errorMessageFromServer: String?,_ productsData: [Product]?) -> Void) {
let urlProducts = URLService.products.endPoint
let headers = ["Content-Type": "application/x-www-form-urlencoded"]
let parameters : [String:Any] = [
"type": searchType,
"language_id": 1,
"page_number": pageNumber,
"minPrice": 0,
"maxPrice":10000000,
"categories_id": categoryID
]
AlamofireManager.shared.request(urlProducts,
method: .post,
parameters: parameters,
encoding: URLEncoding.default,
headers:headers)
.validate()
.responseJSON { response in
switch response.result {
case .failure(let error) :
completion(error,nil,nil)
case .success(let value) :
let json = JSON(value)
let successStatus = json["success"].stringValue
if successStatus == "0" {
let errorMessage = json["message"].stringValue
completion(nil,errorMessage,nil)
} else if successStatus == "1" {
let productsArrayJSON = json["product_data"].arrayValue
var productsData = [Product]()
for productJSON in productsArrayJSON {
if let productDictionary = productJSON.dictionaryObject {
let product = Product(dictionary: productDictionary)
productsData.append(product)
} else {
completion(nil,nil,nil)
break
}
}
completion(nil,nil,productsData)
}
}
}
}
and here is the Alamofire manager code:
struct AlamofireManager {
static let shared: SessionManager = {
let configuration = URLSessionConfiguration.default
configuration.timeoutIntervalForRequest = 15
let sessionManager = Alamofire.SessionManager(configuration: configuration, delegate: SessionDelegate(), serverTrustPolicyManager: nil)
return sessionManager
}()
}
what went wrong in here ?
The completion of an Alamofire request will execute on the main thread by default. If your response from the server is pretty big, then doing those JSON operations can be pretty expensive and cause some frame drops. Alamofire allows you to set the completion queue by calling it like this:
AlamofireManager.shared.request(urlProducts,
method: .post,
parameters: parameters,
encoding: URLEncoding.default,
headers:headers)
.validate()
.responseJSON(queue: DispatchQueue.someQueue) { response in
...
}
I think the reason is not related to the threading. You are not handling the completion block properly in all cases. In case of success you are handling just two scenarios ie when successStatus
is either 1
or 0
. What happens if it is neither of the two? Hence you should add an else block at the end so that the completion block gets called in all cases. Here's the code:
case .success(let value):
let json = JSON(value)
let successStatus = json["success"].stringValue
if successStatus == "0" {
let errorMessage = json["message"].stringValue
completion(nil,errorMessage,nil)
} else if successStatus == "1" {
let productsArrayJSON = json["product_data"].arrayValue
var productsData = [Product]()
for productJSON in productsArrayJSON {
if let productDictionary = productJSON.dictionaryObject {
let product = Product(dictionary: productDictionary)
productsData.append(product)
} else {
completion(nil,nil,nil)
break
}
}
completion(nil,nil,productsData)
}
else {
// Decide how you should call the completion block
completion(xx, yy, zz)
}
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.