Decode Cloud Firestore data result item to NSObject - firebase

I've a cloud firebase result item:
let mpUserRef = db?.collection(MPUser.PROPERTY_DB)
.whereField(MPUser.PROPERTY_FIREBASE_USER_ID, isEqualTo: firebaseUSer.uid)
.limit(to: 1).getDocuments() { (document, err) in
if let err = err {
print("Error getting documents: \(err)")
} else {
for document in document!.documents {
let mpUser = fromJsonToMpUser( userJson: "\(document.data())" )
if mpUser != nil{
delegate.mpUser = mpUser
}
}
}
}
And i want to decode result item in my business Object calling the method:
class func fromJsonToMpUser( userJson: String ) -> MPUser?{
let decoder = JSONDecoder()
do{
let json = userJson.data(using: .utf8)!
let mpUser = try decoder.decode(MPUser.self, from: json)
return mpUser
}catch{
print("Errore while ecode Club")
return nil
}
}
But this does not work, the error message that i have is:
"The given data was not valid JSON.", underlyingError: Optional(Error Domain=NSCocoaErrorDomain Code=3840 "Badly formed array around character 17." UserInfo={NSDebugDescription=Badly formed array around character 17.}))).
So, how can i converte cloud firestore item result in valid Json?

I've resolved my question using CodableFirebase library ( https://github.com/alickbass/CodableFirebase created by #alickbass )

Related

How to store videos in Firebase using Swift

I currently have a function that allows me to upload an image to Firebase Storage. But how in the world can I achieve the same, with a video instead of an image?
I suspect I have to change UIImage and .jpg to something else. But what is the type of a video?
#State var pickedImages: [UIImage] = []
#State var retrievedImages = [UIImage]()
This is my function:
func uploadImage() {
// Create storage reference
let storageRef = Storage.storage().reference()
// Turn our image into data
let selectedImage = pickedImages[0]
let imageData = selectedImage.jpegData(compressionQuality: 0.8)
guard imageData != nil else {
let er = "THIS: Error while converting to data"
return print(er)
}
// Specifie filepath and name
let path = "images/\(UUID().uuidString).jpg"
let fileRef = storageRef.child(path)
// Upload that data
let uploadTask = fileRef.putData(imageData!, metadata: nil) {metaData, error in
print("THIS: from uploadTAsk")
// Check for errors
if error == nil && metaData != nil {
// Save reference in firestore DB
let db = Firestore.firestore()
db.collection("images").document("user1").setData(["url": path]) { error in
print("THIS: inside closure")
// If there was no errors, display the image
if error == nil {
DispatchQueue.main.async {
self.retrievedImages.append(selectedImage)
}
}
}
}
}
}

How can I read the value of a field in Firestore (Swift)

