Giter Club home page Giter Club logo

pring's Introduction

⚠️ Pring is deprecated

Please use 🧢 Ballcap 🧢

Ballcap is new Cloud Firestore framework


Version Platform Downloads

Please donate to continue development.

https://github.com/1amageek/pring.ts

Pring <β>

Firestore model framework. The concept of Document and Collection has been added to Firestore. Pring defines the Scheme of the Document and enables type - safe programming. SubCollection can also be defined in Scheme.

Deep Dive into the Firebase

Please report issues here

Requirements ❗️

Installation ⚙

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

Feature 🎊

☑️ You can define Firestore's Document scheme.
☑️ Of course type safety.
☑️ It seamlessly works with Firestore and Storage.
☑️ You can easily associate subcollections.
☑️ Support GeoPoint.

Design 💻

Firestore Database Design

If you are going to use Firestore and make products, I recommend you to read it.

TODO ✅

Implementation

  • Implement DataType that Firestore can handle
  • Implement data management
  • Implement custom DataType (Specification under consideration)
  • Implement linkage with Firestorage
  • Implement the NestedCollection feature
  • Implement the ReferenceCollection feature
  • Implement DataSource
  • Implement Query-enabled DataSource (Specification under consideration)

Verification (Running Unit test)

  • Verify the implementation of DataType that Firestore can handle
  • Verify the implementation of data management
  • Verify the implementation of custom DataType
  • Verify cooperation with Firestorage
  • Verify the implementation of the NestedCollection feature
  • Verify the implementation of the ReferenceCollection feature
  • Verify the implementation of Query-enabled DataSource

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

Usage

For example..

@objcMembers
class User: Object {
    @objc enum UserType: Int {
        case normal
        case gold
        case premium        
    }
    dynamic var type: UserType = .normal
    dynamic var name: String?
    dynamic var thumbnail: File?
    dynamic var followers: ReferenceCollection<User> = []
    dynamic var items: NestedCollection<Item> = []
    
    // Custom property
    override func encode(_ key: String, value: Any?) -> Any? {
        if key == "type" {
            return self.type.rawValue
        }
        return nil
    }

    override func decode(_ key: String, value: Any?) -> Bool {
        if key == "type" {
            self.type = UserType(rawValue: value as! Int)
            return true
        }
        return false
    }
}
@objcMembers
class Item: Object {
    dynamic var thumbnail: File?
    dynamic var name: String? = "OWABIISHI"
}
// Set an arbitrary ID
let user: User = User(id: "ID")
user.save()
let userA: User = User()
userA.name = "userA"
userA.thumbnail = File(data: UIImageJPEGRepresentation(IMAGE, 0.3)!, mimeType: .jpeg)

let userB: User = User()
userB.name = "userB"
userB.thumbnail = File(data: UIImageJPEGRepresentation(IMAGE, 0.3)!, mimeType: .jpeg)

let item: Item = Item()
item.thumbnail = File(data: UIImageJPEGRepresentation(IMAGE, 0.3)!, mimeType: .jpeg)

userA.followers.insert(userB)
userA.items.insert(item)
userA.save()

Important❗️

Pring clearly separates save and update. This is to prevent unexpected overwriting. Pring provides three methods of initializing Object.

Initialization giving AutoID to Object

let user: User = User()

Initialization giving arbitrary ID

let user: User = User(id: "YOUR_ID") // isSaved false

Initialization when dealing with already saved Object

If you are dealing with an Object that has already been saved, please perform the following initialization. In case of this initialization can not save Please update.

let user: User = User(id: "YOUR_ID", value: [:]) // isSaved true

It is the developer's responsibility to manage the saved state of the Object.

Scheme

Pring inherits Object class and defines the Model. Pring supports many data types.

@objcMembers
class User: Object {
    dynamic var array: [String]                     = ["array"]
    dynamic var set: Set<String>                    = ["set"]
    dynamic var bool: Bool                          = true
    dynamic var binary: Data                        = "data".data(using: .utf8)!
    dynamic var file: File                          = File(data: UIImageJPEGRepresentation(UIImage(named: "")!, 1))
    dynamic var url: URL                            = URL(string: "https://firebase.google.com/")!
    dynamic var int: Int                            = Int.max
    dynamic var float: Double                       = Double.infinity
    dynamic var date: Date                          = Date(timeIntervalSince1970: 100)
    dynamic var geoPoint: GeoPoint                  = GeoPoint(latitude: 0, longitude: 0)
    dynamic var list: List<Group>                   = []    
    dynamic var dictionary: [String: Any]           = ["key": "value"]    
    dynamic var string: String                      = "string"
    
