I can't seem to get this right. I want to get core data from my Database and display all in table view. Running this only displays the last ID multiple times on my table. Could someone advise what I'm doing wrong and/or possibly assist? Thanks.
import Foundation
import CoreData
extension MyFavourites {
#NSManaged var id: String?
}
-
var myFavs : [MyFavourites]?
override func viewDidLoad() {
super.viewDidLoad()
let appDel: AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let context: NSManagedObjectContext = appDel.managedObjectContext
let freq = NSFetchRequest(entityName: "MyFavourites")
freq.returnsObjectsAsFaults = false
do {
myFavs = try context.executeFetchRequest(freq) as? [MyFavourites]
} catch _ {
myFavs = nil
}
tableView.reloadData()
}
-
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return (myFavs?.count)!
}
-
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)
if myFavs!.count > 0 {
for result: AnyObject in myFavs! {
if let favID: String = result.valueForKey("id") as? String {
cell.textLabel?.text = favID
}
}
} else {
print("No Record")
}
return cell
}
If I am reading your code correctly, it will display last recorded favId in all cells. The cellForRowAtIndexPath asks you for value for current cell, but instead of providing that, you loop through all of them and repeatedly assign the same label with favID rewriting it multiple times. At the end of the cycle the label will have the last ID from the list.
You need to remove the loop and assign cell.label.text with ID value from myFavs[indexPath.row].
Related
I can load my current tableview data onto the database and then print out the new data onto my console but can't get the new data back into the tableview and I'm tearing my hair out because I know it should be simple!
I've tried all sorts of things but I just can't figure out where I'm going wrong.
//Saves to database without any problems
//Class
var ref: DatabaseReference!
//ViewDidLoad
ref = Database.database().reference()
func save()
{
let ref = Database.database().reference(withPath: "Admin")
let adding = ref.child(me)
let addData: [String: [String]] = ["addJokes": data]
adding.setValue(addData)
{
(error:Error?, ref:DatabaseReference) in
if let error = error
{
print("Data could not be saved: \(error).")
}
else
{
print("Data saved successfully!")
}
}
}
Can print out the database data to my console but can't get it into my tableview
let ref = Database.database().reference(withPath: "Admin")
ref.observe(.value, with:
{
(snapshot) in
let new = snapshot.value as? String
print(snapshot.value as Any)
if let newData = new
{
self.data.append(newData)
self.mainTable.reloadData()
}
})
Update
TableView details-
TableView Class Ext
extension TableView: UITableViewDataSource, UITableViewDelegate
{
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
if isSearching {
return filteredArray.count
}
else
{
return data.count
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
var array: String?
if isSearching
{
array = filteredArray[indexPath.row]
}
else
{
array = data[indexPath.row]
}
let cell = mainTable.dequeueReusableCell(withIdentifier: cellId, for: indexPath) as UITableViewCell
cell.textLabel?.text = array
return cell
}
TableView Class-
class TableView: UIViewController
{
let cellId = "cellId"
var filteredArray = [String]()
var ref: DatabaseReference!
var data = [
"""
multiple line
data array
"""
]
lazy var mainTable: UITableView =
{
let table = UITableView()
table.translatesAutoresizingMaskIntoConstraints = false
table.register(UITableViewCell.self, forCellReuseIdentifier: cellId)
return table
}()
override func viewDidLoad() {
super.viewDidLoad()
mainTable.delegate = self
mainTable.dataSource = self
}
Console prints exactly what I want back into my tableview. Turning print function into results is usually the easy part.
The problem lies in let new = snapshot.value as? String. Here, new is null thus if let newData = new is always false and if block won't be executed. First, check snapshot.value's data type and value then use it accordingly.
Essentially I have am using JSON data to create an array and form a tableview.
I would like the table cells to be grouped by one of the fields from the JSON array.
This is what the JSON data looks like:
[{"customer":"Customer1","number":"122039120},{"customer":"Customer2","number":"213121423"}]
Each number needs to be grouped by each customer.
How can this be done?
This is how I've implemented the JSON data using the table:
CustomerViewController.swift
import UIKit
class CustomerViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, FeedCustomerProtocol {
var feedItems: NSArray = NSArray()
var selectedStock : StockCustomer = StockCustomer()
let tableView = UITableView()
#IBOutlet weak var customerItemsTableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
//set delegates and initialize FeedModel
self.tableView.allowsMultipleSelection = true
self.tableView.allowsMultipleSelectionDuringEditing = true
self.customerItemsTableView.delegate = self
self.customerItemsTableView.dataSource = self
let feedCustomer = FeedCustomer()
feedCustomer.delegate = self
feedCustomer.downloadItems()
}
}
func itemsDownloaded(items: NSArray) {
feedItems = items
self.customerItemsTableView.reloadData()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// Return the number of feed items
print("item feed loaded")
return feedItems.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// Retrieve cell
let cell = tableView.dequeueReusableCell(withIdentifier: "customerGoods", for: indexPath) as? CheckableTableViewCell
let cellIdentifier: String = "customerGoods"
let myCell: UITableViewCell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier)!
// Get the stock to be shown
let item: StockCustomer = feedItems[indexPath.row] as! StockCustomer
// Configure our cell title made up of name and price
let titleStr = [item.number].compactMap { $0 }.joined(separator: " - ")
return myCell
}
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
tableView.cellForRow(at: indexPath)?.accessoryType = .none
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.cellForRow(at: indexPath)?.accessoryType = .checkmark
let cellIdentifier: String = "customerGoods"
let myCell: UITableViewCell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier)!
myCell.textLabel?.textAlignment = .left
}
}
FeedCustomer.swift:
import Foundation
protocol FeedCustomerProtocol: class {
func itemsDownloaded(items: NSArray)
}
class FeedCustomer: NSObject, URLSessionDataDelegate {
weak var delegate: FeedCustomerProtocol!
let urlPath = "https://www.example.com/example/test.php"
func downloadItems() {
let url: URL = URL(string: urlPath)!
let defaultSession = Foundation.URLSession(configuration: URLSessionConfiguration.default)
let task = defaultSession.dataTask(with: url) { (data, response, error) in
if error != nil {
print("Error")
}else {
print("stocks downloaded")
self.parseJSON(data!)
}
}
task.resume()
}
func parseJSON(_ data:Data) {
var jsonResult = NSArray()
do{
jsonResult = try JSONSerialization.jsonObject(with: data, options:JSONSerialization.ReadingOptions.allowFragments) as! NSArray
} catch let error as NSError {
print(error)
}
var jsonElement = NSDictionary()
let stocks = NSMutableArray()
for i in 0 ..< jsonResult.count
{
jsonElement = jsonResult[i] as! NSDictionary
let stock = StockCustomer()
//the following insures none of the JsonElement values are nil through optional binding
if let number = jsonElement[“number”] as? String,
let customer = jsonElement["customer"] as? String,
{
stock.customer = customer
stock.number = number
}
stocks.add(stock)
}
DispatchQueue.main.async(execute: { () -> Void in
self.delegate.itemsDownloaded(items: stocks)
})
}
}
StockCustomer.swift:
import UIKit
class StockCustomer: NSObject {
//properties of a stock
var customer: String?
var number: String?
//empty constructor
override init()
{
}
//construct with #name and #price parameters
init(customer: String) {
self.customer = customer
}
override var description: String {
return "Number: \(String(describing: number)), customer: \(String(describing: customer))"
}
}
You can achieve this by making an array of array. So something like this
[[{"customer": "customer1", "number": "123"}, {"customer": "customer1", "number": "456"}], [{"customer": "customer2", "number": "678"}, {"customer": "customer2", "number": "890"}]]
This is not the only data structure you can use to group. Another possibility is:
{"customer1": [{"customer": "customer1", "number": "123"}, {"customer": "customer1", "number": "456"}], "customer2": [{"customer": "customer2", "number": "678"}, {"customer": "customer2", "number": "890"}]}
Then you can use UITableView sections to group by customers. Section count would be the number of inside arrays and each section would contain as many rows as there are numbers in that inside array.
You can group a sequence based on a particular key using one of the Dictionary initializer,
init(grouping:by:)
The above method init will group the given sequence based on the key you'll provide in its closure.
Also, for parsing such kind of JSON, you can easily use Codable instead of manually doing all the work.
So, for that first make StockCustomer conform to Codable protocol.
class StockCustomer: Codable {
var customer: String?
var number: String?
}
Next you can parse the array like:
func parseJSON(data: Data) {
do {
let items = try JSONDecoder().decode([StockCustomer].self, from: data)
//Grouping the data based on customer
let groupedDict = Dictionary(grouping: items) { $0.customer } //groupedDict is of type - [String? : [StockCustomer]]
self.feedItems = Array(groupedDict.values)
} catch {
print(error.localizedDescription)
}
}
Read about init(grouping:by:) in detail here: https://developer.apple.com/documentation/swift/dictionary/3127163-init
Make the feedItems object in CustomerViewController of type [[StockCustomer]]
Now, you can implement UITableViewDataSource methods as:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.feedItems.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "customerGoods", for: indexPath) as! CheckableTableViewCell
let items = self.feedItems[indexPath.row]
cell.textLabel?.text = items.compactMap({$0.number}).joined(separator: " - ")
//Configure the cell as per your requirement
return cell
}
Try implementing the approach with all the bits and pieces and let me know in case you face any issues.
Im working on a project in swift 3.0 and I have two UITableViews where I set data fetched from a core-data module entity called UserIncome. As these data will be populated in two UItableViews in a single UIViewController (filtering based on a String value in the ViewWillAppear delegate method),once a row is been deleted in one UITableView, its array automatically gets updated by the other tableView's objects too. But once I click the back button and come back to the same UIViewController all seems fine. My requirement is to update the UItableView once a row is been deleted so as the core data module. The code as bellow. What am I missing here?
import UIKit
import CoreData
class MyIncomesViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var recurringIncomeTableView: UITableView!
#IBOutlet weak var otherIncomeTableView: UITableView!
//var myIncomeType : String?
var stores = [UserIncome] ()
var other = [UserIncome] ()
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
var rowTbl : Int!
var rowTbl2 : Int!
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(_ animated: Bool) {
stores.removeAll()
other.removeAll()
let request = NSFetchRequest <NSFetchRequestResult> (entityName: "UserIncome")
request.returnsObjectsAsFaults = false
do {
let results = try context.fetch(request) as! [UserIncome]
print("Results from the fetch request are : ", request)
// check data existance
if results.count>0 {
print("results are :", results.count)
for resultGot in results {
//lets check if the data is available and whether the loop is working by printing out the "name"
if let incName = resultGot.incomeName {
print("expence name is :", incName)
//set the value to the global variable as to filter the arrays
let myIncomeType = resultGot.incomeType
if myIncomeType == "Recurring Income"{
stores += [resultGot]
print("my recurring income array is : \(stores)")
}else if myIncomeType == "Other Income"{
other += [resultGot]
print("my other income array is : \(other)")
}
}
}
self.recurringIncomeTableView.reloadData()
self.otherIncomeTableView.reloadData()
}
}catch{
print("No Data to load")
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if tableView.tag == 1 {
let cell: RecuringIncomeTableViewCell = tableView.dequeueReusableCell(withIdentifier: "recurringIncomeCell") as! RecuringIncomeTableViewCell
let store = stores [indexPath.row]
cell.incomeNameLabel.text = store.incomeName
cell.amountLabel.text = store.amount
return cell
}
else {
let cell: OtherIncomeTableViewCell = tableView.dequeueReusableCell(withIdentifier: "otherIncomeCell") as! OtherIncomeTableViewCell
let otherIncomes = other [indexPath.row]
cell.incomeNameLabel.text = otherIncomes.incomeName
cell.amountLabel.text = otherIncomes.amount
return cell
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
//performSegue(withIdentifier: "editStore", sender: nil)
if tableView.tag == 1 {
rowTbl = tableView.indexPathForSelectedRow?.row
print("current row in tbl 1 is : ",rowTbl)
}else {
rowTbl2 = tableView.indexPathForSelectedRow?.row
print("current row in tbl 2 is : ",rowTbl2)
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "editRecurringIncome"{
let v = segue.destination as! AddIncomeViewController
let indexPath = self.recurringIncomeTableView.indexPathForSelectedRow
let row = indexPath?.row
v.store = stores[row!]
}else if segue.identifier == "editOtherIncome" {
let t = segue.destination as! AddIncomeViewController
let indexPath = self.otherIncomeTableView.indexPathForSelectedRow
let row = indexPath?.row
t.store = other [row!]
}
}
//
func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
print("delete delegate being activated")
return true
}
//For remove row from tableview & object from array.
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
if tableView.tag == 1 {
if editingStyle == .delete {
let task = stores [indexPath.row]
context.delete(task)
(UIApplication.shared.delegate as! AppDelegate).saveContext()
do {
stores = try context.fetch(UserIncome.fetchRequest())
print("Stores deleted from indexPath",stores)
}catch{
print("fail")
}
recurringIncomeTableView.reloadData()
}
self.recurringIncomeTableView.reloadData()
} else if tableView.tag == 2 {
if editingStyle == .delete {
let task = other[indexPath.row]
print("task on otherTblView is : ",task)
context.delete(task)
(UIApplication.shared.delegate as! AppDelegate).saveContext()
otherIncomeTableView.reloadData()
do {
other = try context.fetch(UserIncome.fetchRequest())
print("Stores deleted from indexPath",other)
}catch{
print("fail")
}
}
self.otherIncomeTableView.reloadData()
}
tableView.reloadData()
}
}
you need to delete task like this way
let task = stores [indexPath.row]
context.delete(task)
stores.removeAtIndex(indexPath.row) // i think you forget this line
(UIApplication.shared.delegate as! AppDelegate).saveContext()
try this,hope it will help you
A core data object doesn't really contain any information. It has a pointer to a context and an ID, so when you ask it for information it goes to the store to ask. If the object is deleted from the context then the manage object that you have stored in your array will no longer work and will crash. This is why you should never retain NSManagedObjects. Either
a) Copy the values from core data into an a different object. When you want to delete an object you have to delete it from both the store and the copy that you are retaining. If new objects are inserted, or they are deleted from some other source outside of you viewController it will not update (but also no crash).
b) Use a NSFetchedResultsController and update the results when the values change. This will give a delegate to tell you when changes happen. So all you have to do it delete the object from the store and then the fetchedResultsController will tell you when to remove it.
I am retrieving objects from a relation in parse. The objects I want are successfully retrieved and printed in the output box, but when I run the app my UITable only presents one of the six objects. Any suggestions on how to get all of them up onto my view? I would greatly appreciate it.
class MyGroupsHomePage: UITableViewController {
let cellidentifer = "MyGroupsCell"
var mygroupsdata: NSMutableArray = NSMutableArray()
func findcurrentuserobjects () {
var currentuser = PFUser.query()
currentuser!.whereKey("username", equalTo: PFUser.currentUser()!.username!)
currentuser!.findObjectsInBackgroundWithBlock { (object:[AnyObject]?, error: NSError?) -> Void in
if error == nil && object != nil {
if let object = object as? [PFObject] {
for objects in object {
self.mygroupsdata.addObject(objects)
}
}
}
self.tableView.reloadData()
}
}
override func viewDidLoad() {
super.viewDidLoad()
findcurrentuserobjects()
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.mygroupsdata.count
}
var groupnamearray: NSMutableArray = NSMutableArray()
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell: UITableViewCell = tableView.dequeueReusableCellWithIdentifier(cellidentifer, forIndexPath: indexPath) as! UITableViewCell
let mygroupdata: PFObject = self.mygroupsdata.objectAtIndex(indexPath.row) as! PFObject
let relation = mygroupdata.relationForKey("UserGroups")
let query = relation.query()
query?.findObjectsInBackgroundWithBlock({ (objet:[AnyObject]?, erro: NSError?) -> Void in
if erro == nil && objet != nil {
if let objet = objet as? [PFObject] {
for objets in objet {
println(objets.objectForKey("GroupName")!)
cell.textLabel?.text = objets.objectForKey("GroupName")! as? String
}
}
} else {
println("Error, could not retrieve user groups \(erro)")
}
})
return cell
}
}
As Paulw11 stated, this is the problem:
for objets in objet {
println(objets.objectForKey("GroupName")!)
cell.textLabel?.text = objets.objectForKey("GroupName")! as? String
}
You keep updating the same property "text" in the same textLabel, which I assume is an IBOutlet in the UITableViewCell subclass that you use to define the apparence of your cell. Without knowing more of how you want this text to be layed out it it difficult to suggest an answer. A quick and dirty way could be (I haven't tested):
for objets in objet {
println(objets.objectForKey("GroupName")!)
let obj = objets.objectForKey("GroupName")! as? String
let newString = "\(obj) "
cell.textLabel?.text = "\(cell.textLabel?.text)\(newString)"
}
But, according to what you want to acheive, you might need to add subviews to your UITableViewCell subclass (either on your cell prototype in Storyboard or programmatically).
I have a JSON Data which I want to get into UITable. The data is dynamic so table should update every time view loads. Can anyone help?
{
data = (
{
id = 102076330;
name = "Vicky Arora";
}
)
}
try this....
When you receive response,get the whole array of dictionary
if let arr = response["data"] as? [[String:String]] {
YourArray = arr
// Define YourArray globally
}
Then in tableview cell,cellForRowAtIndexPath method
if let name = YourArray[indexpath.row]["name"] as? String{
label.text = name
}
//Same You can done with id
And don't forget to set number of rows
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete method implementation.
// Return the number of rows in the section.
return YourArray.count
}
Try this one. But this sample i'm using Alamofire and SwitfyJSON. Import it using CocoaPod.
import UIKit
import Alamofire
class TableViewController: UITableViewController{
var users: [JSON] = []
override func viewDidLoad() {
super.viewDidLoad()
Alamofire.request(.GET, "http://xxxxx/users.json").responseJSON { (request, response, json, error) in
if json != nil {
var jsonObj = JSON(json!)
if let data = jsonObj["data"].arrayValue as [JSON]?{
self.users = data
self.tableView.reloadData()
}
}
}
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Potentially incomplete method implementation.
// Return the number of sections.
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete method implementation.
// Return the number of rows in the section.
return users.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("UserCell", forIndexPath: indexPath) as! UITableViewCell
let user = users[indexPath.row]
if let idLabel = cell.viewWithTag(100) as? UILabel {
if let id = user["id"].string{
idLabel.text = id
}
}
if let nameLabel = cell.viewWithTag(101) as? UILabel {
if let name = user["name"].string{
nameLabel.text = name
}
}
return cell
}
}
If you are up to using Core Data, I would suggest using the NSFetchedRequest.
Every time you are getting the data from the server, save it to Core data, and that will automatically update the table view.
Here is a tutorial from Ray Wenderlich