M1X
M1X

Reputation: 5354

Swift decode httpsCallable cloud functions response

I have a httpsCallable cloud function which is being called from the app. Technically speaking it is working fine but as you can see, the code is very nested and I have the feeling that this can be handled somehow better. I also get a warning.

The response result?.data is of type Any so I can not use it directly in try decoder.decode(PaymentIntent.self, from: data) since this requires Data type. And for this I'm using try? JSONSerialization.data(withJSONObject: result?.data)

Any idea how to decode everything to PaymentIntent model?

let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase

functions.httpsCallable("createPaymentIntent").call(data) { (result, error) in
    if let error = error as NSError? {
        completion(nil, error)
    } else {
        let data = try? JSONSerialization.data(withJSONObject: result?.data)
        
        if let data = data {
            do {
                let paymentIntent = try decoder.decode(PaymentIntent.self, from: data) // Expression implicitly coerced from 'Any?' to 'Any'
                completion(paymentIntent.clientSecret, nil)
            } catch {
                completion(nil, error);
            }
        }

        // Update

        if let data = result?.data as? NSDictionary {
            print(data)
            
            do {
                // Cannot convert value of type 'NSDictionary' to expected argument type 'Data'
                let paymentIntent = try decoder.decode(PaymentIntent.self, from: data)
                completion(paymentIntent.clientSecret, nil)
            } catch {
                completion(nil, error);
            }
        }
    
    }
}

Upvotes: 4

Views: 1226

Answers (3)

Calin Drule
Calin Drule

Reputation: 2949

Firebase added decoders meanwhile:

import FirebaseSharedSwift
import FirebaseFunctions

    ...
func retrieve<T: Decodable>(_ strategy: JSONDecoder.DateDecodingStrategy? = nil) async throws -> T {
    let decoder = FirebaseDataDecoder()
    decoder.keyDecodingStrategy = .convertFromSnakeCase
    
    let result = try await functions
        .httpsCallable(endpoint, responseAs: T.self, decoder: decoder)
        .call(params)
    return result
}

Replace T with your class if your method is not generic and params should be Encodable.

Upvotes: 7

Joris
Joris

Reputation: 6296

You can't do this right now. You'll always get a Dictionary back and you'll need to instantiate the objects yourself using the contents of that Dictionary.

However they are working on it:

https://github.com/firebase/firebase-ios-sdk/pull/8854

Upvotes: 0

Doug Stevenson
Doug Stevenson

Reputation: 317487

The swift SDK for Firebase's callable functions does not return a JSON string. It returns a data type that's already been deserialized from JSON. It's going to be either a dictionary, array, or something that directly corresponds to whatever the cloud function generates. You should do some step-through debugging to examine what result.data actually is, then write some code to verify that it is what you expect, and cast it in order to use it safely.

Upvotes: 1

Related Questions