如何在Swift 3中使用Alamofire执行顺序请求并在每一步更新progressHUD



好吧,我要疯了…

我使用Alamofire 4。x (Swift 3和XCode 8.1)。我需要从需要身份验证的网站获取和解析几个html请求(不幸的是,没有json API)。然后用Fuzi解析HTML,这个过程可能需要一些时间,所以我计划使用ProgressHUD(确切地说是PKHUD)让用户知道发生了什么。我还需要抓取一些不在身份验证后面的html。

我创建了一个结构体和函数来处理整个网络进程并解析数据。

我设法执行请求并抓取我需要的数据,但我似乎无法弄清楚如何在正确的时间使我的HUD更新。

下面是我的代码:
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
}

在这种设置下,我将在TableViewController中设置一个MscRequest并启动一系列请求,如下所示:

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)

有了这个设置,所有的HUD更新都是在同一时间完成的(在主线程上),并最终被驳回,而后台获取和解析仍在继续。这不是我想要的……

我尝试了各种迭代(有或没有自定义队列),我也尝试了直接从Alamofire的手册(省略completionHandler部分)的HTML请求代码,但我仍然得到相同的结果…

我也看了看中央调度教程(比如这个:http://www.appcoda.com/grand-central-dispatch/),但我还没有弄清楚如何在使用Alamofire时应用信息…

值得注意的是,我设法在Objective-C中使用手动NSURLRequests来完成这项工作。我正在把这个旧的应用程序升级到Swift 3,我想我应该试试Alamofire。

忍不住觉得我错过了一些明显的东西…任何建议吗?

好的,我找到了一种方法来做我想要使用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")
    })
}

你必须使用DownloadRequest和progress。

也看看这篇文章,它解释说:

带进度的Alamofire POST请求

最新更新