I want to read out the Value of an Field of my document (in Firebase Firestore with SwiftUI).
What I already have I this:
let value = myDataBase
// My Database instance
//
value.collection("My Collection").whereField("Code", isEqualTo: codeTextInput)
.getDocuments() { (querySnapshot, err) in
if let err = err {
print("Error getting documents: \(err)")
} else {
for document in querySnapshot!.documents {
print("\(document.documentID) => \(document.data())")
}
}
}
(This Code works fine)
And now I want to store the Value of all Documents, which are in my collection and have for the key "Code" the value, which is typed in. But I want to store the Data for the key "Wert"
When I've saved it, I want to use it as an User-Default...
Btw. I don’t want collect more then 1 item with this code, I just want that this item which I collect is the right.
Let sum it up:
You want all documents in your collection with a certain value to be fetched
You want to save all of these values and be able to access them.
I can only recommend working with objects in this scenario. Let's make an example:
Lets import all modules
import Foundation
import Firebase
import FirebaseFirestoreSwift
import FirebaseStorage
import Combine
First we declare the structure:
https://firebase.google.com/docs/firestore/manage-data/add-data#custom_objects
public struct MyObject: Codable {
let id: String
let code: String?
// Needed to identify them in Firestore
enum CodingKeys: String, CodingKey {
case id
case code = "code"
}
}
Now we access it and generate an Object for each document we can fetch that contains your desired value:
https://firebase.google.com/docs/firestore/query-data/get-data#custom_objects
var myArray: Array<MyObject> = [] // Empty array where we will store all objects in
var codeTextInput = "Test"
// Fetch only desired documents
let db = Firestore.firestore()
let docRef = db.collection("My Collection").whereField("Code", isEqualTo: codeTextInput)
func getDocumentsAsObjects() { // docRef.getDocuments Needs to be in function or else: Expressions are not allowed at the top level
docRef.getDocuments { (querySnapshot, err) in //getDocuments (s) as in multiple
if let err = err {
print("Error getting documents: \(err)")
} else {
for document in querySnapshot!.documents { // iterate them and add them to your array
let result = Result {
try document.data(as: MyObject.self)
}
switch result {
case .success(let myObject):
if let myObject = myObject {
myObject.id = document!.documentID // Get the ID of the Document as we might need it later
myArray.append(myObject) // Save the document into your array
} else {
// A nil value was successfully initialized from the DocumentSnapshot,
// or the DocumentSnapshot was nil.
print("Document does not exist")
}
case .failure(let error):
// A `MyObject` value could not be initialized from the DocumentSnapshot.
print("Error decoding city: \(error)")
}
}
}
}
}
Now you have your Objects in your array and can access them

Swiftui + Firestore - Cannot access field in Firestore

trying to access a specific field in Firestore, but can't seem to figure it out. In my document, I have a map like this:
The below is my code. Hour just returns nil for some reason. I've tried a number of different things including ["notificationTime"]["hour"], but can't seem to figure it out. Any idea? Thanks!
self.db.collection("routines").whereField("name", isEqualTo: routineName).getDocuments() { (querySnapshot, err) in
if let err = err {
print ("Error getting documents: \(err)")
} else {
for document in querySnapshot!.documents {
let hour = document.data()["notificationTime.hour"]
print(hour)
return
}
}
}
To read data from a map, use the following approach:
let db = Firestore.firestore()
db.collection("routines").whereField("name", isEqualTo: routineName).getDocuments() { (querySnapshot, err) in
if let err = err {
print("Error getting documents: \(err)")
}
else if let querySnapshot = querySnapshot {
for document in querySnapshot.documents {
let results = document.data()
if let notificationTime = results["notificationTime"] as? [String: Any] {
let hour = notificationTime["hour"] as? Int ?? 0
let minute = notificationTime["minute"] as? Int ?? 0
// ... further handling of your data
}
}
}
}
That being said, I'd like to suggest looking into Timestamps for storing date-time related data. See the documentation for more details.

Swift 4 Load 3D Models from Firebase

I'm trying to get a 3D Model which is stored in Firebase into my iOS Application.
Right now I stored the default Object (ship.scn) into my Firebase Storage.
How can I convert the Data, which I get from Firebase, to a SCNNode?
This is my Code right now:
let storage = Storage.storage().reference()
let modelPath = storage.child("models/ship.scn")
print("ModelPath: \(modelPath)")
modelPath.getMetadata { (metaData, error) in
if error != nil {
print("ERROR: ", error!)
}else{
print("Metadata: \(metaData!)")
}
}
// this is what firebase shows for images
// how can i get the Data as SCNNode?
modelPath.getData(maxSize: 1 * 1024 * 1024) { (data, error) in
if error != nil {
print("Error getData: \(error!)")
}else {
print(data)
}
}
I solved this problem by downloading the 3D Object from firebase into the devices document folder.
So when I need the 3D-object I create a reference to the downloaded 3D-Object
write To Directory: (where modelPath is the storage.child('your path') in firebase)
let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as String
let tempDirectory = URL.init(fileURLWithPath: paths, isDirectory: true)
let targetUrl = tempDirectory.appendingPathComponent("ship.scn")
modelPath.write(toFile: targetUrl) { (url, error) in
if error != nil {
print("ERROR: \(error!)")
}else{
print("modelPath.write OKAY")
}
}
load 3D file from directory:
let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as String
let tempDirectory = URL.init(fileURLWithPath: paths, isDirectory: true)
let targetUrl = tempDirectory.appendingPathComponent("\ship.scn")
var sceneForNode: SCNScene? = nil
do {
// load the 3D-Model node from directory path
sceneForNode = try SCNScene(url: targetUrl, options: nil)
}catch{
print(error)
}
// create node to display on scene
let node: SCNNode? = sceneForNode?.rootNode.childNode(withName: "ship", recursively: true)

