Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

mathieu/feature/Add Firestore DatabaseOperations to AccountKit #677

Merged
Merged
11 changes: 11 additions & 0 deletions Modules/AccountKit/Sources/Database/DatabaseCollections.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Leka - iOS Monorepo
// Copyright APF France handicap
// SPDX-License-Identifier: Apache-2.0

import Foundation

public enum DatabaseCollection: String {
case rootAccounts
case caregivers
case carereceivers
}
12 changes: 12 additions & 0 deletions Modules/AccountKit/Sources/Database/DatabaseErrors.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Leka - iOS Monorepo
// Copyright APF France handicap
// SPDX-License-Identifier: Apache-2.0

import Foundation

public enum DatabaseError: Error {
case customError(String)
case documentNotFound
case decodeError
case encodeError
}
94 changes: 94 additions & 0 deletions Modules/AccountKit/Sources/Database/DatabaseOperations.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// Leka - iOS Monorepo
// Copyright APF France handicap
// SPDX-License-Identifier: Apache-2.0

import Combine
import FirebaseAuth
import FirebaseFirestore

public class DatabaseOperations {
// MARK: Lifecycle

public init() {
// Just to expose the init publicly
}

// MARK: Public

public static let shared = DatabaseOperations()

public func create(data: some AccountDocument, in collection: DatabaseCollection) -> AnyPublisher<String, Error> {
Future<String, Error> { promise in
var documentData = data
documentData.rootOwnerUid = Auth.auth().currentUser?.uid ?? ""

let docRef = self.database.collection(collection.rawValue).document()

do {
try docRef.setData(from: documentData) { error in
if let error {
log.error("\(error.localizedDescription)")
promise(.failure(DatabaseError.customError(error.localizedDescription)))
} else {
log.info("Document \(docRef.documentID) created successfully in \(collection). 🎉")
promise(.success(docRef.documentID))
}
}
} catch {
log.error("\(error.localizedDescription)")
promise(.failure(DatabaseError.encodeError))
}
}
.eraseToAnyPublisher()
}

public func read<T: AccountDocument>(from collection: DatabaseCollection, documentID: String) -> AnyPublisher<T, Error> {
Future<T, Error> { promise in
let docRef = self.database.collection(collection.rawValue).document(documentID)
docRef.getDocument { document, error in
if let error {
log.error("\(error.localizedDescription)")
promise(.failure(error))
} else {
do {
let object = try document?.data(as: T.self)
if let object {
log.info("Document \(String(describing: object.id)) fetched successfully. 🎉")
promise(.success(object))
} else {
log.error("Document not found.")
promise(.failure(DatabaseError.documentNotFound))
}
} catch {
log.error("\(error.localizedDescription)")
promise(.failure(error))
}
}
}
}
.eraseToAnyPublisher()
}

public func readAll<T: AccountDocument>(from collection: DatabaseCollection) -> AnyPublisher<[T], Error> {
Future<[T], Error> { promise in
self.database.collection(collection.rawValue)
.getDocuments { querySnapshot, error in
if let error {
log.error("\(error.localizedDescription)")
promise(.failure(error))
} else {
let objects = querySnapshot?.documents.compactMap { document -> T? in
try? document.data(as: T.self)
} ?? []
log.info("\(String(describing: objects.count)) documents fetched successfully. 🎉")
promise(.success(objects))
}
}
}
.eraseToAnyPublisher()
}

// MARK: Private

private let database = Firestore.firestore()
}
12 changes: 12 additions & 0 deletions Modules/AccountKit/Sources/Extensions/JSONDecoderExtension.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Leka - iOS Monorepo
// Copyright APF France handicap
// SPDX-License-Identifier: Apache-2.0

import Foundation

extension JSONDecoder {
func decode<T: Decodable>(_: T.Type, fromJSONObject object: Any) throws -> T {
let data = try JSONSerialization.data(withJSONObject: object)
return try self.decode(T.self, from: data)
}
}
25 changes: 21 additions & 4 deletions Modules/AccountKit/Sources/Models/RootAccount.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,30 @@
// Copyright APF France handicap
// SPDX-License-Identifier: Apache-2.0

import FirebaseFirestore
import SwiftUI

// MARK: - AccountDocument

public protocol AccountDocument: Codable, Identifiable {
var id: String? { get set }
var rootOwnerUid: String { get set }
var createdAt: Date? { get set }
var lastEditedAt: Date? { get set }
}

// MARK: - RootAccount

struct RootAccount: Identifiable, Codable {
var id: String
struct RootAccount: AccountDocument {
enum CodingKeys: String, CodingKey {
case id
case rootOwnerUid = "root_owner_uid"
case createdAt = "created_at"
case lastEditedAt = "last_edited_at"
}

@DocumentID var id: String?
var rootOwnerUid: String
var createdAt: Date
var lastEditedAt: Date
@ServerTimestamp var createdAt: Date?
@ServerTimestamp var lastEditedAt: Date?
}
Loading