问题
Ok, I am going nuts over this one...
I'm using Alamofire 4.x (Swift 3 and XCode 8.1). I need to fetch and parse several html requests from a site that requires authentication (no json API, unfortunately). The HTML is then parsed with Fuzi and this progress can take some time so I plan to use a ProgressHUD (PKHUD to be exact) to let users know about what is going on. I also need to grab some html that is not behind an authentication.
I created a struct and functions to handle the overall network process and to parse the data.
I managed to perform the requests and grab the data I need but I can't seem to figure out how to make my HUD updates at the right time.
Here is my code so far:
import Alamofire
import Fuzi
import PKHUD
struct MyMSCProvider {
static let baseUrl = "http://mastersswimming.ca"
//I tried with or without a custom queue - same result
static let processingQueue = DispatchQueue(label: "com.colddiver.processing-queue", qos: .utility)
static func fetchData(data: MscRequest) {
if data.profile || data.log {
//Authenticate first!
HUD.show(.labeledProgress(title: "Authenticating", subtitle: ""))
let requestUrl = "\(baseUrl)/MyMscPage.jsp"
let parameters = ["locale": "en", "username": data.user.username, "password": data.user.password]
Alamofire.request(requestUrl, method: .post, parameters: parameters).responseData(
queue: processingQueue,
completionHandler:
{ response in
// Now on the processingQueue you created earlier.
print("THREAD: \(Thread.current) is main thread: \(Thread.isMainThread)")
switch response.result {
case .success:
if data.profile {
DispatchQueue.main.async {
HUD.show(.labeledProgress(title: "Getting Profile", subtitle: ""))
}
let userProfile = parseProfile(data: response.data!, user: data.user)
print(userProfile)
}
if data.log {
DispatchQueue.main.async {
HUD.show(.labeledProgress(title: "Getting Log", subtitle: ""))
}
fetchLog()
}
if data.records {
DispatchQueue.main.async {
HUD.show(.labeledProgress(title: "Getting Records", subtitle: ""))
}
fetchRecords(recordsToFetch: data.recordsToFetch)
}
if data.times {
DispatchQueue.main.async {
HUD.show(.labeledProgress(title: "Getting Times", subtitle: ""))
}
print("Fetching times is not implemented yet")
}
DispatchQueue.main.async {
HUD.flash(.success)
}
case .failure(let error):
HUD.flash(.error)
print("Alamofire request failed")
print(error)
}
}
)
} else {
//Just fetch - no need to authenticate first
if data.records {
DispatchQueue.main.async {
HUD.show(.labeledProgress(title: "Getting Records", subtitle: ""))
}
fetchRecords(recordsToFetch: data.recordsToFetch)
}
if data.times {
print("Fetching times is not implemented yet")
}
DispatchQueue.main.async {
HUD.flash(.success)
}
}
}
static func fetchRecords(recordsToFetch: RecordsToFetch) {
for province in recordsToFetch.provinces {
for ageGroup in recordsToFetch.ageGroups {
for gender in recordsToFetch.genders {
DispatchQueue.main.async {
HUD.show(.labeledProgress(title: "Getting Records", subtitle: "\(province) - \(gender+Helpers.getAgeGroupFromAge(age: Int(ageGroup)!))"))
}
let requestUrl = "\(baseUrl)/Records.jsp"
let parameters = ["locale": "en", "province": province, "age": ageGroup, "gender": gender, "course": "*"]
Alamofire.request(requestUrl, method: .post, parameters: parameters).responseData(
queue: processingQueue,
completionHandler: { response in
switch response.result {
case .success:
let recordArray = parseRecords(data: response.data!, province: province, ageGroup: ageGroup, gender: gender)
case .failure(let error):
DispatchQueue.main.async {
HUD.flash(.failure)
}
print("Alamofire request failed")
print(error)
}
}
)
}
}
}
}
static func fetchLog() {
let requestUrl = "\(baseUrl)/ViewLog.jsp"
Alamofire.request(requestUrl).responseData(
queue: processingQueue,
completionHandler: { response in
switch response.result {
case .success:
let log = parseLog(data: response.data!)
case .failure(let error):
DispatchQueue.main.async {
HUD.flash(.failure)
}
print("Alamofire request failed")
}
}
)
}
// MARK: - Convenience structs
struct MscRequest {
let profile: Bool
let log: Bool
let times: Bool
let records: Bool
let recordsToFetch: RecordsToFetch
let user: MscUser
let parentView: UITableViewController
}
Under this setup, I would setup a MscRequest in a TableViewController and launch a series a requests like so:
let myData = MscRequest.init(
profile: true,
log: true,
times: false,
records: true,
recordsToFetch: RecordsToFetch.init(
provinces: ["NB", "CA"],
ageGroups: ["20", "25", "30", "35", "40"],
genders: ["M", "F"]),
user: MscUser.init(
username: "SomeUserName",
password: "SomePassword"),
parentView: self
)
MyMSCProvider.fetchData(data: myData)
With this setup, all the HUD updates are done at the same time (on the main thread) and end up being dismissed while the background fetching and parsing is still going on. Not exactly what I was going for...
I tried various iterations (with or without a custom queue), I also tried the HTML request code straight from Alamofire's manual (which omits the completionHandler part) but I still get the same results...
I also had a look at Grand Central Dispatch tutorials (such as this one: http://www.appcoda.com/grand-central-dispatch/) but I haven't figured out how to apply info when using Alamofire...
Of note, I managed to make this work in Objective-C with manual NSURLRequests back then. I'm modernizing this old application to Swift 3 and thought I should give Alamofire a try.
Can't help to feel like I am missing something obvious... Any tips?
回答1:
Ok, I found a way to do what I want using a DispatchGroup (Swift 3, Alamofire 4.x)
func fetchData() {
let requestGroup = DispatchGroup()
//Need as many of these statements as you have Alamofire.requests
requestGroup.enter()
requestGroup.enter()
requestGroup.enter()
Alamofire.request("http://httpbin.org/get").responseData { response in
print("DEBUG: FIRST Request")
requestGroup.leave()
}
Alamofire.request("http://httpbin.org/get").responseData { response in
print("DEBUG: SECOND Request")
requestGroup.leave()
}
Alamofire.request("http://httpbin.org/get").responseData { response in
print("DEBUG: THIRD Request")
requestGroup.leave()
}
//This only gets executed once all the above are done
requestGroup.notify(queue: DispatchQueue.main, execute: {
// Hide HUD, refresh data, etc.
print("DEBUG: all Done")
})
}
回答2:
You have to use DownloadRequest and use progress.
Also take a look at this post, it explained:
Alamofire POST request with progress
来源:https://stackoverflow.com/questions/40555188/how-to-perform-sequential-request-with-alamofire-and-update-a-progresshud-at-eve