如何在Alamofire.request()外使用JSON?

时间:2018-12-31 23:56:14

标签: swift alamofire

我尝试从JSON返回Alamofire字典。问题在于,当将JSON分配给外部变量时,分配的值将被保存并只能在Alamofire.request块内部读取,而在其值外部则不被保存(对我来说很神奇)。我做错了什么?

我第一次这样做。我尝试使用URLSession.shared.dataTask,但也无法从任务返回值。 我注意到,断点首先在return res行上触发,然后在return json行上触发,然后在print (JSON)行上触发。我不知道为什么。

struct resultMsg {
    var message: [String:Any] = [:]
    init() { }
    init(message: [String:Any]) {
        self.message = message
    }
}

func sendGET(method: String, data: [String:String]) -> [String:Any] {
    let resultURL: String = rootURI + method
    let url = URL(string: resultURL)!
    var res: [String:Any] = [:]

    Alamofire.request(url, method: .post, parameters: data, encoding: JSONEncoding.default)
        .responseJSON { response in
            print(response)
            if let status = response.response?.statusCode {
                switch(status){
                case 201:
                    print("example success")
                default:
                    print("error with response status: \(status)")
                }
            }
            //to get JSON return value
            if let result = response.result.value {
                let JSON = result as! NSDictionary
                res = JSON as! [String : Any]
                print(JSON) // Here res is contain actualy json
            }
    }
    return res // Here res equal [:]
}

public func Authorize() -> resultMsg {
    let data: [String:String] = ["login":login,
                                 "pass":pass]        
    var json = resultMsg.init()
    json.message = sendGET(method: "auth.php", data: data)
    return json
}

4 个答案:

答案 0 :(得分:1)

这是因为网络请求通常是异步的。当您呼叫Alamofire.request(...)时,网络请求开始,并且request立即返回。同时,网络事务在后台发生。由于request已返回,因此您的sendGET函数将立即继续到下一行。

稍后,当网络请求完成时,将调用完成块。您将值分配给res,但为时已晚,因为sendGET在请求开始时已经返回了一个空字典。

通常这很快,但是可以帮助我们想象这将花费很长时间。毕竟,有时候 会花费很长时间。如果网络连接超时,则可能需要几分钟。

有不同的处理方式。

  • 在一种方法中,您可以将res设置为该代码所在类的属性。然后,您的完成块仍将为res分配一个值,然后调用一些新代码来告诉您的班级结果可用。
  • 在另一种方法中,您将修改完成闭包以将JSON数据传递给直接使用该数据的其他函数。添加一些参数类型为[String : Any]的新函数,然后从完成闭包中调用该函数。

哪个更好,取决于您应用程序中发生的其他事情以及获取数据时如何使用这些数据。

答案 1 :(得分:0)

汤姆·哈灵顿是对的。更好的方法是在

中添加一个完成块
  

func sendGET

喜欢

func sendGET(method: String, data: [String:String], completion: ([String:Any]) -> Void)

并在获取json时调用完成函数,就这样。

快乐编码

答案 2 :(得分:0)

总的来说,我认为编写Swift程序为时尚早。我想移植用C#编写的iOS项目,我认为本机应用程序会更好,并且这些关闭有很多陷阱。但是在C#中,一切都变得更加简单:只需在函数前编写await,但并非总是如此。

感谢所有帮助过我们的人...

UPD 1

我知道这可能是愚蠢的,但是只有这种方法才能正常工作而不会给闭包带来不必要的麻烦。但是重复的代码会变得很多。

UPD 2

很不幸,我还不得不使用GET而不是POST,因为在POST中服务器没有收到任何数据。我用echo json_encode($_POST);进行了检查。但我可能还没有使用过Alamofire

UPD 3

最后,汤姆·哈灵顿的建议使我获得了更好的解决方案。在找到解决方案like so之前,但它在DispatchQueue.main.async中却无法像Alamofire.request一样工作(我建议Alamofire使用main.async)。因此,我做了like here

UPD 4

我又错了。 UPD 3中的溶液有时会在回路体内冻结。我决定最终学习使用自己的完成功能,我做到了:)

func sendGET(_ method: String,_ data: [String:String],_ instance: CommonClass, completion : @escaping (Any?)->()) {
    let url = getURL(method: method, data: data)
    Alamofire.request(url, method: .get).responseJSON { response in
        if let status = response.response?.statusCode {
            instance.manageStatus(status)
        }
        if let result = response.result.value {
            completion(result)
        }
    }
}

func someFunc(_ instance: CommonClass) {      
    let data: [String:String] = ["id":String(id),
                                 "route":route]
    sendGET("getTaskData.php", data, instance, completion: { response in
        let JSON = response as! [[String:String]]
        // Do something with JSON
    })
}

答案 3 :(得分:0)

我会为您提供一种方法。借助Swift 4.2,Codable已启动,因此您可以轻松地进行JSON解析并摆脱代码。

示例类

public struct Faults: Decodable {

    public enum CodingKeys:String,CodingKey{
        case guid
        case adi
    }

    public let guid:Int
    public let adi:String
}

和json解析(获取Web服务)

 public func fetchFaults(complation: @escaping (Result<[Faults]>) -> Void) {
        let urlString = "http://examples"
       request(urlString, method: .get, encoding: URLEncoding.default, headers: ...)
        .responseData { (response) in
            switch response.result {
            case .success(let data):
                do {
                    let faults = try Decoders.plainDateDecoder.decode([Faults].self, from: data)
                    complation(.success(faults))
                } catch{
                    complation(.failure(Error.serializationError(internal: error)))
                }
            case .failure(let error):
                complation(.failure(Error.networkError(internal: error)))
            }
        }
    }