在同一线程中的Swift POST请求 [英] Swift POST Request in same Thread

查看:86
本文介绍了在同一线程中的Swift POST请求的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

希望您能帮助我.我想要一个快速的函数来发出发布请求并返回json数据

Hope you can help me. I want a swift function that make a post request and return the json data

这是我的课程

import Foundation


class APICall {

    //The main Url for the api
    var mainApiUrl = "http://url.de/api/"

    func login(username: String, password: String) -> String {
        let post = "user=\(username)&password=\(password)";
        let action = "login.php";
        let ret = getJSONForPOSTRequest(action: action, post: post)
        return ret;
    }

    //Function to call a api and return the json output
    func getJSONForPOSTRequest(action: String, post: String) -> String {
        var ret: String?

        let apiUrl = mainApiUrl + action;

        let myUrl = URL(string: apiUrl);
        var request = URLRequest(url:myUrl!);
        request.httpMethod = "POST";

        let postString = post;

        request.httpBody = postString.data(using: String.Encoding.utf8);

        let task = URLSession.shared.dataTask(with: request) { (data: Data?, response: URLResponse?, error: Error?) in

            if error != nil
            {
                print("error=\(error)")
                return
            }

            print("response=\(response)")

            do {
                let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? NSDictionary

                if let parseJSON = json {
                    let login = parseJSON["Login"] as? String
                    print("login: \(login)")
                    ret = login
                }
            } catch {
                print(error)
            }
        }
        task.resume()
        return ret!;
    }

}

但是ret为零.在调试器中是否看到任务的内部稍后被另一个线程调用?

But ret is nil. In the debugger is see the inner of the task is called later by another thread?

该如何解决?

谢谢你们

推荐答案

数据任务完成闭包是在另一个线程上调用的,并且在方法执行完成后,因此您需要重新调整代码.

The data task completion closure is called on another thread and after the execution of the method is completed so you need to re-jig your code a bit. Instead of having a String return value for your getJSONForPOSTRequest, don't return anything and instead have an additional argument that is a closure and call that from within your dataTask closure instead.

func getJSONForPOSTRequest(action: String, post: String, completion: (string: String) -> Void) {

    // ...

    let task = URLSession.shared.dataTask(with: request) { (data: Data?, response: URLResponse?, error: Error?) in

        // ... (Convert data to string etc.)

        completion(string: myString)
    }

    task.resume()
}

请记住,这样做意味着在网络请求完成后立即调用完成处理程序,而不是立即进行.

Remember, doing this means that the completion handler will be called once the network request completes and not right away.

让我们从头开始.在iOS中从网络下载内容时,通常使用NSURLSession. NSURLSession有许多可用的方法用于与网络交互的不同方式,但是所有这些方法都使用不同的线程(通常是后台线程),这些线程将独立于其余代码工作.

Lets take this from the beginning. When you download something from the network in iOS you typically use NSURLSession. NSURLSession has a number of methods available to it for different means of interacting with the network, but all of these methods use a different thread, typically a background thread, which will do work independently of the rest of your code.

