Harry T.
Harry T.

Reputation: 3527

Correct way to insert/update Codable struct document/array to Firestore

My Firestore document structure, i created it via Android:

--- Document
----- testA (object)
----- testB (array)
------- 0: Device item 1
------- 1: Device item 2

I have following struct:

import Foundation
public struct Device: Codable {
    var manufacturer: String?
    var model: String?
    var osVersion: String?

    enum CodingKeys: String, CodingKey {
        case manufacturer
        case model
        case osVersion
    }
}

My device object

let currentDevice = Device(manufacturer: "a", model: "b", osVersion: "c")

Test A: Update Device to Document. I tried below code:

db.collection("testCollection").document("testDoc").updateData([testA: currentDevice])
...

Test B: Add Device to testB array.

db.collection("testCollection").document("testDoc")
.updateData([testA: FieldValue.arrayUnion([currentDevice])])

It all cause below error: Terminating app due to uncaught exception 'FIRInvalidArgumentException', reason: 'Unsupported type: __SwiftValue'

I also checked official document but it does not show anyway to update struct object to Firestore. What can i do? Thanks.

Upvotes: 4

Views: 1247

Answers (2)

levous
levous

Reputation: 932

Hopefully this helps somebody out.

First, in the original example above, testA is not quoted. The field key is a string. It should be "testA"

In order to save an item to an array on a document ref, using updateData, swift structs/classes need to be encoded into a dictionary. FirebaseFirestoreSwift helps with this by providing a specialized encoder. Otherwise, you can manually convert from your struct into the expected [String: Any] dictionary.

This example uses FirebaseFirestoreSwift

        let currentDevice = Device(
             manufacturer: "a", 
             model: "b", 
             osVersion: "c"
        )
        // declare outside of the try/catch block
        let encoded: [String: Any]
        do {
            // encode the swift struct instance into a dictionary
            // using the Firestore encoder
            encoded = try Firestore.Encoder().encode(currentDevice)
        } catch {
            // encoding error
            handleError(error)
            return
        }

        let fieldKey = "testA"

        // add a new item to the "testA" array.
        db.collection("testCollection").document("testDoc")
            .updateData(
                [
                    fieldKey: FieldValue.arrayUnion([encoded])
                ]
            ) { error in
                guard let error = error else {
                    // no error, that's great
                    return
                }

                // uh oh, firestore error
                handleError(error)
                return

            }

Upvotes: 5

gso_gabriel
gso_gabriel

Reputation: 4660

Usually, this error is related to the fact that Firestore is not able to serialise different types of variables and arrays at the same time - in your case the problem seems related to the currentDevice variable, that Firestore is not being able to identify.

It seems that you will need to modify the below part, for your application to be able to insert the data.

...  
enum CodingKeys: String, CodingKey {
            case manufacturer
            case model
            case osVersion
        }
...

Since you are not using one "variable" to add the information for the manufacturer, model and osVersion. I would recommend you to check this question from the Community here, to get a possible solution - adapting to your case - of how to convert your data for Firestore to be able to understand.

Let me know if the information helped you!

Upvotes: 0

Related Questions