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!
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)