Contacts not showing for first time in swift - ios

When I try to load the table view with the list of contacts, it is not showing for the first time. But if I try Pull-To-Refresh or go to some view and come back to table view controller, the contacts are loading and showing perfectly. Here is the code:
func getContactNames()
{
let allContacts = ABAddressBookCopyArrayOfAllPeople(addressBookRef).takeRetainedValue() as Array
for record in allContacts {
let currentContact: ABRecordRef = record
let currentContactName = ABRecordCopyCompositeName(currentContact)?.takeRetainedValue()
as? String
if(currentContactName != nil) {
self.arrFriendsList.append(currentContactName! as String)
}
}
// For alphabatically arranging
self.arrFriendsList = self.arrFriendsList.sorted { $0.localizedCaseInsensitiveCompare($1) == NSComparisonResult.OrderedAscending }
self.arrFilteredTableData = self.arrFriendsList
self.tblView.reloadData()
}
I am calling this function in viewDidLoad method. Any idea for solution? Thanks in advance :)

Related

UICollectionView ReloadData not showing content properly

I am trying to filter my homeCollectionView with SegmentControl. On taping on the segment, I am filtering the content based on tag available in the dictionary. When I am performing ReloadData and switching between the segments, in the first go (when I am taping on the segments the first time), the filter is working and all data is coming, but when I tap back on the segments, part of the content in the cell, especially the LabelViews text are not showing up afterwards. Also, it's happening for random indexPath.
This is my code:
#objc func toggleHomeContent(_ notification: NSNotification) {
toggleValContType = notification.object as? String ?? "all"
if (toggleValContType == "all") {
mainArrayData = primaryArrayData
}
else if (toggleValContType == "collections") {
mainArrayData = primaryArrayData { $0["filterType"] == "Col" || $0["filterType"] == "CTA" }
}
else if (toggleValContType == "books") {
mainArrayData = primaryArrayData { $0["filterType"] == "Book" || $0["filterType"] == "CTA" }
}
homeCollectionView?.reloadData()
homeCollectionView?.layoutIfNeeded()
homeCollectionView?.collectionViewLayout.invalidateLayout()
//DispatchQueue.main.async(execute: homeCollectionView.reloadData)
}
And by arrays are declared like this:
var mainArrayData : [[String:String]] = HomeArray().mainArray
var primaryArrayData: [[String:String]] = HomeArray().mainArray
Heres the snapshot of what the issue is:
Snapshot of the issue
Thanks in advance!
Add this code in main thread :
homeCollectionView?.reloadData()
homeCollectionView?.layoutIfNeeded()
DispatchQueue.main.async {
homeCollectionView?.reloadData()
homeCollectionView?.layoutIfNeeded()}

Updating a UITableView on Firebase Data Change

So I am trying to have table cells that auto refresh when any other of the cells are changed. I hoped that my code could find the object within the table array, change a value, and refresh the table view all in one fell swoop, but for some reason, even though my array updates, the table refuses to instantly update. To see my new values I have to restart the app or reopen the View.
This is what I am doing so far:
usersRef.queryOrdered(byChild "contactList/(currentUID!)").queryEqual(toValue: true).observe(.childChanged, with: { (snapshot) in
if let friends = snapshot.value as? [String: AnyObject] {
//Making a new updated contact
let name = friends["name"] as! String
print(name)
let edit = Contact()
edit.name = name;
edit.isSafe = friends["isSafe"] as! Bool
edit.email = friends["email"] as! String
//My manager object has a custom array objects called contacts.
self.manager.index(withName: name, contact: edit)
self.tableView.reloadData();
} else {
print("Failed to Convert")
}
})
}
index(withName: String, contact: Contact) method in the ContactManager:
//This method goes through the contactList to check names to see which contact has a specific name equal to "name", then it uses the "contact" to update that specific child.
public mutating func index(withName: String, contact: Contact) {
for iterator in 0 ..< contactList.count {
if (contactList[iterator].name == withName) {
contactList[iterator] = contact
}
}
}
This is a common problem. Your calling self.tableView.reloadData() in the background thread. All UI changes need to be made on the main thread. like this.
DispatchQueue.main.async {
self.tableView.reloadData();
}

Firebase restoring initial values in textfields after changing text value

