Deleting Firebase values on a SwiftUI List - swift

I am trying to delete a value from the Firebase Realtime Database, just by swiping on a the list row but I am encountering a lot of problems, for example, when I delete a row, on the Firebase Realtime Database an object with a different id is deleted, I have the following code running when the swipe occurs (Sorry for the lot of prints but I was trying to understand the flow on my program :D):
EDIT: I am wondering how I can retrieve the value of the row in the List and by that using this extension:
extension Dictionary where Value: Equatable {
func someKey(forValue val: Value) -> Key? {
return first(where: { $1 == val })?.key
}
}
And this is the delete code.
.onDelete(perform: { indexSet in
locali.remove(atOffsets: indexSet)
Auth.auth().addStateDidChangeListener { auth, user in
print(auth.currentUser?.email)
print(indexSet.count)
print(indexSet.startIndex)
print(indexSet.endIndex)
var indexCounter: Int = indexSet.first!
//MARK: - delete
ref
.child("locali")
.child(auth.currentUser?.uid ?? "no uid")
.child("nomeLocale\(indexCounter)")
.removeValue (completionBlock: { (error, refer) in
if error != nil {
print(error)
} else {
print(refer)
print("Child Removed Correctly")
}
})
//MARK: - refactor
Task {
var dataSnapshot: DataSnapshot
var refactoredWholeList: [String : NSObject]
do {
dataSnapshot = try await ref
.child("locali")
.child(auth.currentUser?.uid ?? "no uid")
.getData()
print(dataSnapshot)
guard var wholeList = dataSnapshot.value as? [String : NSObject] else {
print("error with the guard first inside onDelete")
return
}
var indexCounterDelete: Int = 0
print("before changing indexes ----> \(wholeList) <----\n")
for indexDelLocale in wholeList {
print("This is the indexDelLocale ==> \(indexDelLocale)")
print("indexCounterDelete: \(indexCounterDelete)")
if var entry = wholeList.removeValue(forKey: indexDelLocale.key) {
print("Trying to sobstitute the index: \(indexDelLocale.key)")
print("With: \("nomeLocale\(indexCounterDelete)") for ==> \(entry)")
try await ref
.child("locali")
.child(auth.currentUser?.uid ?? "no uid")
.updateChildValues(["nomeLocale\(indexCounterDelete)" : entry])
}
print("List count ==> \(wholeListRefactored.count)\n")
if wholeList.count == indexCounterDelete {
print("RETURN")
} else {
indexCounterDelete += 1
}
}
print("\nafter changing indexes ----> \(wholeList) <----")
print("This is the wholeListRefactored ==> \(wholeListRefactored)\n")
} catch {
print("error while retrieving the wholeList")
}
} //: Task
// //MARK: - reupload
// for locale in wholeListRefactored {
// ref
// .child("locali")
// .child(auth.currentUser?.uid ?? "no uid")
// .updateChildValues([locale.key : locale.value])
// }
}
}) //: onDelete

Related

SiwftUI Firebase modifying variable inside snapshot change not working

The following is a function to add a listener to a query. Whenever a document is added/removed I make some changes on two arrays (one of the user Ids and one of the user details). As you can see I tried printing everything: I correctly receive the data whenever it is added/removed, I can retrieve the document ID I need but whenever I append it to the usersReqestedUIDs array it always prints it as empty, even if I try to append a random string in it. Why is that?
func addRequestedUsersSnapshot() {
let db = Firestore.firestore()
let userRef = db.collection("user").document(user.UID)
let userRequestedRef = userRef.collection("friends").whereField("status", isEqualTo: "request")
// First query to fetch all friendIDs
userRequestedRef.addSnapshotListener { querySnapshot, error in
guard let snapshot = querySnapshot else {
print("Error fetching snapshots: (error!)")
return
}
snapshot.documentChanges.forEach { diff in
print("ids : (self.usersReqestedUIDs)")
print("type : (diff.type)")
if diff.type == .added {
print("doc id (diff.document.documentID)")
self.usersReqestedUIDs.append("hello")
print("added (diff.document.data())")
print("ids : (self.usersReqestedUIDs)")
self.fetchUserDetailsByUID(uid: diff.document.documentID) { result in
switch result {
case let .success(user):
self.usersReqestedDetails.append(user)
case let .failure(error):
print(error)
}
}
}
if diff.type == .removed {
print("removed (diff.document.data())")
self.usersReqestedDetails.removeAll(where: { $0.UID == diff.document.documentID })
self.usersReqestedUIDs.removeAll(where: { $0 == diff.document.documentID })
}
if diff.type == .modified {
print("modified (diff.document.data())")
}
}
}
}

How to wait for code inside addSnapshotListener to finish execution before returning to function?

func getStudents() {
var student: Student = Student()
db.collection(StudentViewModel.studentCollection).addSnapshotListener { (querySnapshot, error) in
guard error == nil else {
print("ERROR | Getting Student Documents in Firestore Service: \(String(describing: error))")
return
}
guard let snapshot = querySnapshot else {
// no documents
print("No documents to fetch!")
return
}
DispatchQueue.main.sync {
var updatedStudentDocuments: [Student] = [Student]()
for studentDocument in snapshot.documents {
student = Student(id: studentDocument.documentID,
name: studentDocument.data()["name"] as? String ?? "")
updatedStudentDocuments.append(student)
}
self.students = updatedStudentDocuments
}
}
}
Every time I run this function and check what's inside self.students, I find that it's empty. That's because the function getStudents is returning before the closure in addSnapshotListener finishes executing. How do I make the getStudents function wait for the closure to finish executing before continuing its own execution?

