All Projects → 1amageek → Ballcap Ios

1amageek / Ballcap Ios

Licence: mit
Firebase Cloud Firestore support library for iOS. 🧢

Programming Languages

swift
15916 projects

Projects that are alternatives of or similar to Ballcap Ios

Ionic Firebase Starter App
Ionic 4 firebase CRUD tutorial to learn how to create a firebase application to perform Authentication and all CRUD operations in an ionic 4 application. You can use this FREE Ionic Firebase Starter App as a base to create your Ionic App with Firebase backend :)
Stars: ✭ 77 (-64.52%)
Mutual labels:  firebase, firebase-storage
Hify
Social network powered by firebase
Stars: ✭ 115 (-47%)
Mutual labels:  firebase, firebase-storage
Unity Solutions
Use Firebase tools to incorporate common features into your games!
Stars: ✭ 95 (-56.22%)
Mutual labels:  gcp, firebase
Firebaserealtimechat
Sample real-time chat application using Firebase
Stars: ✭ 60 (-72.35%)
Mutual labels:  firebase, firebase-storage
Combinefirebase
Combine wrapper on Google's iOS Firebase library.
Stars: ✭ 126 (-41.94%)
Mutual labels:  firebase, firebase-storage
Firetable
Excel/Google Sheets like UI for Firebase/Firestore. No more admin portals!
Stars: ✭ 1,115 (+413.82%)
Mutual labels:  gcp, firebase
Firebase Php
Unofficial Firebase Admin SDK for PHP
Stars: ✭ 1,657 (+663.59%)
Mutual labels:  firebase, firebase-storage
Thenetwork Open
TheNetwork is a blog cum chat app. It's completely built using firebase. Users can post, comment, like and bookmark the blogs, also users can send follow requests to connect with people. Users can create events and also prepare an event roadmap. Pagination for realtime data is also included in chats, blogs and events.
Stars: ✭ 17 (-92.17%)
Mutual labels:  firebase, firebase-storage
Photoblog Android Blog App
Stars: ✭ 124 (-42.86%)
Mutual labels:  firebase, firebase-storage
Quickstart Cpp
Firebase Quickstart Samples for C++
Stars: ✭ 123 (-43.32%)
Mutual labels:  firebase, firebase-storage
Firebase As3
Integrate Firebase Auth, Realtime Database and Storage in your Adobe AIR projects.
Stars: ✭ 55 (-74.65%)
Mutual labels:  firebase, firebase-storage
Tanam
Plug-n-play CMS for websites on Firebase
Stars: ✭ 139 (-35.94%)
Mutual labels:  firebase, firebase-storage
Whatsup
**Deprecated** Real time chat app written in Swift 4 using Firebase and OTP Authentication
Stars: ✭ 39 (-82.03%)
Mutual labels:  firebase, firebase-storage
Vsfire
Visual Studio Code extension for syntax highlighting, hover help and code completions with Firestore security rules and index definition files
Stars: ✭ 68 (-68.66%)
Mutual labels:  firebase, firebase-storage
Fsfirestore
Functional F# library to access Firestore database hosted on Google Cloud Platform (GCP) or Firebase.
Stars: ✭ 22 (-89.86%)
Mutual labels:  gcp, firebase
React Native Firebase Chat
React Native chat application using firebase.
Stars: ✭ 113 (-47.93%)
Mutual labels:  firebase, firebase-storage
Rxfirebase
Rxjava 2.0 wrapper on Google's Android Firebase library.
Stars: ✭ 509 (+134.56%)
Mutual labels:  firebase, firebase-storage
Quickstart Unity
Firebase Quickstart Samples for Unity
Stars: ✭ 553 (+154.84%)
Mutual labels:  firebase, firebase-storage
Firebase Android Sdk
Firebase Android SDK
Stars: ✭ 1,704 (+685.25%)
Mutual labels:  firebase, firebase-storage
React Firebase Hooks
React Hooks for Firebase.
Stars: ✭ 2,227 (+926.27%)
Mutual labels:  firebase, firebase-storage

🧢 Ballcap-iOS

Version Platform Downloads