I am practicing iOS (Swift) with Firebase. the first viewcontroller retrieves all the records from firebase db and populate the tableView from an array. when the user selects an item from that tableview a new viewcontroller pops up segueing the object from the listView viewcontroller to the detail viewcontroller. data is populated to the fields successfully!
however when i try to update any of the textfield, the moment i switch to another textfield the initial value is restored in the edited textfield.
I have tried to removeAllObservers... but nothing worked. i even removed "import Firebase" and all associated objects and still it restores the initial value.
am i missing any concept here?
this is the code from the ListViewController:
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated);
self.activityIndicator.startAnimating();
self.observeIngrdUpdates = ref.observeEventType(.Value, withBlock: { (snapshot) in
self.ingredients.removeAll();
for child in snapshot.children {
var nutrition:String = "";
var type:Int = 0;
var desc:String = "";
var img:String = "";
var name:String = "";
var price:Double = 0.0;
if let _name = child.value["IngredientName"] as? String {
name = _name;
}
if let _nutrition = child.value["NutritionFacts"] as? String {
nutrition = _nutrition;
}
if let _type = child.value["IngredientType"] as? Int {
type = _type;
}
if let _desc = child.value["UnitDescription"] as? String {
desc = _desc;
}
if let _img = child.value["IngredientImage"] as? String {
img = _img;
}
if let _price = child.value["IngredientPrice"] as? Double {
price = _price;
}
let ingredient = Ingredient(name: name, type: type, image: img, unitDesc: desc, nutritionFacts: nutrition, price: price);
ingredient.key = child.key;
self.ingredients.append(ingredient);
}
self.tableView.reloadData();
})
}
and the PrepareForSegue:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let destinationVC = segue.destinationViewController as! EditIngredientVC;
if segue.identifier?.compare(SEGUE_EDIT_INGREDIENTS) == .OrderedSame {
destinationVC.ingredient = ingredients[tableView.indexPathForSelectedRow!.row];
destinationVC.controllerTitle = "Edit";
} else if segue.identifier?.compare(SEGUE_ADD_INGREDIENT) == .OrderedSame {
destinationVC.controllerTitle = "New";
}
}
this is the code for populating the fields in DetailViewController:
override func viewDidLayoutSubviews() {
lblControllerTitle.text = controllerTitle;
if controllerTitle?.localizedCaseInsensitiveCompare("NEW") == .OrderedSame {
self.segVegFru.selectedSegmentIndex = 0;
} else {
if ingredient != nil {
self.txtIngredientName.text = ingredient!.name;
self.txtUnitDesc.text = ingredient!.unitDesc;
self.segVegFru.selectedSegmentIndex = ingredient!.typeInt;
self.txtNutritionFacts.text = ingredient!.nutritionFacts;
self.txtPrice.text = "\(ingredient!.price)";
}
}
}
Thank you all for your help.
It's probably because you are putting your textField populating code, in viewDidLayoutSubviews() method.
This method will be triggered every time the layout of a views changes in viewcontroller.
Move it to viewDidLoad() and it should be fine.
The reason that it's being reverted to the previous value is because. You are populating the textField.text from the "ingredient" object. The ingredient will have the same value retained that you passed from previous view controller. Until you mutate it at some point.
And by the way Firebase is very cool I like it too :)

How Can I Filtering Using NSMutableArray and UISearchController

