-5

I am trying to get the last AppVersion dokument out from the database!

What am I doing wrong here?

func getLastAppVertion() async throws -> ApplicationVersion {
            firebase.collection("ApplicationVersion")
                    .order(by: "major")
                    .order(by: "minor")
                    .order(by: "patch")
                    .limit(to: 1)
                    .getDocuments { (querySnapshot, error) in
                        if let error = error {
                            throw AppError.networkerror
                        } else {
                            for document in querySnapshot!.documents {
                                let major = document.data()["major"] as? Int ?? 7
                                let minor = document.data()["minor"] as? Int ?? 15
                                let patch = document.data()["patch"] as? Int ?? 0
                                let sendAppVersion = ApplicationVersion(major: major,
                                                                        minor: minor,
                                                                        patch: patch,
                                                                        device: .iPhone)
                                return sendAppVersion
                        }
                    }
                }
    }
Phil Dukhov
  • 67,741
  • 15
  • 184
  • 220
Turbojens
  • 9
  • 3

2 Answers2

1

You're mixing an old asynchronous call with new concurrency.

You need to convert it using withUnsafeThrowingContinuation, something like this:

func getLastAppVertion() async throws -> Float {
    try withUnsafeThrowingContinuation { continuation in
        firebase.collection("ApplicationVersion")
            .order(by: "major")
            .order(by: "minor")
            .order(by: "patch")
            .limit(to: 1)
            .getDocuments { (querySnapshot, error) in
                if let error = error {
                    continuation.resume(throwing: AppError.networkerror)
                    return
                } else {
                    for document in querySnapshot!.documents {
                        let major = document.data()["major"] as? Int ?? 7
                        let minor = document.data()["minor"] as? Int ?? 15
                        let patch = document.data()["patch"] as? Int ?? 0
                        let sendAppVersion = ApplicationVersion(major: major,
                                                                minor: minor,
                                                                patch: patch,
                                                                device: .iPhone)
                        continuation.resume(returning: 1)
                        // not sure why you're using a for loop and returning the first value here
                        return
                    }
                }
            }
    }
}

I suggest you start with Swift concurrency: Update a sample app and other WWDC talks on Swift concurrency to understand how to work with it.

Phil Dukhov
  • 67,741
  • 15
  • 184
  • 220
  • Not withCheckedThrowingContinuation. That is only for debugging. Use withUncheckedThrowingContinuation. – matt Sep 19 '21 at 10:35
  • @matt didn't know the difference and `unsafe` sounds kind of not the best solution just by it's name=) Thank you, checked out on the difference and now I agree that `unsafe` variant should be used – Phil Dukhov Sep 19 '21 at 11:14
  • @matt I thought it's fine to use it in production. I know there is a slight performance hit when using checked vs unchecked, but I thought it was still fine in a real app. – George Sep 20 '21 at 20:50
  • @george it’s more a question of wether you want your app to crash on unexpected second continuation call, or just to ignore this error. I prefer a crash because most probably you’ll face it during testing and fix it, or at least you’ll have a crash log in TF, instead of unexpected behavior when you’re waiting for a different result and not receiving it – Phil Dukhov Sep 20 '21 at 21:02
  • @PhilipDukhov Which is exactly why I would choose the checked version. It crashes when you get an unexpected number of resumes, so IMO is better than unchecked. If you can be certain, then unchecked is fine – George Sep 20 '21 at 21:08
0

You are calling getDocuments with a completion handler.

You cannot mix Swift structured concurrency async/await directly with getDocuments / completion handler. async/await and completion handler are opposites.

Everything about your completion handler is wrong. You cannot throw from a completion handler. You cannot return anything from a completion handler. That's the whole point of async/await. That is why async/await supersedes completion handlers.

To wrap async/await around a completion handler call, you must use withUnsafeThrowingContinuation.

matt
  • 515,959
  • 87
  • 875
  • 1,141
  • Look at the "wrapping a completion handler" section of my intro article https://www.biteinteractive.com/swift-5-5-asynchronous-looping-with-async-await/ for more info. – matt Sep 19 '21 at 11:41
  • I am a bit blank here, can you please show it in code please... This async/await is a little new to me :-) – Turbojens Sep 19 '21 at 14:44
  • I don't propose to write your code for you. My articles teach you about async/await. Read them. The full code is already given in the other answer. – matt Sep 19 '21 at 14:59