Ballcap is a database schema design framework for Cloud Firestore.

Why Ballcap

Cloud Firestore is a great schema-less and flexible database that can handle data. However, its flexibility can create many bugs in development. Ballcap can assign schemas to Cloud Firestore to visualize data structures. This plays a very important role when developing as a team.

Inspired by https://github.com/firebase/firebase-ios-sdk/tree/pb-codable3

Please donate to continue development.

Sameple projects

Feature

☑️ Firestore's document schema with Swift Codable
☑️ Of course type safety.
☑️ It seamlessly works with Firestore and Storage.

Requirements ❗️

Installation ⚙

CocoaPods

  • Insert pod 'Ballcap' to your Podfile.
  • Run pod install.

If you have a Feature Request, please post an issue.

Usage

Document scheme

You must conform to the Codable and Modelable protocols to define Scheme.

struct Model: Codable, Equatable, Modelable {
    var number: Int = 0
    var string: String = "Ballcap"
}

Initialization

The document is initialized as follows:

// use auto id
let document: Document<Model> = Document()

print(document.data?.number) // 0
print(document.data?.string) // "Ballcap"

// KeyPath
print(document[\.number]) // 0
print(document[\.string]) // "Ballcap"


// Use your specified ID
let document: Document<Model> = Document(id: "YOUR_ID")

print(document.data?.number) // 0
print(document.data?.string) // "Ballcap"

// KeyPath
print(document[\.number]) // 0
print(document[\.string]) // "Ballcap"


// Use your specified DocumentReference
let documentReference: DocumentReference = Firestore.firestore().document("collection/document")
// note: If DocumentReference is specified, data is initialized with nil. 
let document: Document<Model> = Document(documentReference) 

print(document.data?.number) // nil
print(document.data?.string) // nil

// KeyPath
print(document[\.number]) // fail
print(document[\.string]) // fail

CRUD

Ballcap has a cache internally.When using the cache, use Batch instead of WriteBatch.

// save
document.save()

// update
document.update()

// delete
document.delete()

// Batch
let batch: Batch = Batch()
batch.save(document: document)
batch.update(document: document)
batch.delete(document: document)
batch.commit()

You can get data by using the get function.

Document<Model>.get(id: "DOCUMENT_ID", completion: { (document, error) in
    print(document.data)
})

The next get function gets data in favor of the cache. If there is no cached data, it gets from the server.

let document: Document<Model> = Document("DOCUMENT_ID")
document.get { (document, error) in
   print(document.data)
}

Why data is optional?

In CloudFirestore, DocumentReference does not necessarily have data. There are cases where there is no data under the following conditions.

  1. If no data is stored in DocumentReference.
  2. If data was acquired using Source.cache from DocumentReference, but there is no data in cache.

Ballcap recommends that developers unwrap if they can determine that there is data.

It is also possible to access the cache without using the network.

let document: Document<Model> = Document(id: "DOCUMENT_ID")
print(document.cache?.number) // 0
print(document.cache?.string) // "Ballcap"

Custom properties

Ballcap is preparing custom property to correspond to FieldValue.

ServerTimestamp

Property for handling FieldValue.serverTimestamp()

struct Model: Codable, Equatable {
    let serverValue: ServerTimestamp
    let localValue: ServerTimestamp
}
let model = Model(serverValue: .pending,
                  localValue: .resolved(Timestamp(seconds: 0, nanoseconds: 0)))

IncrementableInt & IncrementableDouble

Property for handling FieldValue.increment()

struct Model: Codable, Equatable, Modelable {
    var num: IncrementableInt = 0
}
let document: Document<Model> = Document()
document.data?.num = .increment(1)

OperableArray

Property for handling FieldValue.arrayRemove(), FieldValue.arrayUnion()

struct Model: Codable, Equatable, Modelable {
    var array: OperableArray<Int> = [0, 0]
}
let document: Document<Model> = Document()
document.data?.array = .arrayUnion([1])
document.data?.array = .arrayRemove([1])

File

File is a class for accessing Firestorage. You can save Data in the same path as Document by the follow:

let document: Document<Model> = Document(id: "DOCUMENT_ID")
let file: File = File(document.storageReference)

File supports multiple MIMETypes. Although File infers MIMEType from the name, it is better to input MIMEType explicitly.

  • [x] plain
  • [x] csv
  • [x] html
  • [x] css
  • [x] javascript
  • [x] octetStream(String?)
  • [x] pdf
  • [x] zip
  • [x] tar
  • [x] lzh
  • [x] jpeg
  • [x] pjpeg
  • [x] png
  • [x] gif
  • [x] mp4
  • [x] custom(String, String)

Upload & Download

Upload and Download each return a task. You can manage your progress by accessing tasks.

// upload
let ref: StorageReference = Storage.storage().reference().child("/a")
let data: Data = "test".data(using: .utf8)!
let file: File = File(ref, data: data, name: "n", mimeType: .plain)
let task = file.save { (metadata, error) in
    
}

// download
let task = file.getData(completion: { (data, error) in
    let text: String = String(data: data!, encoding: .utf8)!
})

StorageBatch

StorageBatch is used when uploading multiple files to Cloud Storage.

let textData: Data = "test".data(using: .utf8)!
let textFile: File = File(Storage.storage().reference(withPath: "c"), data: textData, mimeType: .plain)
batch.save(textFile)

let jpgData: Data = image.jpegData(compressionQuality: 1)!
let jpgFile: File = File(Storage.storage().reference(withPath: "d"), jpgData: textData, mimeType: .jpeg)
batch.save(jpgFile)
batch.commit { error in

}

DataSource

Ballcap provides a DataSource for easy handling of Collections and SubCollections.

DataSource initialize

from Document

let dataSource: DataSource<Item> = Document<Item>.query.dataSource()

from Collection Reference

CollectionReference

let query: DataSource<Document<Item>>.Query = DataSource.Query(Firestore.firestore().collection("items"))
let dataSource = DataSource(reference: query)

CollectionGroup

let query: DataSource<Document<Item>>.Query = DataSource.Query(Firestore.firestore().collectionGroup("items"))
let dataSource = DataSource(reference: query)

Your custom object

// from Custom class
let dataSource: DataSource<Item> = Item.query.dataSource()

// from CollectionReference
let query: DataSource<Item>.Query = DataSource.Query(Item.collectionReference)
let dataSource: DataSource<Item> = query.dataSource()

NSDiffableDataSourceSnapshot

self.dataSource = Document<Item>.query
    .order(by: "updatedAt", descending: true)
    .limit(to: 3)
    .dataSource()
    .retrieve(from: { (snapshot, documentSnapshot, done) in
        let document: Document<Item> = Document(documentSnapshot.reference)
        document.get { (item, error) in
            done(item!)
        }
    })
    .onChanged({ (snapshot, dataSourceSnapshot) in
        var snapshot: NSDiffableDataSourceSnapshot<Section, DocumentProxy<Item>> = self.tableViewDataSource.snapshot()
        snapshot.appendItems(dataSourceSnapshot.changes.insertions.map { DocumentProxy(document: $0)})
        snapshot.deleteItems(dataSourceSnapshot.changes.deletions.map { DocumentProxy(document: $0)})
        snapshot.reloadItems(dataSourceSnapshot.changes.modifications.map { DocumentProxy(document: $0)})
        self.tableViewDataSource.apply(snapshot, animatingDifferences: true)
    })
    .listen()

UITableViewDelegate, UITableViewDataSource

self.dataSource = Document<Item>.query
    .order(by: "updatedAt", descending: true)
    .limit(to: 3)
    .dataSource()
    .retrieve(from: { (snapshot, documentSnapshot, done) in
        let document: Document<Item> = Document(documentSnapshot.reference)
        document.get { (item, error) in
            done(item!)
        }
    })
    .onChanged({ (snapshot, dataSourceSnapshot) in
        self.tableView.performBatchUpdates({
            self.tableView.insertRows(at: dataSourceSnapshot.changes.insertions.map { IndexPath(item: dataSourceSnapshot.after.firstIndex(of: $0)!, section: 0)}, with: .automatic)
            self.tableView.deleteRows(at: dataSourceSnapshot.changes.deletions.map { IndexPath(item: dataSourceSnapshot.before.firstIndex(of: $0)!, section: 0)}, with: .automatic)
            self.tableView.reloadRows(at: dataSourceSnapshot.changes.modifications.map { IndexPath(item: dataSourceSnapshot.after.firstIndex(of: $0)!, section: 0)}, with: .automatic)
        }, completion: nil)
    })
    .listen()

