Why is my array always nil? - swift

I really don't know whats going on and Im really confused why this happens. I might be doing something wrong but when i try to access my array out of the function, there is nothing in it. If anyone can help me please tell me.
import UIKit
class withFriendsView: UITableViewController {
var withFriendsArray:NSMutableArray = NSMutableArray()
var friendImg = [PFFile]()
var friendusername = [String]()
var friendName = [String]()
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
loadWithFriends()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return friendusername.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell:WithFriendsCell = tableView.dequeueReusableCellWithIdentifier("withFriends", forIndexPath: indexPath) as! WithFriendsCell
friendImg[indexPath.row].getDataInBackgroundWithBlock {
(data:NSData?, error:NSError?) -> Void in
let img = UIImage(data: data!)
cell.friendsImage.image = img
}
return cell
}
func loadWithFriends() {
var channelQuery = ChannelQuery.query()!
var activityQuery = ActivityQuery.query()!
channelQuery.whereKey("Host", matchesKey: "ActChannel", inQuery: activityQuery)
channelQuery.whereKey("Host", equalTo: "kia495")
var data = channelQuery.findObjects()
if data!.count != 0 {
for objects in data! {
let username = objects["Users"]!!.objectId
let predicate = NSPredicate(format: "objectId == %#", username!!)
var userQuery = PFQuery(className: "_User", predicate: predicate)
var objects = userQuery.findObjects()
for object in objects! {
friendImg.append(object.objectForKey("profileImage") as! PFFile)
}
}
}
}
}

You should reload the table after adding an element to the array
First move loadWithFriends() from viewDidAppear to viewDidLoad
in this line, you are returning another array, try returning friendImg.count
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return friendusername.count
}

This findObjectsInBackgroundWithBlock looks like an async method, which will immediately return. The code in the block will be executed only when the data is ready. So when you try to access the data from friendImg, it may not be ready and you will get nil.
If the data is not very large, you can use sync method to do this.

Related

I want to display data from CoreData into a tableview

I want to display data from CoreData into a tableview, im working on favoris, im adding my events on favoris and i want to display it inside a tableview, there is my code :
var lists : [NSManagedObject] = [] {
didSet {
favorisEventTableView.reloadData()
}
}
#IBOutlet weak var favorisEventTableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
favorisEventTableView.dataSource = self
favorisEventTableView.delegate = self
loadFavoris()
// Do any additional setup after loading the view.
}
override func viewWillAppear(_ animated: Bool) {
favorisEventTableView.reloadData()
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return lists.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = favorisEventTableView.dequeueReusableCell(withIdentifier: "FavorisCell")
let contentView = cell?.viewWithTag(0)
let eventId = contentView?.viewWithTag(4) as! UILabel
let item = lists[indexPath.row]
eventId.text = String((item.value(forKey: "id_event") as! Int))
return cell!
}
func loadFavoris() {
let appDelegate = UIApplication.shared.delegate as? AppDelegate
let coreContext = appDelegate?.persistentContainer.viewContext
let fetchRequest = NSFetchRequest<NSManagedObject>(entityName: "Favoris")
do {
lists = try coreContext!.fetch(fetchRequest)
print(lists)
} catch let error as NSError {
print(error.userInfo)
}
}
But it's not displaying anything, maybe the problem on the add function ? im sure it works cause i get "saved" on the console, any help please?
PS: entity "Favoris" has only one attribute "id_event" which is an integer
You need to add loadFavoris inside viewDidLoad not cellForRowAt
override func viewDidLoad() {
super.viewDidLoad()
favorisEventTableView.dataSource = self
favorisEventTableView.delegate = self
loadFavoris()
}
You need to reload your table view every change on your list. you can do it like that
var lists : [NSManagedObject] = [] {
didSet {
tableView.reloadData()
}
}
Don't forget assign the delegate and dataSource protocols of tableView and fetch the data in viewDidLoad() function
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
loadFavoris()
}

Swift - when is data actually loaded in TableViewController

