Swift 4 上的几个相互依赖的 JSON 请求

Several co-dependent JSON requests on Swift 4

我正在重构我以前编写的代码,我在其中使用 Alamofire 下载一些 Json 文件。
第一个请求很简单。我发出请求,得到响应,然后解析它并将其存储在 Realm 上。这里没问题。直截了当的东西。 第二个请求有点棘手,因为我需要从第一个 JSON 请求中检索到的多个 ID。

我对该问题的解决方案是首先在具有 Alamofire 请求的函数上创建一个完成处理程序:

func requestData(httpMethod: String, param: Any?, CallType : String, complition: @escaping (Bool, Any?, Error?) -> Void){

我的想法是使用 Completion 等待 Alamofire Response 完成,然后开始新的请求。事实证明它也没有用。

我能够通过在完成中添加延迟来实现这一目标。

DispatchQueue.main.asyncAfter(deadline: .now() + 4)

它确实有效,但由于多种原因远不是一个好的做法,我想用更智能的东西重构它。

我的问题:

1) 对同一功能发出多个 JSON 请求的最佳方法是什么?一种正确等待第一个开始第二个等等的方法? 2) 现在,我调用一个函数来请求第一个 JSON,并在调用过程中发出第二个请求。在我看来,我把第一个请求挂得太久了,等待所有请求完成然后完成第一个请求。我认为这不是一个好习惯

这是完整的代码。感谢帮助

    @IBAction func getDataButtonPressed(_ sender: Any) {
    requestData(httpMethod: "GET", param: nil, CallType: "budgets") { (sucess, response, error) in

    if sucess{
        print("ready")
        DispatchQueue.main.asyncAfter(deadline: .now() + 4){
        accounts = realm.objects(Account.self)
        requestAccounts()
        }
        DispatchQueue.main.asyncAfter(deadline: .now() + 4){
        users = realm.objects(User.self)
        requestUser()
            }
        }
    }
}


func requestData(httpMethod: String, param: Any?, CallType : String, complition: @escaping (Bool, Any?, Error?) -> Void){
let url = "https://XPTO.com/v1/\(CallType)"
guard let urlAddress = URL(string: url) else {return}
var request = URLRequest(url: urlAddress)
request.httpMethod = httpMethod
request.addValue("application/json", forHTTPHeaderField: "accept")
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("Bearer appKey", forHTTPHeaderField: "Authorization")
if param != nil{
    guard let httpBody = try? JSONSerialization.data(withJSONObject: param!, options:[]) else {return}
    request.httpBody = httpBody
}
Alamofire.request(request).responseJSON { (response) in
        let statusCode = response.response?.statusCode
        print("Status Code \(statusCode!)")
        jsonData = try! JSON(data: response.data!)

    complition(true, jsonData, nil)
    if httpMethod == "GET"{
    saveJsonResponse(jsonData: jsonData, CallType: CallType)
    }
    }
}

func requestAccounts(){
var count = accounts.count

while count != 0{
    let account = accounts[0]
    RealmServices.shared.delete(account)
    count -= 1
}


let numberOfBugdets = budgets.count
for i in 0...numberOfBugdets - 1{

    requestData(httpMethod: "GET", param: nil, CallType: "/budgets/\(budgets[i].id)/accounts") { (sucess, response, error) in
     print("accounts downloaded")

        let numberOfAccounts = jsonData["data"]["accounts"].count
        for j in 0...numberOfAccounts - 1{
            let realm = try! Realm()
            do{
                try realm.write {
                    // Code to save JSON data to Realm
                    realm.add(newAccount)
                }
            } catch {
                print("something")
            }
        }
    }
}

}

func requestUser(){
var count = users.count
while count != 0{
    let user = users[0]
    RealmServices.shared.delete(user)
    count -= 1
}
requestData(httpMethod: "GET", param: nil, CallType: "user") { (success, response, error) in
    print("User data downloaded")
    let realm = try! Realm()
    do{
        try realm.write {
            // Code to save JSON data to Realm
            realm.add(newUser)
            }
    } catch {
        print("something")
    }
}
}

func saveJsonResponse(jsonData: JSON, CallType: String){

case "budgets":
    var count = budgets.count
    while count != 0{
        let budget = budgets[0]
        RealmServices.shared.delete(budget)
        count -= 1
    }

    let numberOfBudgets = jsonData["data"]["budgets"].count
    for i in 0...numberOfBudgets - 1 {

        // Code to save JSON data to Realm
        RealmServices.shared.create(newBudget)

    }

}

在这种情况下,我推荐 completionHandlers

这是关于如何实现和使用它的代码片段如何尝试理解它并在您的代码中实现它。

//CompletionHandlers 

func firstOperation(completionHandler: @escaping (_ id: String) -> Void){
//preform alamoFire and in .response { }  call completionHandler and pass it the id
completionHandler("10")
}
func buttonClicked () {
    firstOperation { (id) in
        secondFunction(completionHandler: { (data) in
            // your data
        })
    }

}
func secondFunction(completionHandler: @escaping (_ data: String) -> Void){
    //preform alamoFire and in .response { }  call completionHandler and pass it the id
    completionHandler("some Data")
}

这应该能让您更好地理解如何实现它,CompletionHandlers 很强大

特别是在处理此类情况时,您必须执行依赖于其他操作结果的操作,并且在网络中我们无法预测操作的时间。

阅读有关 completionHandlers 的更多信息here