Relationship between Document and Object

Document is a class that inherits Object. For simple operations, it is enough to use Document.

public final class Document<Model: Modelable & Codable>: Object, DataRepresentable, DataCacheable {

    public var data: Model?
    
}

You can perform complex operations by extending Object and defining your own class. Use examples are explained in Using Ballcap with SwiftUI

Migrate from Pring

Overview

The difference from Pring is that ReferenceCollection and NestedCollection have been abolished. In Pring, adding a child Object to the ReferenceCollection and NestedCollection of the parent Object saved the parent Object at the same time when it was saved. Ballcap requires the developer to save SubCollection using Batch. In addition, Pring also saved the File at the same time as the Object with the File was saved. Ballcap requires that developers save files using StorageBatch.

Scheme

Ballcap can handle Object class by inheriting Object class like Pring. If you inherit Object class, you must conform to DataRepresentable.

class Room: Object, DataRepresentable {

    var data: Model?

    struct Model: Modelable & Codable {
        var members: [String] = []
    }
}

SubCollection

Ballcap has discontinued NestedCollection and ReferenceCollection Class. Instead, it represents SubCollection by defining CollectionKeys.

Class must match HierarchicalStructurable to use CollectionKeys.

class Room: Object, DataRepresentable & HierarchicalStructurable {

    var data: Model?
    
    var transcripts: [Transcript] = []

    struct Model: Modelable & Codable {
        var members: [String] = []
    }

    enum CollectionKeys: String {
        case transcripts
    }
}

Use the collection function to access the SubCollection.

let collectionReference: CollectionReference = obj.collection(path: .transcripts)

SubCollection's Document save

let batch: Batch = Batch()
let room: Room = Room()
batch.save(room.transcripts, to: room.collection(path: .transcripts))
batch.commit()

Using Ballcap with SwiftUI

First, create an object that conforms to ObservableObject. DataListenable makes an Object observable.

final class User: Object, DataRepresentable, DataListenable, ObservableObject, Identifiable {

    typealias ID = String

    override class var name: String { "users" }

    struct Model: Codable, Modelable {

        var name: String = ""
    }

    @Published var data: User.Model?

    var listener: ListenerRegistration?
}

Next, create a View that displays this object.

struct UserView: View {

    @ObservedObject var user: User

    @State var isPresented: Bool = false

    var body: some View {
        VStack {
            Text(user[\.name])
        }
        .navigationBarTitle(Text("User"))
        .navigationBarItems(trailing: Button("Edit") {
            self.isPresented.toggle()
        })
        .sheet(isPresented: $isPresented) {
            UserEditView(user: self.user.copy(), isPresented: self.$isPresented)
        }
        .onAppear {
            _ = self.user.listen()
        }
    }
}

You can access the object data using subscript.

Text(user[\.name])

Start user observation with onAppear.

.onAppear {
    _ = self.user.listen()
}

Copy object

Pass a copy of Object to EditView before editing the data.

.sheet(isPresented: $isPresented) {
    UserEditView(user: self.user.copy(), isPresented: self.$isPresented)
}

Since the Object is being observed by the listener, changes can be caught automatically.

Finally, create a view that can update the object.

struct UserEditView: View {

    @ObservedObject var user: User

    @Binding var isPresented: Bool

    var body: some View {

        VStack {

            Form {
                Section(header: Text("Name")) {
                    TextField("Name", text: $user[\.name])
                }
            }

            Button("Save") {
                self.user.update()
                self.isPresented.toggle()
            }
        }.frame(height: 200)
    }
}

Updating an object is possible only with update().

Button("Update") {
    self.user.update()
    self.isPresented.toggle()
}
Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].