如何创建一个从异步调用返回返回值的函数。

时间:2017-05-17 04:21:22

标签: swift

所以我有这个POST功能。

class func postRequest(request: URLRequest, saveCookie: Bool, completionHandler: @escaping (_ postRequestStatus: [String:Any]) -> ()) {
    let session = URLSession.shared
    //So now no need of type conversion
    let task = session.dataTask(with: request) {
        (data, response, error) in
        func displayError(_ error: String) {
            print(error)
        }

        /* GUARD: Was there an error? */
        guard (error == nil) else {
            displayError("There was an error with your request: \(String(describing: error))")
            return
        }

        guard let statusCode = (response as? HTTPURLResponse)?.statusCode, statusCode >= 200 && statusCode <= 299 else {
            displayError("Your request returned a status code other than 2xx!")
            return
        }

        /* GUARD: Was there any data returned? */
        guard let data = data else {
            displayError("No data was returned by the request!")
            return
        }

        /* Since the incoming cookies will be stored in one of the header fields in the HTTP Response,parse through the header fields to find the cookie field and save the data */
        if saveCookie{
            let httpResponse: HTTPURLResponse = response as! HTTPURLResponse
            let cookies = HTTPCookie.cookies(withResponseHeaderFields: httpResponse.allHeaderFields as! [String : String], for: (response?.url!)!)
            HTTPCookieStorage.shared.setCookies(cookies as [AnyObject] as! [HTTPCookie], for: response?.url!, mainDocumentURL: nil)
        }

        let json: [String:Any]?
        do
        {
            json = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [String:Any] ?? [:]
        }
        catch
        {
            displayError("Could not parse the data as JSON: '\(data)'")
            return
        }

        guard let server_response = json else
        {
            displayError("Could not parse the data as JSON: '\(data)'")
            return
        }

        if let userID = server_response["UserID"] as? Int64 {
            print(userID)
            completionHandler(server_response)
        }else{
            displayError("Username or password incorrect.")
        }
    }
    return task.resume()
}

现在我通过这个函数调用这个post函数:

class func loginPostRequest(post_data: [String:String], completionHandler: @escaping (_ postRequestStatus: [String:Any]) -> ()){
    let url = URL(string: HTTPConstant.Login.Url)!
    var request = URLRequest(url: url)
    request.httpMethod = "POST"
    var paramString = ""
    for (key, value) in post_data
    {
        paramString = paramString + (key) + "=" + (value) + "&"
    }
    request.httpBody = paramString.data(using: .utf8)
    postRequest(request: request, saveCookie: true, completionHandler: { postRequestStatus in
        completionHandler(postRequestStatus)
    })
}

上述元素功能属于同一类。现在我想在一个不同的类中编写一个函数,这样我就可以调用loginPostRequest()并获得[String:Any]作为结果。像这样:

 var post_data = [String:String]()
    post_data["username"] = "Email"
    post_data["password"] = "Password"
data :[String:Any] = HTTPRequests.loginPostRequest(post_data);

&LT; - - &GT;如何更新其他功能,这样我就可以实现上面的“数据”作为返回值,而不必担心这里的完成处理程序。

3 个答案:

答案 0 :(得分:1)

您的代码非常具体,但我会提供更通用的解决方案,以便它可以帮助其他偶然发现此问题的人。如果您有实施此问题的后续问题,请随时提出。

首先,如果您的目标是获得同步行为,请使用同步方法。许多库提供异步和同步变体的功能。他们没有必要执行额外的工作来使调用异步,只为你做更多的工作来为它进行同步包装。

您可以将任何意味着使用完成句柄的异步调用转换为返回值的同步调用。请遵循以下模式:

func synchronousFunction(_ someInput: Input, timeout: DispatchTime? = nil) -> Result {
    var result: Result?

    let semaphore = DispatchSemaphore(value: 0)

    asynchronousFunction(input, completionHandler: { _result in
        result = _result
        semaphore.signal()
        // signalling the semaphore indicates the async function is done,
        // that `result` is set, and that `synchronousFunction` can return it.
    })

    // Wait for signal from completion handler, up to `timeout`, if it's set
    if let timeout = timeout  { semaphore.wait(timeout: timeout) }
    else { semaphore.wait() }


    if let result = result { return result }
    else { fatalError("The completion handler didn't assign to `result`.") }
}

如果DispatchWallTime对您来说不够准确,您也可以使用DispatchTime。或者,如果您确定异步任务将完成,则完全删除超时逻辑。

答案 1 :(得分:0)

var post_data = [String:String]()
post_data["Email"] = "isername@hotmail.com"
post_data["Password"] = "password1234"
HTTPRequests.loginPostRequest(post_data: post_data, completionHandler: { postRequestStatus in
    let data = postRequestStatus
    print(data)
})

答案 2 :(得分:0)

有几种不同的方法可以做到这一点,因为我已经写了这个解决方案,但我认为有更多的方法可以给猫皮肤......所以为什么不选择。

我只是在猜测而不试图自己实现这一点,但是进行跨线程通信的一种方法是使用协议。这要求关联的类(委托)符合方法/变量。 “manager”类可以使用这些必需的方法/变量将数据传递给委托。也许是这样的? (如果这实际上是你想要实现的,没有使用外部类的处理程序?):

协定/ “管理器”:

//protocol, required vars/func a delegate must have (conform to)
protocol HTTPRequestDelegate {
    var post_data: [String: String] { get }
    func httpRequestDelegate(receivedData: [String: Any])
}

//manager class, acts on delegate via protocol methods/vars
class HTTPRequest: NSObject {  //Im just using the NSObject super

    //declare a property the delegate can access and assign as themself
    var delegate: HTTPRequestDelegate? {
        didSet {
            //once a delegate assigns itself as the delegate, execute the desired class func   
            guard let delegate = delegate else { return }
            HTTPRequest.loginPostRequest(post_data: delegate.post_data, delegate: delegate)
        }
    }


    class func postRequest(request: URLRequest, saveCookie: Bool, completionHandler: @escaping (_ postRequestStatus: [String:Any]) -> ()) {
        //class setup
    }


    // Since you are using class func's, add a parameter to bring in the delegate's of the class
    class func loginPostRequest(post_data: [String:String], delegate: HTTPRequestDelegate) {
        let url:  URL = URL(string: "theURL")!
        let request = URLRequest(url: url)

        postRequest(request: request, saveCookie: true, completionHandler: { postRequestStatus in
                    // once the data has been received, send the data to the delegate via protocol func
                    delegate.httpRequestDelegate(receivedData: postRequestStatus)
        })
    }
}

委托课程:

//delegate class, the class you want to receiver to send info to  
class FooClass: NSObject, HTTPRequestDelegate {

    var post_data: [String: String] { return postData }
    var postData = [String: String]()
    var data: [String: Any]?
    let requestManager = HTTPRequest()

    override init() {
        super.init()
        self.postData["username"] = "Email"
        self.postData["password"] = "Password"
        requestManager.delegate = self
    }

    func httpRequestDelegate(receivedData: [String : Any]) {
        //when the httpRequest class receives the data, it will call this call this function and send the info to the delegate(s) class as parameters.
        data = receivedData
    }
}