How to convert NSManagedObject Array into String Array - Swift 3 - ios
I want to convert NSManagedObject Array into String Array for Search purposes. (I don't want to use predicates)
The search would be performed on an attribute "Notes" of an entity "Documents"
var documents: [NSManagedObject] = [] // data is saved in this after fetch request !
var filtered = [String]()
var dataa = [String]()
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
//the line below is not working
// dataa = [String(describing:documents)]
filtered = dataa.filter({ (text) -> Bool in
let tmp: NSString = text as NSString
// let range = tmp.rangeOfString(searchText, options: NSString.CompareOptions.CaseInsensitiveSearch)
let range = tmp.range(of: searchText, options: NSString.CompareOptions.caseInsensitive)
return range.location != NSNotFound
})
if(filtered.count == 0){
searchActive = true;
} else {
searchActive = true;
}
self.tableView.reloadData()
}
fetched data is stored into documents (NSManagedObject) through this function & it is called on viewDidLoad()
func fetchDocuments() {
let context = getContext()
let fetchRequest = NSFetchRequest<NSManagedObject>(entityName: "Documents")
//PLEASE TELL ME SOME CODE TO SAVE ALL FETCHED NOTES IN "dataa" SO THAT I CAN PERFORM FILTERING.
// It would be done like obtaining value from the nsmanagedobject via (abc.value(forKeyPath: "note") as? String)!
do {
documents = try context.fetch(fetchRequest)
self.tableView.reloadData()
} catch let error as NSError {
let errorDialog = UIAlertController(title: "Error!", message: "Failed to save! \(error): \(error.userInfo)", preferredStyle: .alert)
errorDialog.addAction(UIAlertAction(title: "Cancel", style: .cancel))
present(errorDialog, animated: true)
}
}
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 "" } } }
Filter searchText to tableView
Right now I am using following let data = ["New York, NY", "Los Angeles, CA" . . .] var filteredData: [String]! filteredData = data But I want to use Firebase, with an almost identical structure, by using this var data = [Categories]() (This is categories) struct Categories { let key:String! let content:String! let itemRef:FIRDatabaseReference? init (content:String, key:String = "") { self.key = key self.content = content self.itemRef = nil } init (snapshot:FIRDataSnapshot) { key = snapshot.key itemRef = snapshot.ref if let CategoriesContent = snapshot.value!["content"] as? String { content = CategoriesContent } else { content = "" } } } So that when I search for something these lines is supposed to filter out everything that aren't correct func searchBar(searchBar: UISearchBar, textDidChange searchText: String) { // Unhide tableview, this will be changed to another method tableView.hidden = false filteredData = searchText.isEmpty ? data : data.filter({(dataString: String) -> Bool in // If dataItem matches the searchText, return true to include it return dataString.rangeOfString(searchText) != nil }) tableView.reloadData() } But since filter({(dataString: String) only takes strings it does not work Question : Is there any other way to replace the string with my Firebase struct? Thanks a lot!
this tutorial is so clear in UISearchResultsUpdating and Filtering section.
Displaying Search Results in UITableView
I have a UITableViewController and I've added a search bar as the header view. I'm trying to get the table view data to reload when the text in the search bar changes but it's not working. Here is my textDidChange method. For context, jsonArray is [[String: AnyObject]] and is passed from another view controller. The searchResults array is then set to jsonArray in viewDidLoad() and the table view uses searchResults as it's data source. func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) { if searchText.characters.count == 0 { searchResults?.removeAll() searchResults = jsonArray! } else { searchResults?.removeAll() for market in jsonArray! { let titleRange = market["name"]?.range(of: searchText, options: .caseInsensitive) var address = String() var city = String() var state = String() var zip = Int() if let address1 = market["address1"] as? String { address = address1 } if let city1 = market["city"] as? String { city = city1 } if let state1 = market["state"] as? String { state = state1 } if let zip1 = market["zip"] as? Int { zip = zip1 } let zipCode = String(describing: zip) let addressString = String("\(address) " + "\(city), " + "\(state) " + "\(zipCode)") let addressRange = addressString?.range(of: searchText, options: .caseInsensitive) if titleRange != nil { searchResults?.append(market) } else if addressRange != nil { searchResults?.append(market) } } } tableView.reloadData() } I've added this print statement at the end of the else statement after the for loop and it's showing that the array contains all of the objects it contained originally so it looks like my ranges aren't working correctly. print("search array: \(searchResults?.count)")
I fixed this by using NSRange instead of Range in Swift. let titleNSRange: NSRange = (market["name"]?.range(of: searchText, options: .caseInsensitive))! let addressNSRange: NSRange = (addressNSString.range(of: searchText, options: .caseInsensitive)) if titleNSRange.location != NSNotFound || addressNSRange.location != NSNotFound { searchResults?.append(market) }
you Don't need to call searchResults?.removeAll() function instead you use it searchResults = NSMutableArray.init() Don't use searchResults?.append(market) instead use it searchResults.addObject(market)
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.
CoreData add object
I made an app which's using core data. I made a function which saves 1 or 2 values / write data into core data. This is the following method: func saveName(name: String) { let myDate:NSDate = NSDate() let context = self.fetchedResultsController.managedObjectContext let entity = self.fetchedResultsController.fetchRequest.entity! let newManagedObject = NSEntityDescription.insertNewObjectForEntityForName(entity.name!, inManagedObjectContext: context) as NSManagedObject if markCell == true { newManagedObject.setValue(name, forKey: "markedCell") markCell = false } else { newManagedObject.setValue(name, forKey: "name") newManagedObject.setValue(myDate, forKey: "datum") } // Save the context. var error: NSError? = nil if !context.save(&error) { abort() } } It occurs a crash in the function cellForRowAtIndexPath if markCell == true. If markCell == false (step into else) all works perfect. If I run this function: func saveName(name: String) { let myDate:NSDate = NSDate() let context = self.fetchedResultsController.managedObjectContext let entity = self.fetchedResultsController.fetchRequest.entity! let newManagedObject = NSEntityDescription.insertNewObjectForEntityForName(entity.name!, inManagedObjectContext: context) as NSManagedObject newManagedObject.setValue(name, forKey: "markedCell") markCell = false newManagedObject.setValue(name, forKey: "name") newManagedObject.setValue(myDate, forKey: "datum") // Save the context. var error: NSError? = nil if !context.save(&error) { abort() } } no crash occurs but than I also added a value to markedCell. I only want to add a value into markedCell if the bool is set to true (the user pressed a button -> bool will be set to true and func saveNamewill be called). Load data from core data (create UITableViewCell): //Get task let context = self.fetchedResultsController.managedObjectContext let object = self.fetchedResultsController.objectAtIndexPath(indexPath) as NSManagedObject var taskString:NSString taskString = object.valueForKey("name") as String cell.textLabel!.text = object.valueForKey("name") as? String //Set accessory type var request:NSFetchRequest = NSFetchRequest(entityName: "Person") request.predicate = NSPredicate(format:"markedCell = %#", taskString) var results : [NSManagedObject] = context.executeFetchRequest(request, error: nil) as [NSManagedObject] if (results.count > 0) { //Element exists cell.accessoryType = UITableViewCellAccessoryType.DisclosureIndicator println("Cell is marked") } else { //Doesn't exist cell.accessoryType = UITableViewCellAccessoryType.None println("Cell isn't marked") }
I can bet that the problem comes from the fact that markedCell is declared as optional property in your Core Data model while name or/and datum are not optional. If this is the case your saving works fine when you enter the else loop because at that point you have: markedCell == nil //this is allowed in your Core Data model name != nil datum != nil However, when you do not enter into the else loop you have: markedCell != nil name == nil datum == nil and one of the last two lines is incompatible with your Core Data model. If you want to use your original code you need to ensure that all properties mentioned here are declared as optional.