I'm very much a Swift beginner - am populating a table view from Firebase data.
In the table footer I want to display some calculated totals under the table columns. However when calling footerCell.configure(priceLines, isPortrait: isPortrait) the priceLines dictionary is still empty.
How to remedy this?
Thanks in advance, André Hartman, Belgium
import UIKit
import FirebaseDatabase
class ListTableViewController: UITableViewController {
var priceLines = [NSDictionary]()
var isPortrait = false
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(ListTableViewController.rotated), name: UIDeviceOrientationDidChangeNotification, object: nil)
loadDataFromFirebase()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return priceLines.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("profileCell", forIndexPath: indexPath) as! PriceTableViewCell
cell.configure(priceLines, row: indexPath.row, isPortrait: isPortrait, source: "intraday")
return cell
}
override func tableView(tableView: UITableView,viewForHeaderInSection section: Int) -> UIView? {
let headerCell = tableView.dequeueReusableCellWithIdentifier("HeaderCell") as! CustomHeaderCell
headerCell.configure(isPortrait)
return headerCell
}
override func tableView(tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
let footerCell = tableView.dequeueReusableCellWithIdentifier("FooterCell") as! CustomFooterCell
footerCell.configure(priceLines, isPortrait: isPortrait)
return footerCell
}
override func tableView(tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return 30.0
}
override func tableView (tableView:UITableView, heightForHeaderInSection section:Int) -> CGFloat
{
return 50.0;
}
// MARK:- Load data from Firebase
func loadDataFromFirebase() {
UIApplication.sharedApplication().networkActivityIndicatorVisible = true
refInter.observeEventType(.Value, withBlock: { snapshot in
var tempItems = [NSDictionary]()
for item in snapshot.children {
let child = item as! FIRDataSnapshot
let dict = child.value as! NSDictionary
tempItems.append(dict)
}
self.priceLines = tempItems
self.tableView.reloadData()
UIApplication.sharedApplication().networkActivityIndicatorVisible = false
})
}
func rotated()
{
let newDisplay = (UIDeviceOrientationIsLandscape(UIDevice.currentDevice().orientation))
if(newDisplay != isPortrait){
self.tableView.reloadData()
}
isPortrait = newDisplay
}
}
The documentation clearly says that
When the table view is about to appear the first time it’s loaded, the
table-view controller reloads the table view’s data.
So, it will reload the table automatically somewhere between viewDidLoad and viewWillAppear. Your priceLines is empty at this point and will be populated with data only when the closure in the method loadDataFromFirebase is fired. I'm not sure when it happens in your case, but as you call implicitly reloadData then you should have already priceLines nonempty (of course if the results in the closure have some data)

I get the error "index 6 beyond bounds [0 .. 5]' " when implementing search in my app