    let group: Reference<Group>                         = .init()
    let nestedCollection: NestedCollection<Item>             = []
    let referenceCollection: ReferenceCollection<User>  = []
}
DataType Description
Array It is Array type.
Set It is Set type.In Firestore it is expressed as {"value": true}.
Bool It is a boolean value.
File It is File type. You can save large data files.
URL It is URL type. It is saved as string in Firestore.
Int It is Int type.
Float It is Float type. In iOS, it will be a 64 bit Double type.
Date It is Date type.
GeoPoint It is GeoPoint type.
List It is Object array type.
Dictionary It is a Dictionary type. Save the structural data.
nestedCollection or referenceCollection It is SubCollection type.
String It is String type.
Reference It is Reference type. It hold DocumentReference
Null It is Null type.
Any It is custom type. You can specify it as a custom type if it is a class that inherits from NSObject.

⚠️ Bool Int Float Double are not supported optional type.

⚙️ Manage data

Save

Document can be saved only once.

let object: MyObject = MyObject()
object.save { (ref, error) in
   // completion
}

Retrieve

Retrieve document with ID.

MyObject.get(document!.id, block: { (document, error) in
    // do something
})

Update

Document has an update method. Be careful as it is different from Salada.

MyObject.get(document!.id, block: { (document, error) in
    document.string = "newString"
    document.update { error in
       // update
    }
})

Delete

Delete document with ID.

MyObject.get(document!.id, block: { (document, error) in
    document.delete()
})

Batched writes

let batch: WriteBatch = Firestore.firestore().batch()
batch.add(.save, object: userA)    //  ** File is not saved.
batch.add(.update, object: userB)
batch.add(.delete, object: userC)
batch.commit(completion: { (error) in
  // error handling
})

List

List can access the Object faster than NestedCollection. List holds data in Document, not SubCollection.

// save
let order: Order = Order()
do {
    let orderItem: OrderItem = OrderItem()
    orderItem.name = "aaaa"
    orderItem.price = 39
    order.items.append(orderItem)
}
do {
    let orderItem: OrderItem = OrderItem()
    orderItem.name = "bbb"
    orderItem.price = 21
    order.items.append(orderItem)
}
order.save()

Be sure to update the parent's object when updating data.

// update
Order.get("ORDER_ID") { (order, error) in
    order.items.first.name = "hoge"
    order.update()
}

📄 File

Pring has a File class because it seamlessly works with Firebase Storage.

Save

File is saved with Document Save at the same time.

let object: MyObject = MyObject()
object.thumbnailImage = File(data: PNG_DATA, mimeType: .png)
let tasks: [String: StorageUploadTask] = object.save { (ref, error) in

}

save method returns the StorageUploadTask that is set with the key. For details on how to use StorageUploadTask, refer to Firebase docs.

let task: StorageUploadTask = tasks["thumbnailImage"]

Get data

Get data with size.

let task: StorageDownloadTask = object.thumbnail.getData(100000, block: { (data, error) in
    // do something
})

Update

If the Document is already saved, please use update method. update method also returns StorageUploadTask. Running update method automatically deletes old files.

let newFile: File = File(data: PNG_DATA, mimeType: .png)
object.thumbnailImage = newFile
object.update()

Delete

Delete it with delete method.

object.thumbnailImage = File.delete()
object.update()

If it is held in an array, automatic file deletion is done by deleting from the array and updating it.

object.files.remove(at: 0)
object.update()

Nested Collection & Reference Collection

NestedCollection and ReferenceCollection are classes that define SubCollection.

When holding File in SubCollection, saving of File will be executed first. When many Files are stored in SubCollection at once, the performance deteriorates.

Nested Collection

  • NestedCollection nests data and saves it under the document.
  • The destination path of File is nested path.

Reference Collection

  • ReferenceCollection saves the documentID under the document.
  • Data is saved separately.
@objcMembers
class User: Object {
    dynamic var name: String?
    dynamic var followers: ReferenceCollection<User> = []
    dynamic var items: NestedCollection<Item> = []
}

@objcMembers
class Item: Object {
    dynamic var thumbnail: File?
}

let userA: User = User()
userA.name = "userA"

let userB: User = User()
userB.name = "userB"

let item: Item = Item()
item.thumbnail = File(data: JPEG_DATA, mimeType: .jpeg)

