async await invalid conversion from throwing function

I'm complety lost with async await operation

I have a subclass of UITextField which provide a verif method in the resignFirstResponder method, I call the verif Method and return true or false, depending of the result of the verif

the verif method is asynchrone, because it can make call to a web server.

My code is:

import UIKit

public enum VerifResult<S: NSString> {
    case success
    case failure(S)
}

@IBDesignable class TextField: UITextField {

    @IBInspectable var obligatoire: Bool = false

    override func resignFirstResponder() -> Bool {
        var bRes = false
        do {
            let res = try await withCheckedThrowingContinuation {
                continuation in
                let result = try await verif()
                continuation.resume(returning: result)
            }
            switch res {
            case .success: bRes = true
            case .failure(let msg): bRes = false
            }
        } catch {
            return false
        }
        return bRes
    }

    public func verif() async throws -> VerifResult<NSString> {
        if obligatoire && text == "" {
            return .failure("Zone obligatoire")
        } else {
            // make call to a webserver and return .sucess or .failure
            return .success
        }
    }
}

in the resignFirstResponder on the line try await, the compiler detects the error:

Invalid conversion from throwing function of type '(CheckedContinuation<_, Error>) async throws -> Void' to non-throwing function type '(CheckedContinuation<T, Error>) -> Void'

I'm lost, I don't understand why where is my mistake?

Replies

Maybe I'm missing something - which is possible, because I am not a Swift expert, but...

resignFirstResponder is not async. You can only "await" inside async functions, right?

  • You're right, resignFirstesponder is not async, but I think that await withCheckedThrowingContinuation resolve the problem of non async functions calling async ones

Add a Comment

if I replace the function verif in a synchron function which calls a closure: like this

public func verif(_ completion: @escaping(VerifResult<NSString>) -> Void) {
        if obligatoire && text == "" {
            print("Echec")
            completion(.failure("Zone obligatoire"))
        } else {
            // make call to a webserver and return .sucess or .failure
            print("OK")
            completion(.success)
        }
    }

and I use DispatchGroup in the resignFirstResponder function, like this:

override func resignFirstResponder() -> Bool {

    var bRes = false
    let group = DispatchGroup()
    group.enter()
    print("group entered")
    verif({
        result in
        switch result {
        case .success: bRes = true
        case .failure:
            bRes = false
            print("Echec")
        }
        group.leave()
    })
    group.wait()
    print("Groupe libéré")
    return bRes
}

it seems to work: resignFirstResponder calls async functions and finish only when these functions are called. What do you think?

Fundamentally, are you expecting the UI to block while you do your server communication?

In this case yes, because the value return by resignFirstResponder depends on the call to the function verif()

OK. That may or may not work. It's certainly not best practice to block the UI thread while you do networking.

The documented place to validate UITextField content is the UITextFieldDelegate's textFieldShouldEndEditing method. But that is also a synchronous method called on the main thread, so it doesn't solve this problem.

I would try this:

  • Start the network request from shouldEndEditing, and return false.
  • When you get the response to the network request, if it's OK, call resignFirstResponder.
  • I think shouldEndEditing is now called again, and you need to return true if the content has not changed.

Or something like that. Don't block the main thread for networking.

Thank's, I will try this