我正在尝试创建我的第一个应用程序,并使代码尽可能模块化和可重用。因此,我尝试创建一个类,允许我发出简单的HTTP GET请求并返回结果文本,如果需要,还可以选择返回JSON。我在下面粘贴了我的代码。我得到的问题是我的函数在HTTP调用完成之前返回,因此返回一个空对象。我想我知道为什么会这样,因为我在正在执行的任务上调用resume并等待调用回调函数 - 但同时代码执行仍在继续,因此我返回一个空对象。那么......这里推荐的方法是什么,拜托?我应该将回调放回到我的原始视图控制器上的函数,而不是尝试以这种方式创建可重用的类,或其他东西!我张开双臂等待任何建议;)
P.S。这些代码中的一些可能看起来很熟悉,因为很多内容来自我在线阅读的其他文章/帖子,然后根据我的需要量身定制。
class WebController {
func makeGetCall(strURL:String, returnJSON:Bool) -> WebControllerResponse {
//declare a var to store our return
let wcr:WebControllerResponse = WebControllerResponse()
// Set up the URL request
guard let url = URL(string: strURL) else {
wcr.successful = false
wcr.returnData = "Error: cannot create URL"
return wcr
}
let urlRequest = URLRequest(url: url)
// set up the session
let config = URLSessionConfiguration.default
let session = URLSession(configuration: config)
// make the request
session.dataTask(with: urlRequest, completionHandler: {
(data, response, error) in
// check for any errors
guard error == nil else {
wcr.successful = false
wcr.returnData = "error calling GET - " + String(describing: error)
return
}
// make sure we got data
guard let responseData = data else {
wcr.successful = false
wcr.returnData = "Error: did not receive data"
return
}
if (returnJSON == true) {
// parse the result as JSON, since that's what the API provides
do {
guard let retObj = try JSONSerialization.jsonObject(with: responseData, options: []) as? [String: AnyObject] else {
wcr.successful = false
wcr.returnData = "error trying to convert data to JSON"
return
}
wcr.returnJSON = retObj
wcr.returnData = String(describing: responseData)
wcr.successful = true
}
catch {
wcr.successful = false
wcr.returnData = "error trying to convert data to JSON"
return
}
}
else {
// just return raw data
wcr.returnData = String(describing: responseData)
wcr.successful = true
return
}
}).resume()
return wcr
}
}
我的回复对象非常简单:
class WebControllerResponse {
var successful:Bool = false
var returnData:String = ""
var returnJSON:[String:AnyObject]?
init() {
}
}
以下是根据@dirtydanee的反馈更新的代码:错误:
类CalTest:UIViewController,WebControllerDelegate {
@IBOutlet weak var bigText: UITextView!
@IBOutlet var urlIn: UITextField!
@IBOutlet weak var testLabel: UILabel!
@IBAction func getButton(_ sender: Any) {
let wc:WebController = WebController()
wc.delegate = self
wc.makeGetCall(strURL: urlIn.text!, returnJSON:false)
}
func webController(webController: WebController, didReceiveResponse: WebControllerResponse) {
bigText.text = didReceiveResponse.returnData
}
}
答案 0 :(得分:1)
URLSession
dataTask
是一个异步API,因此您需要从完成块返回结果,而不是之后。
当你调用dataTask完成处理程序时,你需要从WebController
创建一个委托来通知观察类。
// Create the protocol
protocol WebControllerDelegate: class {
func webController(webController: WebController, didReceiveResponse: WebControllerResponse)
}
class WebController {
// Add delegate variable to class
weak var delegate: WebControllerDelegate?
// Initaite the call, but do not return anything from the function directly
func makeGetCall(strURL:String, returnJSON:Bool) {
//declare a var to store our return
let wcr:WebControllerResponse = WebControllerResponse()
// Set up the URL request
guard let url = URL(string: strURL) else {
wcr.successful = false
wcr.returnData = "Error: cannot create URL"
self.delegate?.webController(webController: self, didReceiveResponse: wcr)
return
}
let urlRequest = URLRequest(url: url)
// set up the session
let config = URLSessionConfiguration.default
let session = URLSession(configuration: config)
// make the request
session.dataTask(with: urlRequest, completionHandler: {
(data, response, error) in
// check for any errors
guard error == nil else {
wcr.successful = false
wcr.returnData = "error calling GET - " + String(describing: error)
// Add the delegate call to the right places
self.delegate?.webController(webController: self, didReceiveResponse: wcr)
return
}
// make sure we got data
guard let responseData = data else {
wcr.successful = false
wcr.returnData = "Error: did not receive data"
return
}
if (returnJSON == true) {
// parse the result as JSON, since that's what the API provides
do {
guard let retObj = try JSONSerialization.jsonObject(with: responseData, options: []) as? [String: AnyObject] else {
wcr.successful = false
wcr.returnData = "error trying to convert data to JSON"
self.delegate?.webController(webController: self, didReceiveResponse: wcr)
return
}
wcr.returnJSON = retObj
wcr.returnData = String(describing: responseData)
wcr.successful = true
self.delegate?.webController(webController: self, didReceiveResponse: wcr)
}
catch {
wcr.successful = false
wcr.returnData = "error trying to convert data to JSON"
self.delegate?.webController(webController: self, didReceiveResponse: wcr)
return
}
}
else {
// just return raw data
wcr.returnData = String(describing: responseData)
wcr.successful = true
self.delegate?.webController(webController: self, didReceiveResponse: wcr)
return
}
}).resume()
}
}
<强>用法:强>
class Foo: WebControllerDelegate {
let webController = WebController()
func testWebController() {
// Assign delegate to self
webController.delegate = self
webController.makeGetCall(strURL: "whatever your URL should be", returnJSON: true)
}
// Declare the delegate
func webController(webController: WebController, didReceiveResponse: WebControllerResponse) {
//转到主UI线程 DispatchQueue.main.sync { 将文本分配给UITextView bigText.text = didReceiveResponse.returnData } } }
答案 1 :(得分:1)
因为HTTP请求是异步发生的,所以函数将在完成之前返回。
解决此问题的最简单方法是在函数上使用完成块:
func makeGetCall(strURL:String, returnJSON:Bool completion: @escaping (WebControllerResponse?) -> Void) {
// ...
}
然后,当HTTP请求完成后,您可以调用完成处理程序
completion(webControllerResponse)