Swift 如何创建从异步调用返回值的函数。
所以我有这个POST函数Swift 如何创建从异步调用返回值的函数。,swift,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
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()
}
以上提到的函数属于同一类。现在我想在另一个类中编写一个函数,这样我就可以调用loginPostRequest(),并得到[String:Any]作为结果。大概是这样的:
var post_data = [String:String]()
post_data["username"] = "Email"
post_data["password"] = "Password"
data :[String:Any] = HTTPRequests.loginPostRequest(post_data);
如何更新其他函数,以便我可以实现上述“数据”作为返回值,而不必担心这里的完成处理程序 您的代码非常具体,但我将提供一个更通用的解决方案,这样它也可以帮助其他偶然发现这一点的人。如果您在实施过程中有后续问题,请随时提问
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)
})
首先,如果您的目标是获得同步行为,请使用同步方法。许多库提供异步和同步变体的函数。他们没有必要执行额外的工作来使调用异步,只需要您做更多的工作来为它进行同步包装
您可以将任何要使用完成句柄的异步调用转换为返回值的同步调用。只需遵循以下模式:
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`.") }
}
如果
DispatchTime
对您来说不够准确,您也可以使用DispatchWallTime
。如果您确信异步任务将完成,也可以完全删除超时逻辑。有两种不同的方法可以做到这一点,因为我写了这篇文章,给出了一个解决方案,但我认为有不止一种方法可以剥猫皮。。那么为什么我们没有选择呢
我只是猜测一下,并没有尝试自己实现它,但实现跨线程通信的一种方法是使用协议。这需要关联的类(委托)符合方法/变量。“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
}
}
代理类:
//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
}
}
@ebby94是的,你可以,你可以等待完成处理程序中解锁的信号量,然后你可以返回结果(也在完成处理程序中设置)@Alexander不知道。我会调查的。感谢您提供的信息:)@Ra'salGhul
loginPostRequest
的completionHandler有什么问题?这种方法也是很好的,为什么您希望return@Alexander我有点困惑。你是说我可以从一个异步函数返回一个值吗?我知道我们可以在完成处理程序中获取值,但我不知道我们可以返回值。我试着搜索你说的话,但找不到相关的东西。任何关于您的意思的链接或解释都将不胜感激。@ebby94否,您使用完成处理程序分配给一个局部变量(由完成处理程序闭包捕获,但在同步函数的作用域中定义),然后在异步调用完成后(如信号量的信号所示)首先返回该变量,如果可能,使用同步方法是一个不好的建议。相反,尽可能多地使用异步方法。等待/轮询(阻止当前线程)比异步代码(通常调度到后台线程)更昂贵/效率更低。您正在谈论的URLSession
的同步方法是什么?我什么都不知道。@vadian我的意思是,如果你的目标是拥有同步行为,那么就使用同步方法。关于URLSession,我错了。这个目标通常是缺乏异步数据处理知识/理解的借口您好,请务必解释您的代码片段。为什么这样做有效?