Here's my code. While following multiple tutorials on how to implement search in Swift I've had no luck.
import UIKit
class DataTableExercisesTableViewController: UITableViewController, UISearchBarDelegate, UISearchResultsUpdating {
var exercises = ["Abs", "Arms", "Back", "Chest", "Legs", "Shoulders", "Triceps"]
var searchActive : Bool = false
#IBOutlet weak var searchBar: UISearchBar!
var filteredTableData = [String]()
var resultSearchController = UISearchController()
override func viewDidLoad() {
super.viewDidLoad()
self.resultSearchController = ({
let controller = UISearchController(searchResultsController: nil)
controller.searchResultsUpdater = self
controller.dimsBackgroundDuringPresentation = false
controller.searchBar.sizeToFit()
self.tableView.tableHeaderView = controller.searchBar
return controller
})()
// Reload the table
self.tableView.reloadData()
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return exercises.count;
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell")! as UITableViewCell;
if (self.resultSearchController.active) {
cell.textLabel?.text = filteredTableData[indexPath.row]
return cell
}
else {
cell.textLabel?.text = exercises[indexPath.row]
return cell
}
}
func updateSearchResultsForSearchController(searchController: UISearchController)
{
filteredTableData.removeAll(keepCapacity: false)
let searchPredicate = NSPredicate(format: "SELF CONTAINS[c] %#", searchController.searchBar.text!)
let array = (exercises as NSArray).filteredArrayUsingPredicate(searchPredicate)
filteredTableData = array as! [String]
self.tableView.reloadData()
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
self.tableView.reloadData()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
I've been having trouble implementing search from different tutorials and just doesn't seem to be working out too well. Any insight is much appreciated.
Your numberOfRowsInSection is always returning exercises.count. But when you are filtering, you are not using exercises, but a smaller array, filteredTableData. So, just as in cellForRowAtIndexPath, you need to change your answer if you are filtering.
The best solution is before access the array value just the check the total count should less the the index you want fetch from the array or use below way to iterate the array
EX :
let arrayOfInts: [Int] = [1, 2, 3];
for i in arrayOfInts {
print(i);
}
In your case you could change the code :
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
var rowCount = 0
if(self.resultSearchController.active){
rowCount = filteredTableData.count
}
else{
rowCount = exercises.count
}
return rowCount;
}

swift uitableview show only different value of an user Array

I'm working on a message app with parse.
I would like to filter by sender the result of query but it duplicate sender for each messages.
I think it is possible to find same value on a array? (in this exemple clientsArray)
Any idea?
Below the code that I used.
var clientName = ""
var clientsArray:[String] = [String]()
override func viewDidLoad() {
super.viewDidLoad()
//Message List Query
let messages = PFQuery(className:"Message")
messages.whereKey("user", equalTo:PFUser.currentUser()!["retailer"]!)
messages.findObjectsInBackgroundWithBlock {
(objects: [AnyObject]?, error: NSError?) -> Void in
if error == nil {
print("Successfully retrieved \(objects!.count) scores.")
if let objects = objects as? [PFObject] {
self.clientName.removeAll()
for object in objects {
if let clientName = object["clientName"] as? String {
self.clientsArray.append(clientName)
}
self.tableView.reloadData()
print(self.clientsArray)
}
}
} else {
print(error)
}
}
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return clientsArray.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("clientsCell", forIndexPath: indexPath)
let clientName = clientsArray[indexPath.row]
if let clientNameLabel = cell.viewWithTag(301) as? UILabel {
clientNameLabel.text = clientName
}
return cell
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showChat" {
if let destination = segue.destinationViewController as? newChatViewController {
destination.clientName = clientsArray[(tableView.indexPathForSelectedRow?.row)!]
//self.hidesBottomBarWhenPushed = true
}
}
}
It's really hard to answer without seeing what the data structure looks like. But I can tell you the problem with your code.
if let clientName = object["clientName"] as? String {
self.clientsArray.append(clientName)
}
Your if-let statement is simply getting the object of ["clientName"], which is just your clients name. Then you're appending your clients name to your clientsArray object.
This is why you're just seeing luke.
I can help resolve the problem, but I need to know what your data's structure looks like. Can you print out an entire object?

Saving values to UserDefults from Textfield in TableView : Swift

