Parsing CloudKit Error (CKError) - ios
I'm using CloudKit and I'm checking if a specific zone was already created.
For this example, let's say that a zone isn't set, so CloudKit retrieves me a CKError.
This CKError has a property called partialErrorsByItemID which is of type [AnyHashable : Error]?
Here's the code:
fileprivate func checkIfZonesWereCreated() {
let privateDB = CKContainer.default().privateCloudDatabase
let op = CKFetchRecordZonesOperation(recordZoneIDs: [zoneID1, zoneID2])
op.fetchRecordZonesCompletionBlock = { (dict, err) in
if let err = err as? CKError, let _err = err.partialErrorsByItemID {
print(_err)
/* prints
[AnyHashable(<CKRecordZoneID: 0x60800003cba0; ownerName=__defaultOwner__, zoneName=TestZone>): <CKError 0x60400005a760: "Zone Not Found" (26/2036); server message = "Zone 'TestZone' does not exist"; uuid = ...-2DF4E13F81E2; container ID = "iCloud.com.someContainer">]
*/
// If I iterate through the dictionary
_err.forEach({ (k, v) in
print("key:", k) // prints: key: <CKRecordZoneID: 0x60800002d9e0; ownerName=__defaultOwner__, zoneName=TestZone>
print("value:", v) // prints: value: <CKError 0x60400005a760: "Zone Not Found" (26/2036); server message = "Zone 'TestZone' does not exist"; uuid = ...-2DF4E13F81E2; container ID = "iCloud.com.someContainer
})
return
}
print("dict:", dict)
}
privateDB.add(op)
}
How do I parse this error? I need to access the zoneName ?
The key in _err is a CKRecordZoneID. Once you have that, use the zoneName property to get the zone name.
I would write your code as follows:
fileprivate func checkIfZonesWereCreated() {
let privateDB = CKContainer.default().privateCloudDatabase
let op = CKFetchRecordZonesOperation(recordZoneIDs: [zoneID1, zoneID2])
op.fetchRecordZonesCompletionBlock = { (dict, err) in
if let err = err as? CKError {
switch err {
case CKError.partialFailure:
if let _err = err.partialErrorsByItemID {
for key in _err.keys {
if let zone = key as? CKRecordZoneID {
let name = zone.zoneName
print("Missing zone: \(name)")
}
}
return
}
default:
break
}
}
print("dict:", dict)
}
privateDB.add(op)
}
Related
How to fetch contacts and store in array on iOS?
I am working on a Tinder Swiping application for iOS Contacts. I have imported the contacts library and successfully have gotten the contacts to print in the console. However, I am trying to dynamically add those names to a card like Tinder. I have created a model class to hold the name, however, I am unable to append my data to that model. struct ContactInfo { var name: String } let contactInfo = [ContactInfo]() func fetchContacts(){ let contactStore = CNContactStore() var contacts = [CNContact]() let keys = [CNContactFormatter.descriptorForRequiredKeys(for: .fullName)] let request = CNContactFetchRequest(keysToFetch: keys) do { try contactStore.enumerateContacts(with: request) { (contact, stop) in contacts.append(contact) var info = contact.givenName + " " + contact.familyName print(info) } } catch { print(error.localizedDescription) } } var userModels : [ContactInfo] = { var model : [ContactInfo] = [] for n in 1...10 { model.append(ContactInfo(name: names[Int(arc4random_uniform(UInt32(names.count)))])) } return model }() I would like all of my contacts to append to the model variable which is then returned to the cards.
As my understanding, the enumerateContacts is asynchronous. It means that, when your app is executing the line to create the userModels array, the contacts aren't fetched yet : so your array stays empty. I would try to move the userModels array creation in another controller, where you are displaying your cards. To achieve that, you can use a delegate, that receives the fetched contacts as a parameter. Then, you can assign your array with this parameter content and create your cards' content. Here is a great tutorial on how to use a delegate with Swift. Hope this will help you.
I have created helper class for that. That might help you. Example ContactSyncHelper.sharedInstance.getAllContacts { (contacts, error) in if error { print("error") }else { print(contacts) } } ContactSyncHelper import UIKit import Foundation import Contacts let kphone_number = "phone_number" let kcountry_code = "country_code" class ContactSyncHelper: NSObject { static let sharedInstance: ContactSyncHelper = { let instance = ContactSyncHelper() // setup code return instance }() // MARK: - Initialization Method override init() { super.init() } //Example // ContactSyncHelper.sharedInstance.getAllContacts { (contacts, error) in // if error { // print("error") // }else { // print(contacts) // } // } func getAllContacts(completion: ([NSMutableDictionary],Bool) -> ()) { switch CNContactStore.authorizationStatus(for: .contacts) { // Update our UI if the user has granted access to their Contacts case .authorized: break // Prompt the user for access to Contacts if there is no definitive answer case .notDetermined : completion([],true) break // Display a message if the user has denied or restricted access to Contacts case .denied, .restricted: //CommData.showAlert(self, withMsg: "Permission was not granted for Contacts.", withTitle: "Privacy Warning!", action: nil) completion([],true) break } let contactStore = CNContactStore() let keysToFetch = [ CNContactFormatter.descriptorForRequiredKeys(for: .fullName), CNContactEmailAddressesKey, CNContactPhoneNumbersKey, CNContactImageDataAvailableKey, CNContactThumbnailImageDataKey] as [Any] // Get all the containers var allContainers: [CNContainer] = [] do { allContainers = try contactStore.containers(matching: nil) } catch { print("Error fetching containers") } var arrayNumbers: [NSMutableDictionary] = [] for container in allContainers { let fetchPredicate = CNContact.predicateForContactsInContainer(withIdentifier: container.identifier) do { let containerResults = try contactStore.unifiedContacts(matching: fetchPredicate, keysToFetch: keysToFetch as! [CNKeyDescriptor]) containerResults.forEach { (contact:CNContact) in contact.phoneNumbers.forEach { (justPhone:CNLabeledValue) in let numberValue = justPhone.value let countryCode = numberValue.value(forKey: "countryCode") as? String var strphone = numberValue.stringValue strphone = strphone.replacingOccurrences(of: "(", with: "") strphone = strphone.replacingOccurrences(of: ")", with: "") strphone = strphone.replacingOccurrences(of: "-", with: "") strphone = strphone.replacingOccurrences(of: "+", with: "") strphone = strphone.components(separatedBy: .whitespaces).joined() if strphone.hasPrefix("0"){ strphone.remove(at: (strphone.startIndex)) } if(countryCode != nil) { var countryCode1:String = self.getCountryPhonceCode(country: countryCode!) if strphone.hasPrefix(countryCode1) { strphone = strphone.deletingPrefix(countryCode1) } countryCode1 = "+\(countryCode1)" let dict = NSMutableDictionary() dict.setValue(strphone, forKey: kphone_number) dict.setValue(countryCode1, forKey: kcountry_code) arrayNumbers.append(dict) } } } } catch { print("Error fetching results for container") } } completion(arrayNumbers,false) } func getCountryPhonceCode (country : String) -> String { if country.count == 2 { let x : [String] = ["972","IL","93","AF","355","AL","213","DZ","1","AS","376","AD","244","AO","1","AI","1","AG","54","AR","374","AM","297","AW","61","AU","43","AT","994","AZ","1","BS","973","BH","880","BD","1","BB","375","BY","32","BE","501","BZ","229","BJ","1","BM","975","BT","387","BA","267","BW","55","BR","246","IO","359","BG","226","BF","257","BI","855","KH","237","CM","1","CA","238","CV","345","KY","236","CF","235","TD","56","CL","86","CN","61","CX","57","CO","269","KM","242","CG","682","CK","506","CR","385","HR","53","CU" ,"537","CY","420","CZ","45","DK" ,"253","DJ","1","DM","1","DO","593","EC","20","EG" ,"503","SV","240","GQ","291","ER","372","EE","251","ET","298","FO","679","FJ","358","FI","33","FR","594","GF","689","PF","241","GA","220","GM","995","GE","49","DE","233","GH","350","GI","30","GR","299","GL","1","GD","590","GP","1","GU","502","GT","224","GN","245","GW","595","GY","509","HT","504","HN","36","HU","354","IS","91","IN","62","ID","964","IQ","353","IE","972","IL","39","IT","1","JM","81","JP","962","JO","77","KZ","254","KE","686","KI","965","KW","996","KG","371","LV","961","LB","266","LS","231","LR","423","LI","370","LT","352","LU","261","MG","265","MW","60","MY","960","MV","223","ML","356","MT","692","MH","596","MQ","222","MR","230","MU","262","YT","52","MX","377","MC","976","MN","382","ME","1","MS","212","MA","95","MM","264","NA","674","NR","977","NP","31","NL","599","AN","687","NC","64","NZ","505","NI","227","NE","234","NG","683","NU","672","NF","1","MP","47","NO","968","OM","92","PK","680","PW","507","PA","675","PG","595","PY","51","PE","63","PH","48","PL","351","PT","1","PR","974","QA","40","RO","250","RW","685","WS","378","SM","966","SA","221","SN","381","RS","248","SC","232","SL","65","SG","421","SK","386","SI","677","SB","27","ZA","500","GS","34","ES","94","LK","249","SD","597","SR","268","SZ","46","SE","41","CH","992","TJ","66","TH","228","TG","690","TK","676","TO","1","TT","216","TN","90","TR","993","TM","1","TC","688","TV","256","UG","380","UA","971","AE","44","GB","1","US","598","UY","998","UZ","678","VU","681","WF","967","YE","260","ZM","263","ZW","591","BO","673","BN","61","CC","243","CD","225","CI","500","FK","44","GG","379","VA","852","HK","98","IR","44","IM","44","JE","850","KP","82","KR","856","LA","218","LY","853","MO","389","MK","691","FM","373","MD","258","MZ","970","PS","872","PN","262","RE","7","RU","590","BL","290","SH","1","KN","1","LC","590","MF","508","PM","1","VC","239","ST","252","SO","47","SJ","963","SY","886","TW","255","TZ","670","TL","58","VE","84","VN","284","VG","340","VI","678","VU","681","WF","685","WS","967","YE","262","YT","27","ZA","260","ZM","263","ZW"] var keys = [String]() var values = [String]() let whitespace = NSCharacterSet.decimalDigits //let range = phrase.rangeOfCharacterFromSet(whitespace) for i in x { // range will be nil if no whitespace is found // if (i.rangeOfCharacterFromSet(whitespace) != nil) { if (i.rangeOfCharacter(from: whitespace, options: String.CompareOptions.caseInsensitive) != nil) { values.append(i) } else { keys.append(i) } } let countryCodeListDict:NSDictionary = NSDictionary(objects: values as [String], forKeys: keys as [String] as [NSCopying]) if let _: AnyObject = countryCodeListDict.value(forKey: country.uppercased()) as AnyObject { return countryCodeListDict[country.uppercased()] as! String } else { return "" } } else { return "" } } }
How to get custom value back from Spotlight with CSCustomAttributeKey
I am trying to get some data back from Core Spotlight which I am storing using a custom attribute key. Tested this on macOS and iOS as well, the result is always the same. My test class: import CoreSpotlight class SpotlightSearch { let domainId = "com.company.some" let originalDataKeyName: String init() { self.originalDataKeyName = domainId.replacingOccurrences(of: ".", with: "_") + "_originalData" } func addToIndex(title: String, content: String) { guard let originalDataKey = CSCustomAttributeKey(keyName: originalDataKeyName, searchable: false, searchableByDefault: false, unique: false, multiValued: false) else { return } let uniqueId = "MyUniqueId" + title let originalContent = NSString(string: content) let attributeSet = CSSearchableItemAttributeSet(itemContentType: kUTTypeText as String) attributeSet.title = title attributeSet.setValue(originalContent, forCustomKey: originalDataKey) let item = CSSearchableItem(uniqueIdentifier: uniqueId, domainIdentifier: domainId, attributeSet: attributeSet) CSSearchableIndex.default().indexSearchableItems([item]) { error in if let error = error { print("Indexing error: \(error.localizedDescription)") } else { print("Item '\(title)' successfully indexed!") } } } var query: CSSearchQuery? func search(title: String) { var allItems = [CSSearchableItem]() let queryString = "title == '\(title)'cd" let attributes = [ "title", originalDataKeyName ] let newQuery = CSSearchQuery(queryString: queryString, attributes: attributes) newQuery.foundItemsHandler = { (items: [CSSearchableItem]) -> Void in allItems.append(contentsOf: items) } newQuery.completionHandler = { [weak self] (error: Error?) -> Void in guard let originalDataKeyName = self?.originalDataKeyName, let originalDataKey = CSCustomAttributeKey(keyName: originalDataKeyName) else { return } print("Search complete") for item in allItems { let attributeSet = item.attributeSet let customData = attributeSet.value(forCustomKey: originalDataKey) // Always nil if customData == nil { print("\(String(describing: originalDataKeyName)) not found in \(attributeSet.description)") } else if let originalData = customData as? NSData { let data = Data(referencing: originalData) if let originalString = String(data: data, encoding: .utf8) { print("Found '\(originalString)'") } } } } query = newQuery newQuery.start() } } On app init: let newSpotlightSearch = SpotlightSearch() newSpotlightSearch.addToIndex(title: "Banana", content: "🍌") Later: spotlightSearch.search(title: "Banana") It will find the title, but will not give me back the custom attribute value. If I put a breakpoint after "// Always nil" and use po attributeSet I will get (lldb) po attributeSet { "_kMDItemBundleID" = "de.axelspringer.SearchMac"; "_kMDItemDomainIdentifier" = "com.company.some"; "_kMDItemExpirationDate" = "2018-08-26 00:00:00 +0000"; "_kMDItemExternalID" = MyUniqueIdBanana; "com_company_some_originalData" = "\Ud83c\Udf4c"; kMDItemTitle = Banana; } So the value is there, but Spotlight will not return it to me. Already tried to use NSData instead of NSString for the custom attribute, but same result. Also found this orphaned question in the Apple developer forums: CSCustomAttributeKey valueForCustomKey not working
I believe it's iOS issue. While it's not fixed, maybe Apple will allow you to use a private API to do your thing. So, attributeSet has private Dictionaries attributes and customAttributes. You can try to get those values using Key Value Coding and ObjC: NSDictionary *attributes = [attributeSet valueForKey:#"attributes"]; id customData = attributes[originalDataKeyName]; OR NSDictionary *customAttributes = [attributeSet valueForKey:#"customAttributes"]; id customData = customAttributes[originalDataKeyName]; Key type in those dictionaries is either NSString* or CSCustomAttributeKey*, so you can try supplying both originalDataKeyName and originalDataKey.
How to know if a Firebase has fetched a value
I have three functions getNewOrder(),storeOrderDetails(_ details:[String:String]) and getUserInfo(_ userID:String). Function getNewOrder() is called first. It fetches new orders ( .childAdded values) and sends the dictionary to storeOrderDetails(_ details:[String:String]). storeOrderDetails(_ details:[String:String])then segregate all the values and callsgetUserInfo(_ userID:String)` by passing it userID which was present in its details. getUserInfo(_ userID:String) then fetches users details and returns user's information. However, the problem is [ userInfo = getUserInfo(_ userID:String) in storeOrderDetails(_ details:[String:String]) ] userInfo is always empty. Apparently func getUserInfo(_ userID:String) goes into a completion block after it has returned empty value. I want these three functions to execute in sequential way. Any advice is highly appreciated. Please follow the below Links to review my code. https://imgur.com/hNjvyDk https://imgur.com/J0LMXMg func childAdded(){ let ref = Database.database().reference().child("Orders").child(todaysDate) ref.observe(.childAdded) { (snapshot) in var details = [String:String]() if let orderID = snapshot.key as? String { ref.child(orderID).observeSingleEvent(of: .value, with: { (snap) in self.newOrderTextView.text = "" self.customerNameLabel.text = "" self.customerPhoneLabel.text = "" self.orderNumberLabel.text = "" let enumerator = snap.children while let rest = enumerator.nextObject() as? DataSnapshot { details[rest.key as? String ?? ""] = rest.value as? String ?? "" } self.storeUserDetails(details) }) } } } func storeUserDetails(_ details:[String:String]){ if details["CustomerID"] != nil { userInfo = getUserDetails(details["CustomerID"]!) print(userInfo) } if !userInfo.isEmpty{ let order = OrderDatabase() order.customerEmail = userInfo["Email"]! order.customerName = userInfo["Name"]! order.orderAcceptStatus = details["OrderStatus"]! order.customerOrderNumber = details["orderNumber"]! order.orderID = details["orderID"]! order.time = details["Time"]! order.customerFirebaseID = details["CustomerID"]! self.orderDatabase[details["orderNumber"]!] = order self.orderTable.reloadData() } } func getUserDetails(_ userID:String) -> [String:String]{ var details = [String:String]() let userDetailsReference = Database.database().reference().child("Users") userDetailsReference.child(userID).observeSingleEvent(of: DataEventType.value, with: { (snapshot) in if let dictionary = snapshot.value as? NSDictionary { self.customerNameLabel.text = dictionary.value(forKey: "Name") as? String self.customerPhoneLabel.text = dictionary.value(forKey: "Email") as? String details["Name"] = dictionary.value(forKey: "Name") as? String details["Email"] = dictionary.value(forKey: "Email") as? String } }) return details }
From what I can see here, I am betting that the issue you are facing has to do with the fact that the methods are asynchronous. So one thing is not completely finished and some other method gets fired too soon. There are a few ways to deal with this issue. One is completion handlers, and the other is adding observers. Below is an example of doing both for Firebase. Here I'm asking a getLocationPhotos method to get all the photos from Firebase. Notice the observers and completion handler func getLocationPhotos(coordinate:CLLocationCoordinate2D){ dbHandler.getImageFileNames(coordinateIn: coordinate) { (filenames) in if filenames.isEmpty { log.debug(String.warningGet + "filenames is empty") return }//if filenames.isEmpty self.imageFiles = filenames.filter { $0 != "none" } if self.imageFiles.isEmpty { log.error(String.errorGet + "imageFiles array is empty") return }//if imageFiles.isEmpty for file in self.imageFiles { let reference = self.storageHandler.imageReference.child(file) let download = self.imageView.sd_setImage(with: reference) if let i = self.imageView.image { self.imageArray.append(i) self.collectionView.reloadData() }//let i download?.observe(.progress, handler: { (snapshot) in guard let p = snapshot.progress else { return }//let p self.progressView.progress = Float(p.fractionCompleted) if self.progressView.progress == Float(1) { self.progressView.isHidden = true } })//progress download?.observe(.success, handler: { (snapshot) in self.progressView.progress = 1 self.progressView.isHidden = true self.collectionView.setNeedsLayout() })//success download?.observe(.failure, handler: { (snapshot) in log.error(String.errorGet + "Error occured getting data from snapshot") })//failure }//for file }//dbHandler
Wrong read from Firebase
I am uploading a product to Firebase using this code : let storageRef = Storage.storage().reference().child("ProductsImages").child(product.UniqueID()).child("MainImage.png") if let mainChosenImage = self.selectedImageToUpload { if let uploadData = UIImageJPEGRepresentation(mainChosenImage, 0.2) { storageRef.putData(uploadData, metadata: nil) { (StorageMetaData, error) in if error != nil { print(error) return } self.mainImageURL = StorageMetaData?.downloadURL()?.absoluteString if let urlString = self.mainImageURL { self.ref.child("Products").child(product.UniqueID()).child("MainImage").setValue(urlString) self.ref.child("Users").child(user.uid).child("Products").child(product.UniqueID()).child("MainImage").setValue(urlString) product.AddImageURLToProduct(URL: urlString) } } } } product.RegisterProductForAllUsers(database: self.ref) product.RegisterProductForAddingUser(database: self.ref) self.performSegue(withIdentifier: "unwindToMyProductsViewController", sender: self) Now I know that writing an image like this is async (1), but after item is added (Let's say we ignore picture for now), I have this in Firebase: saved Firebase Product But when I go back to my collectionView and load the information (It happens in the ViewDidLoad method), this is the information I read: Product information read This is my code for ViewDidLoad: if let currentUserID = loggedOnUserID { // Retrieve the products and listen for changes databaseHandle = ref?.child("Users").child(currentUserID).child("Products").observe(.childAdded, with: { (snapshot) in // Code to execute when new product is added let prodValue = snapshot.value as? NSDictionary let prodName = prodValue?["Name"] as? String ?? "" let prodPrice = prodValue?["Price"] as? Double ?? -1 let prodDesc = prodValue?["Description"] as? String ?? "" let prodURLS = prodValue?["MainImage"] as? String ?? "" let prodAmount = prodValue?["Amount"] as? Int ?? 0 let prodID = snapshot.key let prodToAddToView = Product(name: prodName, price: prodPrice, currency: "NIS", description: prodDesc, location: "IL", toSell: false, toBuy: false, owner: currentUserID, uniqueID: prodID, amount: prodAmount) if (prodURLS != "") { prodToAddToView.AddImageURLToProduct(URL: prodURLS) } self.products.append(prodToAddToView) DispatchQueue.main.async { self.MyProductsCollection.reloadData() } } ) // Closes observe function Also - my code writing to Database : public func RegisterProductForAllUsers(database dataBase: DatabaseReference) { dataBase.child("Products").child(self.UniqueID()).child("Name").setValue(self.Name()) dataBase.child("Products").child(self.UniqueID()).child("UniqueID").setValue(self.UniqueID()) dataBase.child("Products").child(self.UniqueID()).child("Price").setValue(self.Price()) dataBase.child("Products").child(self.UniqueID()).child("Description").setValue(self.Description()) dataBase.child("Products").child(self.UniqueID()).child("ToBuy?").setValue(self.m_ToBuy) dataBase.child("Products").child(self.UniqueID()).child("ToSell?").setValue(self.m_ToSell) dataBase.child("Products").child(self.UniqueID()).child("Owner").setValue(self.m_Owner) dataBase.child("Products").child(self.UniqueID()).child("Amount").setValue(self.m_Amount) dataBase.child("Products").child(self.UniqueID()).child("MainImage").setValue(self.m_PicturesURLs.first) } I am writing "Name" first, which is maybe the reason I only read name properly? Is there a way to make all these writings be atomic ? with only 1 value for some reason. (2) 1) Any way to make it sync ? 2) How can I read the proper values ?
leaky listener firebase ios
I am trying to load message box data for chat functionality. The message box is loaded as: override func viewDidAppear(animated: Bool) { super.viewDidAppear(animated) if (PFUser.currentUser()!["firebaseUID"] !== nil) { print(PFUser.currentUser()!["firebaseUID"]) self.updateResultArray(PFUser.currentUser()!["firebaseUID"] as! String) } } func updateResultArray(uid: String) { let userName = String(PFUser.currentUser()!["username"]) //print("updateResultArray is getting called") let userhandle = self.firebase.childByAppendingPath("users").childByAppendingPath(uid).childByAppendingPath("rooms").queryOrderedByValue() .observeSingleEventOfType(.Value, withBlock: { roomsnapshot in let enumerator = roomsnapshot.children while let rest = enumerator.nextObject() as? FDataSnapshot { self.roomArray.append(rest.key) } //get the latest message from all the rooms if self.roomArray.isEmpty == false { for i in 0...self.roomArray.count-1 { print("in the room loop \(self.roomArray[i])") let messagehandle = self.messagesRef.childByAppendingPath(self.roomArray[i]).queryOrderedByKey().queryLimitedToFirst(1).observeSingleEventOfType(.Value, withBlock: { messagesnapshot in print("the messagesnapshot child count is \(messagesnapshot.childrenCount)") let enumerator = messagesnapshot.children while let rest = enumerator.nextObject() as? FDataSnapshot { let sender = rest.value.objectForKey("sender") as? String let reciever = rest.value.objectForKey("reciever") as? String //print("sender is \(sender!) and reciever is \(reciever!)") let eventhandle = self.firebase.childByAppendingPath("rooms").childByAppendingPath(self.roomArray[i]).observeSingleEventOfType(.Value, withBlock: { eventsnapshot in if eventsnapshot.value is NSNull { // The value is null } else { let eventAttr = eventsnapshot.value.objectForKey("eventAttributes") as? String let eventDetails = eventsnapshot.value.objectForKey("eventDetails") as? String //print("userName is \(userName)") //print("sender is \(sender)") if (userName != sender!) //for event joinee { let firstname1 = eventsnapshot.value.objectForKey("firstname1") as? String self.otherNames.append(sender!) self.resultsNameArray.append(firstname1!) self.base4String = eventsnapshot.value.objectForKey("img1") as! String self.resultsImageFiles.append(self.base4String) } else //for event creator { let firstname2 = eventsnapshot.value.objectForKey("firstname2") as? String self.otherNames.append(reciever!) self.resultsNameArray.append(firstname2!) self.base4String = eventsnapshot.value.objectForKey("img2") as! String self.resultsImageFiles.append(self.base4String) } let newlineChars = NSCharacterSet.newlineCharacterSet() let evntArray = eventDetails!.componentsSeparatedByCharactersInSet(newlineChars).filter{!$0.isEmpty} self.eventArray.append(evntArray[0]) self.eventdetailsArray.append(eventAttr!) dispatch_async(dispatch_get_main_queue()) { () -> Void in self.resultsTable.reloadData() } } }) // self.firebase.removeAuthEventObserverWithHandle(eventhandle) } }) //self.messagesRef.removeAuthEventObserverWithHandle(messagehandle) } } }) //firebase.removeAuthEventObserverWithHandle(userhandle) } since i am using observeSingleEventOfType i havent coded to remove handlers( i have tried that as well). In the individual chat, the code is like this: func refreshResults() { print("the roomid is \(roomid)") //update from firebase let messagehandle = self.messagesRef.childByAppendingPath(roomid).queryOrderedByKey() .observeEventType(.Value, withBlock: { messageTextsnapshot in self.messageArray.removeAll() self.senderArray.removeAll() // print("the messageTextsnapshot child count is \(messageTextsnapshot.childrenCount)") // I got the expected number of items let enumerator = messageTextsnapshot.children while let rest = enumerator.nextObject() as? FDataSnapshot { let text = rest.value.objectForKey("message") as? String let sender = rest.value.objectForKey("sender") as? String if text != nil && text != "" { self.messageArray.append(text!) self.senderArray.append(sender!) } } for subView in self.resultsScrollView.subviews { subView.removeFromSuperview() } for var i = 0; i <= self.messageArray.count-1; i++ { if self.senderArray[i] == userName { if (self.messageArray[i].rangeOfString(self.acceptMessage) != nil) { let chatBubbleData = ChatBubbleData(text: self.messageArray[i], image:self.myImg, date: NSDate(), type: .AcceptMine) self.addChatBubble(chatBubbleData) } else { let chatBubbleData = ChatBubbleData(text: self.messageArray[i], image:self.myImg, date: NSDate(), type: .Mine) self.addChatBubble(chatBubbleData) } } else { if (self.messageArray[i].rangeOfString(self.acceptMessage) != nil) { let chatBubbleData = ChatBubbleData(text: self.messageArray[i], image:self.otherImg, date: NSDate(), type: .Accept) self.addChatBubble(chatBubbleData) } else { let chatBubbleData = ChatBubbleData(text: self.messageArray[i], image:self.otherImg, date: NSDate(), type: .Opponent) self.addChatBubble(chatBubbleData) } } let bottomOffset:CGPoint = CGPointMake(0, self.resultsScrollView.contentSize.height - self.resultsScrollView.bounds.size.height) self.resultsScrollView.setContentOffset(bottomOffset, animated: false) } }) self.messagesRef.removeAuthEventObserverWithHandle(messagehandle) } There are a few other listeners similar to this. the problem is when i go back from this view(individual chat to message box, the memory consumption increases. I have cleared all arrays and closed the handlers immediately after use. but still memory consumption increases and sometimes in message box same rows are replicated again. how should i solve this. I tried using observeSingleEventOfType but it is not a correct solution as the data sync stops. Used this as reference: https://www.firebase.com/blog/2015-10-15-best-practices-uiviewcontroller-ios-firebase.html
It looks like your message box object is not being released due to a retain cycle caused by the listener callback block holding a reference to the message box object. You can alleviate this by using [weak self] in blocks that you pass to other objects. For example: .observeSingleEventOfType(.Value, withBlock: { [weak self] roomsnapshot in let enumerator = roomsnapshot.children ... This makes 'self' an optional type, and you can then add: guard let strongSelf = self else { ... }
The problem was that i was closing the listeners on the parent and not on the child. so the listeners were still in memory. When i closed the listeners on the full path it worked.