I'm working on an iOS application using Swift and I'm using NSMutableArray, and when I try to add UISearchController to my UITableViewController It give me this error
1.1 click here, please
But when I try to do it with NSArray it works great.
If you wondering about Why am I using NSMutableArray?
Because I need it to pass the object of the NSMutableArray that is in the selected row from the UITableViewController to another UIViewController like this:
1.2 click here, please
What I have to do now, how can I adding search bar?
Thanks in advance.
You can hack it by converting the mutable array to a nsarray and then using that.
Where your doing the stuff in 1.1 just add before the the self.businessNamesArray line add this line
var regArray = self.businessNamesArray as NSArray as! [String]
then change the self.businessNamesArray line to regArray.filter{ rest of your code here
If you use regular Swift arrays, which I think is the right idea since you're using Swift, you can get it working if you change your array variable type and fix your array filtering syntax. First, change the array declaration to this:
var BusinessNamesArray:[String]?
The filter function your are using on your self.BusinessNamesArray returns a filtered array rather than filtering the array in place (which is what it seems like you want). If you want to replace the content of your self.BusinessNamesArray, you would need to do something like this:
func updateSearchResultsForSearchController(searchController: UISearchController) {
self.BusinessNamesArray = self.BusinessNamesArray.filter { (business:String) -> Bool in
return true
// You probably want to compare strings here like this instead:
// guard let searchText = searchController.searchBar.text else {
// return false
// }
// return business.hasPrefix(searchText)
}
// You probably also need to reload your table view at this point:
self.tableView.reloadData()
}
Keep in mind though that your BusinessNamesArray is now filtered and you can't un-filter it. Instead, you should probably keep a second array of your Strings called something like searchResults. Then you can use that for filtering and maintain your original list of business names in the original array. So you would add a class variable called var searchResults:[String]? and the search filtering code would change to.
func updateSearchResultsForSearchController(searchController: UISearchController) {
self.searchResults = self.BusinessNamesArray.filter { (business:String) -> Bool in
guard let searchText = searchController.searchBar.text else {
return false
}
return business.hasPrefix(searchText)
}
self.tableView.reloadData()
}
When you call reloadData on your table view at this point, you will probably want to check and see if your search controller is active in the table view delegate's numberOfSectionsInTableView as well other delegate functions and use the searchResults array if it is active and the BusinessNamesArray if its not--something like:
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
if (self.resultSearchController.active) {
return self.searchResults.count ?? 0
} else {
return self.BusinessNamesArray.count ?? 0
}
}
(The self.resultSearchController variable is a local instance of UISearchController. I'm not sure what you've named yours.)
You'll then use similar code to decide which items to grab for your prepareForSegue: code:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Guard your variables here so you can return early if they're
// not valid
guard let upcoming = segue.destinationViewController as? DetailsViewController,
myindexpath = self.tableView.indexPathForSelectedRow else {
return
}
var titleString = ""
if (self.resultSearchController.active) {
if let searchResults = self.searchResults {
titleString = searchResults[myindexpath.row]
}
} else {
if let businessNames = self.BusinessNamesArray {
titleString = businessNames[myindexpath.row]
}
}
upcoming.titleString = titleString
self.deselectRowAtIndexPath(myindexpath, animated: true)
}

Swift updating UITableView with new data