I am trying to save values from a tableview that users will input into the textfield, the problem is that I do not know how to access the new value and replace the string in the array.
So basically the app will display fields based on what the user wants and then the user can edit those values to their liking. Once the textfields have been updated, the values are stored again in userdefaults so that the next time the tableview is opened, the update values will appear.
This is what the tableviewcontroller looks like at the moment:
//
// Asset1TableViewController.swift
// Net Calc 2
//
// Created by Joshua Peterson on 30/06/2015.
// Copyright © 2015 Peterson Productions. All rights reserved.
//
import UIKit
class Asset1TableViewController: UITableViewController {
var dataHolder: [NSString] = [NSString]()
var finalDataHolder: [NSString] = [NSString]()
var acountAmountHolder: [NSString] = [NSString]()
var finalAccountAmountHolder: [NSString] = [NSString]()
let defaults = NSUserDefaults.standardUserDefaults()
let key1 = "keySave1"
let key2 = "keySave2"
override func viewDidLoad() {
super.viewDidLoad()
dispatch_async(dispatch_get_main_queue(), { () -> Void in
if let storedTitleValue : NSArray? = self.defaults.arrayForKey(self.key1) {
if storedTitleValue == nil {
self.dataHolder = [NSString]()
} else {
let readArray : [NSString] = storedTitleValue as! [NSString]
for element in readArray {
self.dataHolder.append(element as String)
self.finalDataHolder.append(element as String)
}
}
}
if let storedAmountValue : NSArray? = self.defaults.arrayForKey(self.key2) {
if storedAmountValue == nil {
self.acountAmountHolder = [NSString]()
} else {
let readArray : [NSString] = storedAmountValue as! [NSString]
for element in readArray {
self.acountAmountHolder.append(element as String)
self.finalAccountAmountHolder.append(element as String)
}
}
}
})
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dataHolder.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Account1Cell", forIndexPath: indexPath) as! Account1Cell
cell.AccountLabel.text = dataHolder[indexPath.row] as String
cell.AccountAmount.text = acountAmountHolder[indexPath.row] as String
return cell
}
#IBAction func addButtonTapped(sender: AnyObject) {
let newAccounTitle = "Account Name"
let newAccountAmount = "R0.00"
dataHolder.append(newAccounTitle)
acountAmountHolder.append(newAccountAmount)
tableView.reloadData()
}
#IBAction func saveButtonTapped(sender: AnyObject) {
// Save
defaults.setObject(dataHolder as Array, forKey: key1)
defaults.setObject(acountAmountHolder as Array, forKey: key2)
defaults.synchronize()
}
override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
return true
}
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle == UITableViewCellEditingStyle.Delete {
dataHolder.removeAtIndex(indexPath.row)
acountAmountHolder.removeAtIndex(indexPath.row)
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.Automatic)
}
}
/*
// Override to support rearranging the table view.
override func tableView(tableView: UITableView, moveRowAtIndexPath fromIndexPath: NSIndexPath, toIndexPath: NSIndexPath) {
}
*/
/*
// Override to support conditional rearranging of the table view.
override func tableView(tableView: UITableView, canMoveRowAtIndexPath indexPath: NSIndexPath) -> Bool {
// Return NO if you do not want the item to be re-orderable.
return true
}
*/
}
I have tried to apply some of the code that i have found on the website but the problem is that I cant actually connect to the cell.
Ok so after some research I have added a few functions to the custom cell class so that it looks like this:
import UIKit
protocol TableViewCellDelegate {
// Indicates that the edit process has begun for the given cell
func cellDidBeginEditing(editingCell: Account1Cell)
// Indicates that the edit process has committed for the given cell
func cellDidEndEditing(editingCell: Account1Cell)
}
class Account1Cell: UITableViewCell, UITextFieldDelegate {
#IBOutlet weak var AccountLabel: UITextField!
#IBOutlet weak var AccountAmount: UITextField!
var delegate: TableViewCellDelegate?
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
AccountLabel.delegate = self
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
func textFieldShouldReturn(textField: UITextField) -> Bool {
// close the keyboard on Enter
AccountLabel.resignFirstResponder()
return false
}
func textFieldShouldBeginEditing(textField: UITextField) -> Bool {
// disable editing of completed to-do items
return true
}
func textFieldDidEndEditing(textField: UITextField) {
if AccountLabel != nil {
let newAccountLabel = AccountLabel.text
print(newAccountLabel) // Prints out the new edited text!!!!!!!!
}
if delegate != nil {
delegate!.cellDidEndEditing(self)
}
}
func textFieldDidBeginEditing(textField: UITextField) {
if delegate != nil {
delegate!.cellDidBeginEditing(self)
}
}
}
Now what I need to do is either replace that value in the Array at the index (which i think is going to be rather complicated) or create some sort of loop that will read ALL the values and simply store all of the new values to UserDefaults. Maybe there is something else?
Any help is appreciated!!
You should have a protocol in your custom cell like this, and call it when the text field in the cell gets modified:
protocol TableViewCellToTVController{
func cellCurrentlyEditing(editingCell: Account1Cell) -> Int
}
....
func textFieldShouldReturn(textField: UITextField) -> Bool {
// close the keyboard on Enter
let myrow: Int? = self.delegate_special?.cellCurrentlyEditing(self)
println("cellCurrentlyEditing got called from delegate" , myrow)
AccountLabel.resignFirstResponder()
return false
}
implement this function in tableviewcontroller to know which row got selected :
func cellCurrentlyEditing(editingCell: Account1Cell) -> Int{
var rowNum = 0
let indexP: NSIndexPath = tableView.indexPathForCell(editingCell)!
rowNum = indexP.row
return rowNum
}
also make your tableviewcontroller the delegate for each cell:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Account1Cell", forIndexPath: indexPath) as! Account1Cell
cell.AccountLabel.text = dataHolder[indexPath.row] as String
cell.AccountAmount.text = acountAmountHolder[indexPath.row] as String
cell.delegate_special = self;
return cell
}