userA.followers.insert(userB)
userA.items.insert(item)
userA.save()
let item: Item = Item()
userA.items.insert(item)
userA.update() { error in
  if let error = error {
    // error handling
    return
  }
  // do something
}

DataSource

DataSource is a class for easy handling of data retrieval from Collection.

class DataSourceViewController: UITableViewController {

    var dataSource: DataSource<User>?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        self.dataSource = User.order(by: \User.createdAt).limit(to: 30).dataSource()
            .on({ [weak self] (snapshot, changes) in
                guard let tableView: UITableView = self?.tableView else { return }
                switch changes {
                case .initial:
                    tableView.reloadData()
                case .update(let deletions, let insertions, let modifications):
                    tableView.beginUpdates()
                    tableView.insertRows(at: insertions.map { IndexPath(row: $0, section: 0) }, with: .automatic)
                    tableView.deleteRows(at: deletions.map { IndexPath(row: $0, section: 0) }, with: .automatic)
                    tableView.reloadRows(at: modifications.map { IndexPath(row: $0, section: 0) }, with: .automatic)
                    tableView.endUpdates()
                case .error(let error):
                    print(error)
                }
            }).listen()
    }

    // MARK: - Table view data source

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return self.dataSource?.count ?? 0
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell: DataSourceViewCell = tableView.dequeueReusableCell(withIdentifier: "DataSourceViewCell", for: indexPath) as! DataSourceViewCell
        configure(cell, atIndexPath: indexPath)
        return cell
    }

    func configure(_ cell: DataSourceViewCell, atIndexPath indexPath: IndexPath) {
        guard let user: User = self.dataSource?[indexPath.item] else { return }
        cell.textLabel?.text = user.name
        cell.disposer = user.listen { (user, error) in
            cell.textLabel?.text = user?.name
        }
    }

    func tableView(_ tableView: UITableView, didEndDisplaying cell: DataSourceViewCell, forRowAt indexPath: IndexPath) {
        cell.disposer?.dispose()
    }

    override func tableView(_ tableView: UITableView, canPerformAction action: Selector, forRowAt indexPath: IndexPath, withSender sender: Any?) -> Bool {
        return true
    }

    override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
        if editingStyle == .delete {
            self.dataSource?.removeDocument(at: indexPath.item)
        }
    }
}

SubCollection DataSource

User.get("USER_ID") { (user, error) in
    guard let user: User = user else { return }
    self.dataSource = user.followers.order(by: \User.createdAt).dataSource()
        .on { (snapshot, changes) in
            // something
        }.listen()
}

Synchronous Client Side Join

@objcMembers
class User: Object {

    let group: Reference<Group> = Reference()
}

Please add on(parse:) to DataSource.

self.dataSource = User.order(by: \User.updatedAt).dataSource()
    .on({ [weak self] (snapshot, changes) in
        guard let tableView: UITableView = self?.tableView else { return }
        debugPrint("On")
        switch changes {
        case .initial:
            tableView.reloadData()
        case .update(let deletions, let insertions, let modifications):
            tableView.beginUpdates()
            tableView.insertRows(at: insertions.map { IndexPath(row: $0, section: 0) }, with: .automatic)
            tableView.deleteRows(at: deletions.map { IndexPath(row: $0, section: 0) }, with: .automatic)
            tableView.reloadRows(at: modifications.map { IndexPath(row: $0, section: 0) }, with: .automatic)
            tableView.endUpdates()
        case .error(let error):
            print(error)
        }
    })
    .on(parse: { (snapshot, user, done) in
        user.group.get({ (group, error) in
            done(user)
        })
    })
    .onCompleted({ (snapshot, users) in
        debugPrint("completed")
    })
    .listen()

Query

Get documents

User.where(\User.name, isEqualTo: "name").get { (snapshot, error) in
    print(snapshot?.documents)
}

Get SubCollections

WHERE

let user: User = User(id: "user_id")
user.items.where(\Item.name, isEqualTo: "item_name").get { (snapshot, error) in
    print(snapshot?.documents)
}

ORDER

let user: User = User(id: "user_id")
user.items.order(by: \Item.updatedAt).get { (snapshot, error) in
    print(snapshot?.documents)
}

Create DataSource from Query

let user: User = User(id: "user_id")
user.items
    .where(\Item.name, isEqualTo: "item_name")
    .dataSource()
    .on({ (snapshot, change) in
        // do something
    })
    .onCompleted { (snapshot, items) in
        print(items)
}

Full-text search

Please use ElasticSearch or Algolia when performing full-text search on Firebase. There is a library when implementing with Swift.

https://github.com/miuP/Algent

