Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 908
Truong Hieu Avatar asked Sep 12 '25 09:09

Truong Hieu


1 Answers

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

            }
like image 117
levous Avatar answered Sep 15 '25 00:09

levous