Сохранение модели Firestore с @DocumentID в UserDefaults в Swift

Я пытаюсь сохранить модель firebase по умолчанию, но кодирование/декодирование поля id не выполняется.

struct DeviceModel: Identifiable, Codable, Hashable {
    
    @DocumentID var id: String?
    var fcmToken: String
    var userId: String
    @ServerTimestamp var expiresOn: Timestamp?
    
    enum CodingKeys: String, CodingKey {
        case id
        case fcmToken = "token"
        case expiresOn = "expires_on"
        case userId = "user_id"
    }
    
    init(id: String? = nil,
         fcmToken: String,
         userId: String,
         expiresOn: Timestamp? = Timestamp()) {
        self.id = id
        self.fcmToken = fcmToken
        self.userId = userId
        self.expiresOn = expiresOn
    }
    
}

Расширение UserDefault

extension UserDefaults {
    func decode<T: Decodable>(_ type: T.Type, forKey defaultName: String) throws -> T {
//        try! JSONDecoder().decode(T.self, from: data(forKey: defaultName) ?? .init())
        try! Firestore.Decoder().decode(T.self, from: data(forKey: defaultName) ?? .init())
    }
    func encode<T: Encodable>(_ value: T, forKey defaultName: String) throws {
//        try! set(JSONEncoder().encode(value), forKey: defaultName)
        try! set(Firestore.Encoder().encode(value), forKey: defaultName)
    }
}

Сначала я попытался использовать JSONEncoder и получил сообщение об ошибке, говорящее о том, что @DocumentID должен быть закодирован/декодирован с использованием кодировщика/декодера firestore.

Fatal error: 'try!' expression unexpectedly raised an error: 
FirebaseFirestoreSwift.FirestoreEncodingError.encodingIsNotSupported("DocumentID values can only be encoded with Firestore.Encoder")

После этого я использую их, как указано выше, но теперь я получаю

2023-04-18 10:52:19.298750+0300 Voxic[49960:1461668] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Attempt to insert non-property list object {
    "expires_on" = "<FIRTimestamp: seconds=1685555885 nanoseconds=220984000>";
    token = "<token>";
    "user_id" = 41giBVERER34QNPFZmlB3;
} for key localDeviceKey'

Одна вещь, которую я нахожу странной, заключается в том, что user_id не печатается в виде строки в приведенной выше ошибке. Хотя, возможно, это просто проблема с печатью.

Есть предположения? Спасибо

я нашел похожий вопрос, пожалуйста, проверьте решение здесь: stackoverflow.com/a/61432255/16322682

Jatin 18.04.2023 10:45

@ Джатин, спасибо, но это не тот сценарий. Я уже использую DocumentID в своем проекте. и кодирование/декодирование в/из firebase работает.

ALex 18.04.2023 11:52
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
2
85
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Попробуйте использовать пользовательскую функцию кодирования и пользовательскую функцию инициализации (от :)

  • Пользовательская функция кодирования, которая кодирует свойство DocumentID с помощью Firestore.Encoder
  • Пользовательская функция init(from:), которая декодирует свойство DocumentID с помощью Firestore.Decoder
struct DeviceModel: Identifiable, Codable, Hashable {
    @DocumentID var id: String?
    var fcmToken: String
    var userId: String
    @ServerTimestamp var expiresOn: Timestamp?
    
    enum CodingKeys: String, CodingKey {
        case id
        case fcmToken = "token"
        case expiresOn = "expires_on"
        case userId = "user_id"
    }
    
    init(id: String? = nil,
         fcmToken: String,
         userId: String,
         expiresOn: Timestamp? = Timestamp()) {
        self.id = id
        self.fcmToken = fcmToken
        self.userId = userId
        self.expiresOn = expiresOn
    }
    
    // Custom init(from:) function that decodes the id property using the Firestore.Decoder
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        fcmToken = try container.decode(String.self, forKey: .fcmToken)
        userId = try container.decode(String.self, forKey: .userId)
        expiresOn = try container.decode(Timestamp?.self, forKey: .expiresOn)
        
        // Decode the DocumentID property using the Firestore.Decoder
        if let idContainer = try? decoder.container(keyedBy: CodingKeys.self),
           let id = try? idContainer.decode(String.self, forKey: .id) {
            self.id = id
        }
    }
    
    // Custom encode function that encodes the DocumentID property using the Firestore.Encoder
    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(fcmToken, forKey: .fcmToken)
        try container.encode(userId, forKey: .userId)
        try container.encode(expiresOn, forKey: .expiresOn)
        
        // Encode the DocumentID property using the Firestore.Encoder
        if let id = id {
            let firestoreEncoder = Firestore.Encoder()
            let something = try firestoreEncoder.encode(["id":id])
            
            if let mapId = something["id"] as? String {
                try container.encode(mapId, forKey: .id)
            }
        }
    }
}

Другие вопросы по теме