Created
August 1, 2024 06:03
-
-
Save bannzai/7e0481a435f74a19ec1c23e9bcbae10f to your computer and use it in GitHub Desktop.
Document.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Foundation | |
import FirebaseFirestore | |
import FirebaseFirestoreSwift | |
protocol SetIdentifiable: Identifiable { | |
mutating func set(id: ID) | |
} | |
typealias AnyEntity = Codable & Sendable & Hashable & Identifiable & SetIdentifiable | |
// onCall等でEntityを返す際にDocumentIDで以下のエラーになる。そのためonCallでのレスポンスはEntityを使用して、Firestoreからのデータ取得はDocument<Entity>を使用する | |
// - decodingIsNotSupported : "Could not find DocumentReference for user info key: CodingUserInfoKey(rawValue: \"DocumentRefUserInfoKey\").\nDocumentID values can only be decoded with Firestore.Decoder" | |
@dynamicMemberLookup | |
struct Document<Entity: AnyEntity>: Hashable, Identifiable, Sendable { | |
@DocumentID var id: Identifier<Entity>? | |
var entity: Entity | |
@ClientCreatedTimestamp var createdDateTime: Date? | |
@ClientUpdatedTimestamp var updatedDateTime: Date? | |
@ServerCreatedTimestamp var serverCreatedDateTime: Date? | |
@ServerUpdatedTimestamp var serverUpdatedDateTime: Date? | |
enum TimestampCodingKeys: CodingKey { | |
case createdDateTime | |
case updatedDateTime | |
case serverCreatedDateTime | |
case serverUpdatedDateTime | |
} | |
subscript <V>(dynamicMember keyPath: WritableKeyPath<Entity, V>) -> V { | |
get { | |
entity[keyPath: keyPath] | |
} | |
set { | |
entity[keyPath: keyPath] = newValue | |
} | |
} | |
subscript <V>(dynamicMember keyPath: KeyPath<Entity, V>) -> V { | |
entity[keyPath: keyPath] | |
} | |
} | |
extension Document: Codable where Entity.ID == Self.ID { | |
init(from decoder: Decoder) throws { | |
let singleValueContainer = try decoder.singleValueContainer() | |
_id = try singleValueContainer.decode(DocumentID<Identifier<Entity>>.self) | |
entity = try singleValueContainer.decode(Entity.self) | |
entity.set(id: id) | |
let timestamps = try decoder.container(keyedBy: TimestampCodingKeys.self) | |
_createdDateTime = try timestamps.decodeIfPresent(ClientCreatedTimestamp.self, forKey: .createdDateTime) ?? .init(wrappedValue: nil) | |
_updatedDateTime = try timestamps.decodeIfPresent(ClientUpdatedTimestamp.self, forKey: .updatedDateTime) ?? .init(wrappedValue: nil) | |
_serverCreatedDateTime = try timestamps.decodeIfPresent(ServerCreatedTimestamp.self, forKey: .serverCreatedDateTime) ?? .init(wrappedValue: nil) | |
_serverUpdatedDateTime = try timestamps.decodeIfPresent(ServerUpdatedTimestamp.self, forKey: .serverUpdatedDateTime) ?? .init(wrappedValue: nil) | |
} | |
func encode(to encoder: Encoder) throws { | |
var container = encoder.singleValueContainer() | |
try container.encode(entity) | |
var timestamps = encoder.container(keyedBy: TimestampCodingKeys.self) | |
try timestamps.encode(createdDateTime, forKey: .createdDateTime) | |
try timestamps.encode(updatedDateTime, forKey: .updatedDateTime) | |
try timestamps.encode(serverCreatedDateTime, forKey: .serverCreatedDateTime) | |
try timestamps.encode(serverUpdatedDateTime, forKey: .serverUpdatedDateTime) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
FirestoreのDocumentと、onCallでDocumentを返却するときにTimestampの情報がonCallの場合は欠落するので(FirestoreのSDKを通さないレスポンスだから)、頑張って共通化した。上手くいったけど、型宣言の時に都度 Document という宣言になるのとうっかりすると同じCollectionでDocumentアリ・ナシの別の型で.setとかしちゃいそうなので供養