Is it possible to use async/await for POST HTTP requests in UIKit, namely a from UIButton
?
I can get the request sent but then it immediately crashes. Working perfectly using URLSession
, using this as a learning experience.
lazy var buttonTest: UIButton = {
let button = UIButton()
button.addTarget(self, action: #selector(testAsyncTwo), for: .touchUpInside)
return button
}()
@objc func testAsync() async throws {
let date = Date()
let df = DateFormatter()
df.dateFormat = "yyyy-MM-dd HH:mm:ss"
let dateString = df.string(from: date)
let json: [String: Any] = ["item": "1",
"time": "\(dateString)"]
let jsonData = try? JSONSerialization.data(withJSONObject: json)
guard let url = URL(string: "https://myapiendpoint.com/api/") else { fatalError("Missing URL") }
var urlRequest = URLRequest(url: url)
let token: String = "mySecretKey"
urlRequest.setValue("Token \(token)", forHTTPHeaderField: "Authorization")
urlRequest.httpMethod = "POST"
urlRequest.setValue("\(String(describing: jsonData?.count))", forHTTPHeaderField: "Content-Length")
urlRequest.setValue("application/json", forHTTPHeaderField: "Content-Type")
urlRequest.httpBody = jsonData
let (data, response) = try await URLSession.shared.data(for: urlRequest)
guard (response as? HTTPURLResponse)?.statusCode == 200 else { fatalError("Error while fetching data") }
let responseJSON = try? JSONSerialization.jsonObject(with: data, options: [])
if let responseJSON = responseJSON as? [String: Any] {
print(responseJSON) //Code after Successfull POST Request
}
}
Thanks very much!
CodePudding user response:
The crash is unrelated to your URLSession
code. The problem is that you have declared the button’s selector to be asynchronous throwing function. That is incompatible with an @objc
selector.
Replace:
@objc func testAsync() async throws {
...
}
With:
@objc func didTapButton(_ sender: Any) {
Task {
try await testAsync()
}
}
func testAsync() async throws {
...
}
The key observation is that the @objc
method is not an asynchronous function. I would also give the button handler a sender
parameter and a more descriptive name, to follow established conventions and make it clear at a glance that it is handling a button tap.
Obviously, when you do this, you will have to change the #selector
reference, too:
button.addTarget(self, action: #selector(didTapButton(_:)), for: .touchUpInside)