A generic based abstraction layer on top of Firestore.
- Firebase/Firestore: a scalable NoSQL cloud database to store and sync data for client and server-side development.
- Google/Promises: a modern framework that provides a synchronization construct for Swift to facilitate writing asynchronous code.
- Identity: a small library that makes it easy to create type-safe identifiers in Swift.
- Support Codable.
- Support Promises.
- Provide easy to use read and write operations.
- KeyPath based query builder.
- Type-safe identifiers.
Your model must be conformed to Entity
protocol which conformed to Codable
.
For example:
struct Book: Entity {
var id: ID = ""
var title: String
var author: String
var releaseDate: Date?
var pages: Int
}
Conform to QueryKey
protocol to enable KeyPath query.
extension Book: QueryKey {
static var keys: [PartialKeyPath<Book>: CodingKey] {
return [
\Self.id: CodingKeys.id,
\Self.title: CodingKeys.title,
\Self.author: CodingKeys.author,
\Self.releaseDate: CodingKeys.releaseDate,
\Self.pages: CodingKeys.pages
]
}
}
The usecase is a protocol which do one specific thing.
protocol BooksUseCase {
func loadBooks() -> Promise<[Book]>
func loadBook(byID id: Book.ID) -> Promise<Book>
func saveBook(_ book: Book) -> Promise<Void>
func updateBook(_ book: Book) -> Promise<Void>
func deleteBook(byID id: Book.ID) -> Promise<Void>
}
Concrete implmentation of the BooksUseCase
.
final class DefaultBooksUseCase<Repository: AbstractRepository> where Repository.Value == Book {
private let repository: Repository
init(repository: Repository) {
self.repository = repository
}
}
extension DefaultBooksUseCase: BooksUseCase {
func loadBooks() -> Promise<[Book]> {
return repository.query {
$0.filter(by: \.author, equal: "George R. R. Martin")
.order(by: \.releaseDate)
.limit(to: 5)
}
}
func loadBook(byID id: Book.ID) -> Promise<Book> {
return repository.fetch(byID: id)
}
func saveBook(_ book: Book) -> Promise<Void> {
return repository.save(entity: book)
}
func updateBook(_ book: Book) -> Promise<Void> {
return repository.update(entity: book)
}
func deleteBook(byID id: Book.ID) -> Promise<Void> {
return repository.delete(byID: id)
}
}
It helps to hide the concrete implementation of use case.
protocol UseCaseFactory {
func makeBooksUseCase() -> BooksUseCase
}
final class DefaultUseCaseFactory: UseCaseFactory {
func makeBooksUseCase() -> BooksUseCase {
let path = Path("books")
let repository = FirestoreRepository<Book>(path: path)
return DefaultBooksUseCase(repository: repository)
}
}
CocoaPods is a dependency manager for Cocoa projects. For usage and installation instructions, visit their website. To integrate FirestoreClient into your Xcode project using CocoaPods, specify it in your Podfile
:
pod 'FirestoreClient'
Then, run the following command:
$ pod install
Anas Alhasani