Wait for 2 callbacks before instantiating an object

I would like to download from firebase:
data issued from a group profile (Firebase realtime DB)
including...
data issued from the group admin profile (Firebase realtime DB)
a group profile image (Firebase Storage)
Then I can instantiate a group object with its data and its image
First approach, I used 3 nested closures that allowed me to get data, and then to get the image.
It did work, but it was quite long to get sequentially all that stuffs from firebase.
So I tried to use GCD in order to push my 2 latest Firebase queries (user data + group image) at the same time (rather than one after the other), and to wait for the last callback to start instantiating my group.
Is it a correct approach ?
If yes, I find some difficulties to implement it...
My issue : returnedUser and returnedGroupImage are always nil
Here is my bunch of code :
static func getGroup(_ groupID:String, completionBlock: #escaping (_ group: Group?) -> ()) {
dataRef.child("data").child("groups").child(groupID).observe(.value, with: { (snapshot) in
if let snapshotValue = snapshot.value {
guard let name = (snapshotValue as AnyObject).object(forKey: "name") as? String else
{
completionBlock(nil)
return
}
guard let adminID = (snapshotValue as AnyObject).object(forKey: "adminID") as? String else
{
completionBlock(nil)
return
}
let queue = DispatchQueue(label: "asyncQueue", attributes: .concurrent, target: .main)
let dispatch_group = DispatchGroup()
var returnedUser: User?
var returnedGroupImage: UIImage?
queue.async (group: dispatch_group) {
FireBaseHelper.getUser(adminID, completionBlock: { (user) in
if user != nil {
returnedUser = user
}
})
}
queue.async (group: dispatch_group) {
FireBaseHelper.getGroupImage(groupID, completionBlock: { (image) in
if image != nil {
returnedGroupImage = image
}
})
}
dispatch_group.notify(queue: DispatchQueue.main) {
// Single callback that is supposed to be executed after all tasks are complete.
if (returnedUser == nil) || (returnedGroupImage == nil) {
// always true !
return
}
let returnedGroup = Group(knownID: (snapshotValue as AnyObject).key, named: name, createdByUser: currentUser!)
returnedGroup.groupImage = returnedGroupImage
completionBlock(returnedGroup)
}
}
})
}
Thanks for your help !
I believe that the way you are using DispatchGroups are not correct.
let dispatch_group = DispatchGroup()
var returnedUser: User?
var returnedGroupImage: UIImage?
dispatch_group.enter()
FireBaseHelper.getUser(adminID, completionBlock: { (user) in
if user != nil {
returnedUser = user
}
dispatch_group.leave()
})
dispatch_group.enter()
FireBaseHelper.getGroupImage(groupID, completionBlock: { (image) in
if image != nil {
returnedGroupImage = image
}
dispatch_group.leave()
})
dispatch_group.notify(queue: DispatchQueue.main) {
// Single callback that is supposed to be executed after all tasks are complete.
if (returnedUser == nil) || (returnedGroupImage == nil) {
// always true !
return
}
let returnedGroup = Group(knownID: (snapshotValue as AnyObject).key, named: name, createdByUser: currentUser!)
returnedGroup.groupImage = returnedGroupImage
completionBlock(returnedGroup)
}

Resources