pring's People

Contributors

1amageek avatar brightsider avatar inquisitivesoft avatar lm2343635 avatar miup avatar sgr-ksmt avatar shukob avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

pring's Issues

String enums not saving

Models are as follows:

enum Gender : String {
    case male, female, undefined
}

@objcMembers
class User: Base {
    dynamic var gender: Gender = .undefined

    override func encode(_ key: String, value: Any?) -> Any? {
        if key == "gender" {
            return self.gender.rawValue
        }

        return nil
    }
    
    override func decode(_ key: String, value: Any?) -> Bool {
        if key == "gender" {
            if let value = value as? String, let gender = Gender(rawValue: value) {
                self.gender = gender
            }
            
            return true
        }
        
        return false
    }
}

Build failure in Xcode 9.3

[REQUIRED] Step 1: Describe your environment

  • Xcode version: 9.3 (9E145)
  • Pring version: 0.7.14

[REQUIRED] Step 3: Describe the problem

Our project cannot build in Xcode 9.3.

for 'deleteFiles(container:block:)' at /Users/hoge/Xcode/Komerco/Pods/Pring/Pring/AnySubCollection.swift:57:12
error: Segmentation fault: 11

Update() method issue after the version 0.13.0

[REQUIRED] Step 1: Describe your environment

  • Xcode version: 9.4.1
  • Pring version: > 0.13.0

[REQUIRED] Step 3: Describe the problem

The update method was executed, however the document in the cloud firestore was not updated.
This error not happens in the version 0.12.2.
It seems that this issue is caused by this commit 8445f3a

Relevant Code:

This is my update code.

currentUser.update { error in
    if let error = error {
        print("Error saving user \(error)")
        completion(false)
        return
    }
    completion(true)
}

Before running this, I set a reference object for the currentUser, and there is no group reference in the user document in the cloud firestore.

currentUser.group.set(group)

ReferenceCollection cannot be loaded.

[REQUIRED] Step 1: Describe your environment

  • Xcode version: 9.4
  • Pring version: 0.12

[REQUIRED] Step 3: Describe the problem

I have such a reference collection in my object.

@objcMembers class Race: Object {

    dynamic var avatar: String = ""
    dynamic var name: String = ""
    dynamic var introduction: String = ""
    dynamic var status: RaceStatus = .unknown
    dynamic var raceAt: Date = Date(timeIntervalSince1970: 0)
    
    let competitors: ReferenceCollection<Competitor> = []
    
    override func encode(_ key: String, value: Any?) -> Any? {
        switch key {
        case #keyPath(status):
            return status.rawValue
        default:
            return nil
        }
    }
    
    override func decode(_ key: String, value: Any?) -> Bool {
        switch key {
        case #keyPath(status):
            guard let value = value as? Int else {
               return false
            }
            status = RaceStatus(rawValue: value) ?? .unknown
            return true
        default:
            return false
        }
    }
    
}

Steps to reproduce:

When I tries to load the competitors from a race object like this

        orders = race.competitors.query.dataSource().on({ [weak self] (snapshot, changes) in
            guard let horseCollectionView = self?.horseCollectionView else {
                return
            }
            switch changes {
            case .initial:
                horseCollectionView.reloadData()
            case .update(let deletions, let insertions, let modifications):
                horseCollectionView.performBatchUpdates({
                    horseCollectionView.insertItems(at: insertions.map { IndexPath(row: $0, section: 0) })
                    horseCollectionView.deleteItems(at: deletions.map { IndexPath(row: $0, section: 0) })
                    horseCollectionView.reloadItems(at: modifications.map { IndexPath(row: $0, section: 0) })
                }, completion: nil)
            case .error(let error):
                print(error)
            }
        }).onCompleted({ (snapshot, competitors) in
            guard let documents = snapshot?.documents else {
                return
            }
            for document in documents {
                print(document.data()) // Not empty.
            }
            print(competitors) // Empty
        }).listen()

I found the competitors are empty event the data of document is not empty.

Query does not work

Even with the code like below all posts will be retrieved.

dataSource = Post.order(by: \Post.createdAt).limit(to: 1).dataSource()
...
}.listen()

Nested Object does not work correctly

[REQUIRED] Step 1: Describe your environment

  • Xcode version: 10.1 (10B61)
  • Pring version: 0.15.2

[REQUIRED] Step 3: Describe the problem

  • A property of a type subclassing Object is
    • not correctly populated from snapshot
    • not updated when directly changing the properties

Steps to reproduce:

Create 2 classes both inherit from Object (name them A and B), make B one of A's property, then
1 A().save() and A.get(id: ...)
2 let a = A(); a.b.something = c; a.save()

What happened? How can we make the problem occur?
This could be a description, log/console output, etc.
1 a.b is nil after get, even if it is saved on firestore
2 saved value of a.b is not c, but the default value.

1 is caused by the constructor of Object
2 is caused by the nature of KVO, we have to make a parent property also in Object to propagate changes up to root parent.

Relevant Code:

// TODO(you): code here to reproduce the problem

How to clear cache.

It seems that Pring saves the loaded data for the next data loading.
How can I clear the cache?
Thanks.

Retrieve reference without listening to changes

Hello, is it possible to use the "parse" functionality to retrieve references along the object in a query, however I only want to retrieve the query once, and not listen to updates, deletions, changes etc.

Is it possible?

Thanks!

Error on pack().commit not resolved

When there is error saving document to firebase error object is ignored and you continue with getDocument method. I think error message is quite important here (bad permissions etc.)

My solution is to return nil reference and error object if there is any and perform getDocument only where there is no error in save.

self.pack().commit { (error) in

Filtering query by array

Hi,

i want to ask about filtering query by array, filtering data after query is possible but it isn't good solution in case of large amounts of data . I know this can be kind of tricky, because firestore itself doesn't have this type of query. Their solution is to use map of values https://firebase.google.com/docs/firestore/solutions/arrays .

Do you have any solution for this problem? Or are you planing to implement something like this?

Thank you very much for you time and good work.

ModelName naming

Maybe better use lowercased first letter instead of lowercased all name?
Example, ListingReport -> listingReport intead of listingreport

Reference path issue

I have cards in user and set this card to Item.
Card path - version/1/user/ID/cards/ID
Card path in item.card.get - version/1/card/ID

Crash - Could not cast value of type NSNull to NSDate

environment

Xcode 9.2
Pring 0.6.3
Firebase 4.8.2
Firestore 0.10.0

problem

Could not cast value of type 'NSNull' (0x10ea9f770) to 'NSDate' (0x10ea9efc8).

Crash on this line

Steps to reproduce:

post.users.remove(user)
post.update()

then after

post.users.insert(user)
post.update()

Log

Thread 0 name:  Dispatch queue: com.apple.main-thread
Thread 0 Crashed:
0   libsystem_kernel.dylib        	0x000000018b479014 0x18b45a000 + 126996
1   libsystem_pthread.dylib       	0x000000018b543264 0x18b53e000 + 21092
2   libsystem_c.dylib             	0x000000018b3eda60 0x18b38c000 + 399968
3   libsystem_c.dylib             	0x000000018b3ed9d0 0x18b38c000 + 399824
4   libswiftCore.dylib            	0x0000000100f68f08 _hidden#25636_ + 3084040 (__hidden#25644_:271)
5   libswiftCore.dylib            	0x0000000100f5eacc _hidden#25267_ + 3041996 (__hidden#25314_:185)
6   libswiftCore.dylib            	0x0000000100f5eb44 swift_dynamicCastClass + 3042116 (__hidden#25314_:269)
7   libswiftCore.dylib            	0x0000000100f97644 swift_dynamicCastForeignClass + 3274308 (__hidden#27753_:1067)
8   libswiftCore.dylib            	0x0000000100f625e0 _hidden#25272_ + 3057120 (__hidden#25314_:2892)
9   libswiftCore.dylib            	0x0000000100f608c4 swift_dynamicCast + 3049668 (__hidden#25314_:1613)
10  MyApp                        	0x00000001002f2e14 specialized closure #2 in DataSource.get(with:block:) + 2141716 (DataSource.swift:343)
11  MyApp                        	0x00000001002f4f0c partial apply for closure #2 in DataSource.get(with:block:) + 2150156 (DataSource.swift:0)
12  MyApp                        	0x0000000100312b2c specialized closure #1 in static Document.get(_:block:) + 2272044 (Document.swift:0)
13  MyApp                        	0x000000010031342c partial apply for closure #1 in static Document.get(_:block:) + 2274348 (Document.swift:0)
14  MyApp                        	0x000000010030c584 thunk for @callee_owned (@owned StorageMetadata?, @owned Error?) -> () + 2246020 (Document.swift:0)
15  MyApp                        	0x00000001002348cc __50-[FIRDocumentReference getDocumentWithCompletion:]_block_invoke + 400
16  MyApp                        	0x0000000100234eec __72-[FIRDocumentReference addSnapshotListenerInternalWithOptions:listener:]_block_invoke + 308
17  libdispatch.dylib             	0x000000018b3369e0 0x18b335000 + 6624
18  libdispatch.dylib             	0x000000018b3369a0 0x18b335000 + 6560
19  libdispatch.dylib             	0x000000018b33b5e8 0x18b335000 + 26088
20  CoreFoundation                	0x000000018c42d0c8 0x18c352000 + 897224
21  CoreFoundation                	0x000000018c42ace4 0x18c352000 + 888036
22  CoreFoundation                	0x000000018c35ada4 0x18c352000 + 36260
23  GraphicsServices              	0x000000018ddc5074 0x18ddb9000 + 49268
24  UIKit                         	0x0000000192615c9c 0x1925a1000 + 478364
25  MyApp                        	0x00000001000efbc4 main + 31684 (UIViewController+Extension.swift:9)
26  libdyld.dylib                 	0x000000018b36959c 0x18b365000 + 17820

It seems that snapshot obtained from listener does not contain updatedAt but I had no idea where it was lost.

Add support for Carthage

[REQUIRED] Step 1: Describe your environment

  • Xcode version: 10.1
  • Pring version: 0.15.0

[REQUIRED] Step 3: Describe the problem

If use the framework through CocoaPods, it takes too much time on our CI (Bitrise.io) for deploying, so we can't deploy the build.
We have tried to move in a separate framework, but than we had the next problem: when Pring tries to call Firestore, it returns warning that the FirebaseApp is not configured.

Array of references not supported

Array of references seems to be unsupported. In DataType.init(key:value:data:) our runtime field type of Array<Pring.Reference<MyType>> doesn't fall into any useful type matching case.

This seems to be because there's a check for subjectType == [Any].self, rather than using as? to check for compatible array types.

Bugs documentation

The readme suggests that the library contains bug, could you document them in issues so that can be visible and get to by someone who has the time.

Thanks

Feature Request: use superclassMirror to make Object subclass inheritable

[REQUIRED] Step 1: Describe your environment

  • Xcode version: 10.1 (10B61)
  • Pring version: 0.15.2

[REQUIRED] Step 3: Describe the problem

Currently only leaf class properties can be handled. It is useful if we can have class hierarchy using Object subclasses.

Steps to reproduce:

What happened? How can we make the problem occur?
This could be a description, log/console output, etc.

Relevant Code:

// TODO(you): code here to reproduce the problem

Failing to update Object with a decoded property

class Conversation : Object {
     override func decode(_ key: String, value: Any?) -> Bool {
           if key == "lastMessage", let value = value as? [AnyHashable : Any] {
               self.lastMessage = Message(id: key, value: value)
               return true
           }
        return false
     }
}

class Message : Object {
    dynamic var owner: String = ""
    dynamic var text: String = ""
    dynamic var status: MessageStatus = .delivered
}

// Fetched an instance of Conversation and update a property followed by calling update
conversation.lastMessage.status = .read
conversation.update()

// Experienced a crash:
'Cannot remove an observer <Message 0x1c01d8600> for the key path "owner" from <Message 0x1c01d8600> because it is not registered as an observer.'

Clarification of use case of Relation

[REQUIRED] Step 1: Describe your environment

  • Xcode version: 10.1 (10B61)
  • Pring version: 0.15.0

[REQUIRED] Step 3: Describe the problem

I want to know the intended differences between Relation and Reference.
In my understanding, Relation is a wrapper of the pattern in which we save an id of a collection object instead of a DocumentReference to it. Is it collect?

Steps to reproduce:

What happened? How can we make the problem occur?
This could be a description, log/console output, etc.

Relevant Code:

// TODO(you): code here to reproduce the problem

Object.update doesn't persist changes

After fetching an object from the database, setting a property of the object and calling .update() doesn't give any errors, but also doesn't reflect the changes in the database. Tried using object.reference.updateData(object.value) instead and it works as expected.

[Not Issue] Couple of questions on the framework

First of all its an interesting framework and I need a few information to see if I can try it out.

  1. For me the firebase paths are already defined. If that is the case, how can I tell the object to use a particular path? Right now you are using version/modelversion/modelName, which wont work in my case.

  2. I have a nested subcollection usecases in my project as explained here (https://stackoverflow.com/questions/54751023/swift-firestore-sub-collections-custom-objects-and-listeners/54751305#comment96528634_54751305) will this framework ease my flow?.

predicate doesn't work on modified

Step 1: Describe your environment

  • Xcode version:10.1
  • Pring version: 0.16.1

Step 3: Describe the problem

Steps to reproduce:

  1. Create User and Save
let user: User = User()
user.name = "hoge"
user.save()
  1. Create Option and set predicate.
let option = Options()
option.predicate = NSPredicate(format: "name == %@", "moge")
  1. Create DataSource
  let dataSource = UserInfo.dataSource(options: option)
     .on({ [weak self] (snapshot, changes) in
            switch changes {
                case .initial:
                    ()
                case .update(let deletions, let insertions, let modifications):
                    ()
                case .error(let error):
                    print(error)
                }
      }).listen()
  1. Update UserName
    Update User.name "hoge" to "moge" at FireStore

  2. now is user.name = "moge", but never call changes or add event.

Relevant Code:

DataSource.swift - line 305

func _operate() {
   case .modified: 
      guard self.documents.contains(where: { return $0.id == id}) else {
         return
      }
}

Should consider about currently not contains items?
ここでfilterd条件が外れる変更だった場合も考慮するべき?

Storage is hard-coded

Storage is hard-coded in Object. As a result, multi-project configs are not possible with Pring.

Here are the culprits:

return Storage.storage().reference().child(self.path)

and

return Storage.storage().reference().child(self.path)

Pring should have an interface for injecting a custom FIRApp, in order to override the default Storage.storage reference with Storage.storage(app: passedInValue).

Example usage with Firebase User

Since most will be using this against a Firebase user, would be valuable to have an example that shows how to do a a Firebase user to model relationship.

User extension Firebase Error

Hey,

I am trying to use your example to add the Firebase User as an object, but whenever I try to extend Firebase in a file like: extension Firebase { }

It just says "Use of undeclared type 'Firebase'" - so I guess this is not possible? Firebase works fine outside, and I can accesss Firebase.User inside functions and such

File saving failed using batch

[REQUIRED] Step 1: Describe your environment

  • Xcode version: 9.2
  • Pring version: 0.7.8

[REQUIRED] Step 3: Describe the problem

I tried to save Images using WriteBatch class.
Saving to Firestore is succeeded, but File is not saved in storage.

Relevant Code:

class Image: Object {
    dynamic var file: File?
}

let imageModel = Firebase.Image()
imageModel.file = File(data: UIImageJPEGRepresentation(image, 1.0)!)
let batch = Firestore.firestore().batch()
batch.add(.save, object: imageModel)
batch.commit { error in
    print(error) // printed nil, but file is not exist in storage
}

Datasource .listen() observer not updating on delete

[REQUIRED] Step 1: Describe your environment

  • Xcode version: 9.4
  • Pring version: 0.8.4

[REQUIRED] Step 3: Describe the problem

Removing document does not update the dataSource listen() observer, never removing the tableViewCell. However it does remove it from firestore.

Steps to reproduce:

What happened? How can we make the problem occur?
This could be a description, log/console output, etc.

Create a dataSource with .on().listen()

self.hubsDataSource.removeDocument(at: indexPath.row) to delete

Relevant Code:

hubsDataSource .on({ [unowned self] (snapshot, changes) in switch changes { case .initial: self.hubTableView.reloadData() case .update(let deletions, let insertions, let modifications): self.hubTableView.beginUpdates() self.hubTableView.insertRows(at: insertions.map { IndexPath(row: $0, section: 0) }, with: .top) self.hubTableView.deleteRows(at: deletions.map { IndexPath(row: $0, section: 0) }, with: .bottom) self.hubTableView.reloadRows(at: modifications.map { IndexPath(row: $0, section: 0) }, with: .fade) self.hubTableView.endUpdates() case .error(let error): print(error) } }) .onCompleted({ [unowned self] (snapshot, users) in if self.hubsDataSource.count > 0 { self.gifImageView.stopAnimating() self.gifImageView.isHidden = true self.hubTableView.tableFooterView?.isHidden = true } }) .listen()

How about support encoding or decoding for key ?

[REQUIRED] Step 1: Describe your environment

  • Xcode version: 10.1
  • Pring version: 0.14.0

[REQUIRED] Step 3: Describe the problem

Steps to reproduce:

I declared variable as camel-case in model. But I have to save using underscore to firestore.
I found encoding or decoding for value but couldn't change for key. I try to change _properties but it's private variable.

How about support decodeKey, encodeKey ?

Relevant Code:

// TODO(you): code here to reproduce the problem

Updating referencecollection overrides data

Hello.

I am currently trying to insert a new item into a ReferenceCollection of a User object (followers ex)

As the item I am inserting already exists, right now when I try to update() on the user object I am inserting to, it overrides the data for the referenced object. I am only initating the object with an ID for the document, so it sets all the fields to null.

If the update methods also updates the references, wouldn't that be inconvienient if the other object is updated in between, would mean the data would be rolled back?


if let userSnapshot = snapshot.documents.first
                                {
                                    user.friends.insert(CHUser(id: id))
                                    user.update({ (error) in
                                        if let error = error
                                        {
                                            print(error)
                                            completion(CHError.UnknownError, nil)
                                            return
                                        }
                                        
                                        completion(nil, userSnapshot.documentID)
                                    })
                                }

Customize "Object" variables

Hello.

I don't quite fully understand why /version/1/ is a required prefix?

Is it possible to change this, and maybe also override the modelName variable?

/ Mads

Store object

Hello!
How I can to store Object for reusing?
Needs UserDefaults or any other storage.

Crashes on DataSource and Object

I have been testing Pring with my app and (me + 2 testers) and we've encountered quite high frequency of crashes. It seems most problematic part is when using data source from nested or reference collection. I am using simple query.dataSource().onCompleted({...}).get() on these objectes

snimek obrazovky 2017-12-08 v 9 58 11

snimek obrazovky 2017-12-08 v 9 57 54

snimek obrazovky 2017-12-08 v 9 57 29

Saved path is wrong in File Object

I try to save File in Object that referenced other object.
Save completed successfully, but Parent.child.file.ref.fullPath is wrong.
In storage, file is exist in version/1/parent/${parentID}/child/${childID}/${fileName}, but fullpath returns version/1/child/${childID}/${fileName}

sample is below

class Parent {
    child: Reference<Child> = .init()
}

class Child {
    file: File?
}

let child = Child()
child.file = File(data: imageData, mimeType: .jpg)
let parent = Parent()
parent.child = child
parent.save()

ReferenceCollection.query.order(by:) is not working

I tried to get ordered collection by using ReferenceCollection's DataSource. But order(by:) may not be working.

// Models
class Parent: Object {
    dynamic var children: ReferenceCollection<Child> = []
}

class Child: Object {
    dynamic var parent: Reference<Parent> = .init()
}

// Datasource

self.dataSource = parent.children.query
    .order(by: \Child.cratedAt)
    .dataSource()
    .on { (_, changes) in
        guard let tableView = self?.tableView else { return }
        switch changes {
        case .initial:
            self?.tableView.reloadData()
        case .update(let deletions, let insertions, let modifications):
            tableView.beginUpdates()
            tableView.insertRows(at: insertions.map { IndexPath(row: $0, section: 0) }, with: .automatic)
            tableView.deleteRows(at: deletions.map { IndexPath(row: $0, section: 0) }, with: .automatic)
            tableView.reloadRows(at: modifications.map { IndexPath(row: $0, section: 0) }, with: .automatic)
            tableView.endUpdates()
        case .error(let error):
            debugPrint(error)
        }
    }
    .listen()

And I also tried to below code. It works correctly . But listen() may not be working.
I can't observe Changes.update, when new object inserted.

self.dataSource = Child.query
    .where("parent", isEqualTo: parent.reference)
    .order(by: \Child.cratedAt)
    .dataSource()
    .on { (_, changes) in
        guard let tableView = self?.tableView else { return }
        switch changes {
        case .initial:
            self?.tableView.reloadData()
        case .update(let deletions, let insertions, let modifications):
            tableView.beginUpdates()
            tableView.insertRows(at: insertions.map { IndexPath(row: $0, section: 0) }, with: .automatic)
            tableView.deleteRows(at: deletions.map { IndexPath(row: $0, section: 0) }, with: .automatic)
            tableView.reloadRows(at: modifications.map { IndexPath(row: $0, section: 0) }, with: .automatic)
            tableView.endUpdates()
        case .error(let error):
            debugPrint(error)
        }
    }
    .listen()

Random crash when app is launched

This crash is occurring randomly when app is launched. This crash is occurring often 50% of time.
As far as I understand - This crash occurred when matching 'id' document is not available in 'self.documents'

Screenshot 2020-07-13 at 6 03 13 PM

Screenshot 2020-07-13 at 5 59 51 PM

Numbers are not allowed when using Cloud Functions

As of now as all collections need the prefix "version/1" etc. It disables our ability to use Cloud Functions. Cloud Functions does not allow numbers in the regex, I've just tried and it complains about the "1" being there.

How do we prevent this from happening?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.