I'm trying to repopulate my UITableView with data from another JSON call.
However my current setup doesn't seem to work, and while there are many identical questions on SO the answers I could find I've already tried.
I'm saving my API data in CoreData entity objects. And I'm filling my UITableView with my CoreData entities.
In my current setup I have 3 different API Calls that has a different amount of data, and of course different values. I need to be able to switch between these 3 datasets, and that's what I'm trying to accomplish now. (so far without progress).
I have a function called "loadSuggestions", which is where I assume my fault lies.
First I check for an internet connection.
I set the managedObjectContext
I check what API I need to call (This is determined before the function is called, and I checked that it works as intended)
I delete all the current data from the entity that it's trying to call. (I also tried to delete the data from the last data the UITableView had loaded. That didn't change anything). I also checked that this works. After deleting the data, I checked that it prints out an empty array, I also tried logging the objects it deletes to make sure.
I then fetch the new data, save it into temporary variables. Then save it to my core data.
Then I make my second API call (dependant on a variable from the first one), fetch that data and save it the same way.
I append the object to the array the UITableView fills it's cells from. (I checked that it prints out correctly as well)
And lastly I reload the tableView. (doesn't change a thing)
Here's the function:
func loadSuggestions() {
println("----- Loading Data -----")
// Check for an internet connection.
if Reachability.isConnectedToNetwork() == false {
println("ERROR: -> No Internet Connection <-")
} else {
// Set the managedContext again.
managedContext = appDelegate.managedObjectContext!
// Check what API to get the data from
if Formula == 0 {
formulaEntity = "TrialFormulaStock"
println("Setting Entity: \(formulaEntity)")
formulaAPI = NSURL(string: "http://api.com/json/entry_weekly.json")
} else if Formula == 1 {
formulaEntity = "ProFormulaStock"
println("Setting Entity: \(formulaEntity)")
formulaAPI = NSURL(string: "http://api.com/json/entry_weekly.json")
} else if Formula == 2 {
formulaEntity = "PremiumFormulaStock"
formulaAPI = NSURL(string: "http://api.com/json/proff_weekly.json")
println("Setting Entity: \(formulaEntity)")
} else if Formula == 3 {
formulaEntity = "PlatinumFormulaStock"
println("Setting Entity: \(formulaEntity)")
formulaAPI = NSURL(string: "http://api.com/json/fund_weekly.json")
}
// Delete all the current objects in the dataset
let fetchRequest = NSFetchRequest(entityName: formulaEntity)
let a = managedContext.executeFetchRequest(fetchRequest, error: nil) as! [NSManagedObject]
for mo in a {
managedContext.deleteObject(mo)
}
// Removing them from the array
stocks.removeAll(keepCapacity: false)
// Saving the now empty context.
managedContext.save(nil)
// Set up a fetch request for the API data
let entity = NSEntityDescription.entityForName(formulaEntity, inManagedObjectContext:managedContext)
var request = NSURLRequest(URL: formulaAPI!)
var data = NSURLConnection.sendSynchronousRequest(request, returningResponse: nil, error: nil)
var formula = JSON(data: data!)
// Loop through the api data.
for (index: String, actionable: JSON) in formula["actionable"] {
// Save the data into temporary variables
stockName = actionable["name"].stringValue
ticker = actionable["ticker"].stringValue
action = actionable["action"].stringValue
suggestedPrice = actionable["suggested_price"].floatValue
weight = actionable["percentage_weight"].floatValue
// Set up CoreData for inserting a new object.
let stock = NSManagedObject(entity: entity!,insertIntoManagedObjectContext:managedContext)
// Save the temporary variables into coreData
stock.setValue(stockName, forKey: "name")
stock.setValue(ticker, forKey: "ticker")
stock.setValue(action, forKey: "action")
stock.setValue(suggestedPrice, forKey: "suggestedPrice")
stock.setValue(weight, forKey: "weight")
// Get ready for second API call.
var quoteAPI = NSURL(string: "http://dev.markitondemand.com/Api/v2/Quote/json?symbol=\(ticker)")
// Second API fetch.
var quoteRequest = NSURLRequest(URL: quoteAPI!)
var quoteData = NSURLConnection.sendSynchronousRequest(quoteRequest, returningResponse: nil, error: nil)
if quoteData != nil {
// Save the data from second API call to temporary variables
var quote = JSON(data: quoteData!)
betterStockName = quote["Name"].stringValue
lastPrice = quote["LastPrice"].floatValue
// The second API call doesn't always find something, so checking if it exists is important.
if betterStockName != "" {
stock.setValue(betterStockName, forKey: "name")
}
// This can simply be set, because it will be 0 if not found.
stock.setValue(lastPrice, forKey: "lastPrice")
} else {
println("ERROR ----------------- NO DATA for \(ticker) --------------")
}
// Error handling
var error: NSError?
if !managedContext.save(&error) {
println("Could not save \(error), \(error?.userInfo)")
}
// Append the object to the array. Which fills the UITableView
stocks.append(stock)
}
// Reload the tableview with the new data.
self.tableView.reloadData()
}
}
Currently, when I push to this viewController, this function is called in viewDidAppear like so:
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(true)
tableView.allowsSelection = true
if isFirstTime {
loadSuggestions()
isFirstTime = false
}
}
It populates the tableView correctly and everything seems to work as planned.
However if I open my slide-out menu and call a function to load different data, nothing happens, here's an example function:
func platinumFormulaTapGesture() {
// Menu related actions
selectView(platinumFormulaView)
selectedMenuItem = 2
// Setting the data to load
Formula = 3
// Sets the viewController. (this will mostly be the same ViewController)
menuTabBarController.selectedIndex = 0
// Set the new title
navigationController?.navigationBar.topItem!.title = "PLATINUM FORMULA"
// And here I call the loadSuggestions function again. (this does run)
SuggestionsViewController().loadSuggestions()
}
Here's the 2 relevant tableView functions:
number of Rows:
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return stocks.count
}
And cellForRowAtIndexPath, (this is where I set up my cells with the CoreData)
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("com.mySuggestionsCell", forIndexPath: indexPath) as! mySuggestionsCell
let formulaStock = stocks[indexPath.row]
cell.stockNameLabel.text = formulaStock.valueForKey("name") as! String!
cell.tickerLabel.text = formulaStock.valueForKey("ticker") as! String!
action = formulaStock.valueForKey("action") as! String!
suggestedPrice = formulaStock.valueForKey("suggestedPrice") as! Float
let suggestedPriceString = "Suggested Price\n$\(suggestedPrice.roundTo(2))" as NSString
var suggestedAttributedString = NSMutableAttributedString(string: suggestedPriceString as String)
suggestedAttributedString.addAttributes(GrayLatoRegularAttribute, range: suggestedPriceString.rangeOfString("Suggested Price\n"))
suggestedAttributedString.addAttributes(BlueHalisRBoldAttribute, range: suggestedPriceString.rangeOfString("$\(suggestedPrice.roundTo(2))"))
cell.suggestedPriceLabel.attributedText = suggestedAttributedString
if action == "SELL" {
cell.suggestionContainer.backgroundColor = UIColor.formulaGreenColor()
}
if let lastPrice = formulaStock.valueForKey("lastPrice") as? Float {
var lastPriceString = "Last Price\n$\(lastPrice.roundTo(2))" as NSString
var lastAttributedString = NSMutableAttributedString(string: lastPriceString as String)
lastAttributedString.addAttributes(GrayLatoRegularAttribute, range: lastPriceString.rangeOfString("Last Price\n"))
percentDifference = ((lastPrice/suggestedPrice)*100.00)-100
if percentDifference > 0 && action == "BUY" {
lastAttributedString.addAttributes(RedHalisRBoldAttribute, range: lastPriceString.rangeOfString("$\(lastPrice.roundTo(2))"))
} else if percentDifference <= 0 && percentDifference > -100 && action == "BUY" {
lastAttributedString.addAttributes(GreenHalisRBoldAttribute, range: lastPriceString.rangeOfString("$\(lastPrice.roundTo(2))"))
} else if percentDifference <= 0 && percentDifference > -100 && action == "SELL" {
lastAttributedString.addAttributes(RedHalisRBoldAttribute, range: lastPriceString.rangeOfString("$\(lastPrice.roundTo(2))"))
} else if percentDifference == -100 {
lastPriceString = "Last Price\nN/A" as NSString
lastAttributedString = NSMutableAttributedString(string: lastPriceString as String)
lastAttributedString.addAttributes(GrayLatoRegularAttribute, range: lastPriceString.rangeOfString("Last Price\n"))
lastAttributedString.addAttributes(BlackHalisRBoldAttribute, range: lastPriceString.rangeOfString("N/A"))
}
cell.lastPriceLabel.attributedText = lastAttributedString
} else {
println("lastPrice nil")
}
weight = formulaStock.valueForKey("weight") as! Float
cell.circleChart.percentFill = weight
let circleChartString = "\(weight.roundTo(2))%\nWEIGHT" as NSString
var circleChartAttributedString = NSMutableAttributedString(string: circleChartString as String)
circleChartAttributedString.addAttributes(BlueMediumHalisRBoldAttribute, range: circleChartString.rangeOfString("\(weight.roundTo(2))%\n"))
circleChartAttributedString.addAttributes(BlackSmallHalisRBoldAttribute, range: circleChartString.rangeOfString("WEIGHT"))
cell.circleChartLabel.attributedText = circleChartAttributedString
cell.selectionStyle = UITableViewCellSelectionStyle.None
return cell
}
I define my appDelegate as the very first thing in my class:
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
var managedContext = NSManagedObjectContext()
I think that's all the code that could possibly be the cause of the bug. Again I think the most likely cause would be in the loadSuggestions function.
To force update the tableView I also tried calling setNeedsDisplay and setNeedsLayout both on self.view and tableView, neither of which seemed to do anything at all.
Any advice in figuring out why this tableView refuses to update would be an enormous help!
And I apologize for the walls of code, but I havn't been able to find the exact origin of the issue.
This line in the platinumFormulaTapGesture function is incorrect,
SuggestionsViewController().loadSuggestions()
This creates a new instance of SuggestionsViewController, which is not the one you have on screen. You need to get a pointer to the one you have. How you do that depends on your controller hierarchy, which you haven't explained fully enough.

Resources