singleton property set with async\await func is nil after called later

a property from a singleton is nil when checkin is value.
calling this function from viewDidLoad like this
Task {
do {
try await CXOneChat.shared.connect(environment: .NA1, brandId: 1111, channelId: "keyID")
self.checkForConfig()
} catch {
print(error.localizedDescription)
}
}
this connect function checks for several issues and load from network the config
public func connect(environment: Environment, brandId: Int, channelId: String) async throws {
self.environment = environment
self.brandId = brandId
self.channelId = channelId
try connectToSocket()
channelConfig = try await loadChannelConfiguration()
generateDestinationId()
generateVisitor()
if customer == nil {
customer = Customer(senderId: UUID().uuidString, displayName: "")
try await authorizeCustomer()
} else if authorizationCode.isEmpty{
try await reconnectCustomer()
} else {
try await authorizeCustomer()
}
}
this work ok the self.checkForConfig does some stuff with config in singleton object.
after tapping a button and go for another ViewController. call this func
func loadThread() {
do {
if CXOneChat.shared.getChannelConfiguration()?.settings.hasMultipleThreadsPerEndUser ?? false {
try CXOneChat.shared.loadThreads()
} else {
try CXOneChat.shared.loadThread()
}
} catch {
print(error)
}
}
depending on the config call one or another config value load one or the function. in this case the getChannelConfiguration() is nil and call to loadThread().
func loadThread(threadId: UUID? = nil) throws {
guard let config = channelConfig else {
throw CXOneChatError.missingChannelConfig
}
let eventType = config.isLiveChat ? EventType.recoverLivechat : EventType.recoverThread
guard let brandId = brandId else { throw CXOneChatError.invalidBrandId }
guard let channelId = channelId else { throw CXOneChatError.invalidChannelId }
guard let id = getIdentity(with: false) else { throw CXOneChatError.invalidCustomerId }
let retrieveThread = EventFactory.shared.recoverLivechatThreadEvent(brandId: brandId, channelId: channelId, customer: id, eventType: eventType, threadId: threadId)
guard let data = getDataFrom(retrieveThread) else { throw CXOneChatError.invalidData }
let string = getStringFromData(data)
socketService.send(message: string)
}
here checks for he config in singleton object but throws error because config is nil.
my question is why is nil config in this check. the config is downloaded and store. but when get the values got a null value. Im missing some code here or what I is wrong with this.

Firebase Storage listAll() body not executed

I am new to Firebase and Swift. My previous question was very vague due to a misunderstanding on my part. In a class named "A" for example I am trying to create an object of class "B" that contains the fetchARImageTargets function that I have below. I am trying to assign the array ARImageTargets to a var in class "A" however, the listAll completion is not returned in time, which results in the var being empty. Is there a way that I can edit my function or class to avoid the var being set prematurely?
let ARImageTargetStorageRef = Storage.storage().reference().child("ImageTargets")
self.fetchARImageTargets(ref: ARImageTargetStorageRef)
func fetchARImageTargets(ref: StorageReference) {
ref.listAll { (result, error) in
if let error = error {
print(error)
}
for prefix in result.prefixes {
self.fetchARImageTargets(ref: prefix)
}
for item in result.items {
item.getMetadata { (metadata, error) in
if let error = error {
print(error)
} else {
var imageTarget = ARImageTarget()
item.downloadURL(completion: { (url, error) in
imageTarget.ImageURL = url
})
imageTarget.Id = metadata?.customMetadata?["Id"] as String?
let width = metadata?.customMetadata?["PhysicalWidth"] as String?
imageTarget.PhysicalWidth = CGFloat(truncating: NumberFormatter().number(from: width!)!)
self.ARImageTargets.append(imageTarget)
}
}
}
}
}

Future Combine sink does not recieve any values

I want to add a value to Firestore. When finished I want to return the added value. The value does get added to Firestore successfully. However, the value does not go through sink.
This is the function that does not work:
func createPremium(user id: String, isPremium: Bool) -> AnyPublisher<Bool,Never> {
let dic = ["premium":isPremium]
return Future<Bool,Never> { promise in
self.db.collection(self.dbName).document(id).setData(dic, merge: true) { error in
if let error = error {
print(error.localizedDescription)
} else {
/// does get called
promise(.success(isPremium))
}
}
}.eraseToAnyPublisher()
}
I made a test function that works:
func test() -> AnyPublisher<Bool,Never> {
return Future<Bool,Never> { promise in
promise(.success(true))
}.eraseToAnyPublisher()
}
premiumRepository.createPremium(user: userID ?? "1234", isPremium: true)
.sink { receivedValue in
/// does not get called
print(receivedValue)
}.cancel()
test()
.sink { recievedValue in
/// does get called
print("Test", recievedValue)
}.cancel()
Also I have a similar code snippet that works:
func loadExercises(category: Category) -> AnyPublisher<[Exercise], Error> {
let document = store.collection(category.rawValue)
return Future<[Exercise], Error> { promise in
document.getDocuments { documents, error in
if let error = error {
promise(.failure(error))
} else if let documents = documents {
var exercises = [Exercise]()
for document in documents.documents {
do {
let decoded = try FirestoreDecoder().decode(Exercise.self, from: document.data())
exercises.append(decoded)
} catch let error {
promise(.failure(error))
}
}
promise(.success(exercises))
}
}
}.eraseToAnyPublisher()
}
I tried to add a buffer but it did not lead to success.
Try to change/remove .cancel() method on your subscriptions. Seems you subscribe to the publisher, and then immediately cancel the subscription. The better option is to retain and store all your subscriptions in the cancellable set.