考虑到这一点,当您调用dataTask方法时,您会注意到您必须添加一个完成闭包作为参数之一(在示例中请注意,您正在使用一种称为跟踪闭包"的东西)闭包,它是方法调用中的最后一个参数,且不与其他参数一起包含在方法的括号中).可以将闭包看作是在不同时间执行的一段代码,它与周围的其他代码执行不一致(请参见有关闭包的Swift文档

With this in mind, when you call the dataTask method you will notice that you have to add a completion closure as one of the parameters (notice in your example you are using something called a 'trailing closure' which is a closure that is the last argument in the method call that doesn't fall within the parenthesis of the method with the rest of the arguments). Think of a closure as a piece of code that is executed at a different time, it's not executed in line with the rest of the code around it (See the Swift documentation on closures here). In this case the closure will be called once the network request has been completed. Network requests aren't instant so we typically use a background thread to execute them while the user is shown an activity indicator etc and can still use the app. If we waited until the network request completed on the same thread as the rest of our code then it results in the app appearing laggy and even frozen which is terrible for users.

现在回到您的示例;当您调用getJSONForPOSTRequest方法时,该方法中的代码将完成并在网络请求完成之前返回,这就是为什么我们不需要使用返回值的原因.网络请求完成后,您的关闭代码将被调用.因为闭包是在以后调用的,所以它也从代码中的另一个完全不同的地方被调用,在这种情况下,它是从iOS的网络代码中调用的.因为如果这样,如果您从闭包内返回一个值,那么您将尝试将该值返回给您不想要的网络代码,那么您希望将该值返回给您自己的代码.

So going back to your example at hand; when you call your getJSONForPOSTRequest method the code within that method will complete and return before the network request has completed which is why we don't need to use a return value. Once the network request has completed your closure code will get called. Because the closure is called later it's also being called from an entirely different place within the code, in this case it's called from within iOS's network code. Because if this if you return a value from within the closure you will be trying to return the value to the network code which isn't what you want, you want to return the value to your own code.

要将网络响应的值返回给您的代码,您需要自己定义一个闭包(或委托,但在此不做介绍).如果您看一下上面的示例代码,我已经从您的getJSONForPOSTRequest方法中删除了返回值,并添加了一个名为"completion"的新参数,并且如果您查看该参数的类型,则可以看到它是(string: String) -> Void,定义一个传递字符串(您将从网络下载的字符串)的闭包.现在,您的方法中有了一个闭包,我们可以使用此闭包来调用getJSONForPOSTRequest的调用者,并提供从网络中下载的数据.

To return the value of the network response to your code you need to define a closure (or a delegate, but I'm not going to go into that here) yourself. If you look at the example code above I've removed the return value from your getJSONForPOSTRequest method and added a new argument called 'completion', and if you look at the type of that argument you can see it's (string: String) -> Void, this defines a closure that passes in a string (the string that you will have downloaded from the network). Now that we have a closure thats within your method we can use this to call back to the caller of the getJSONForPOSTRequest with the data we have downloaded form the network.

让我们采用您的login方法,看看我们如何在其中使用getJSONForPOSTRequest:

Lets take your login method and see how we use getJSONForPOSTRequest within it:

func login(username: String, password: String, completion: (success: Bool) -> Void) {

    let post = "user=\(username)&password=\(password)";
    let action = "login.php";

    let ret = getJSONForPOSTRequest(action: action, post: post) { string in 

        // This will be called once the network has responded and 'getJSONForPOSTRequest' has processed the data
        print(string)

        completion(success: true)
    }
}

再次看到,我们没有直接从login方法返回任何东西,因为它必须依赖于调用网络的非同步性.

See that again we aren't returning anything directly from the login method as it has to rely on the a-synchronousness of calling off to the network.

到现在为止,您可能会开始感觉到被称为回调地狱"的事物,但这是处理网络的标准方法.在您的UI代码中,您将调用login,这将是链的结尾.例如,下面是一些假设的UI代码:

It might feel by now that you are starting to get into something called 'callback hell', but this is the standard way to deal with networking. In your UI code you will call login and that will be the end of the chain. For example here is some hypothetical UI code:

func performLogin() {

    self.activityIndicator.startAnimating()

    self.apiCaller.login(username: "Joe", password: "abc123") { [weak self] success in 

        print(success)

        // This will get called once the login request has completed. The login might have succeeded of failed, but here you can make the decision to show the user some indication of that
        self?.activityIndicator.stopAnimating()
        self?.loginCompleted()
    }
}

希望您能澄清一些问题,如果您还有其他疑问,请问.

Hopefully that clarifies a few things, if you have any other questions just ask.

这篇关于在同一线程中的Swift POST请求的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