Table view not updating with Firebase - swift

I am trying to get a table view to update from my Firebase database. My data is structured like this (for reporting automobiles):
Reports:
Randomly generated report ID number:
make: "make of car"
model: "make of model"
I don't think that I am calling the data correctly. I do not know how to select for the random report ID. But there may be something else that I am missing. I am trying to get only the make and model of the vehicle to display in the text of the cell
var reportList:[String] = []
var ref: DatabaseReference!
var handle: DatabaseHandle?
#IBOutlet weak var reportsTableView: UITableView!
#IBAction func backButtonPressed(_ sender: UIBarButtonItem) {
self.performSegue(withIdentifier: "reportsToHome", sender: self)
}
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return reportList.count
}
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
let cell = UITableViewCell(style: .default, reuseIdentifier: "cell")
cell.textLabel?.text = reportList[indexPath.row]
return cell
}
override func viewDidLoad() {
super.viewDidLoad()
ref = Database.database().reference()
handle = ref.child("Reports").observe(.childAdded, with: { (snapshot) in
if (snapshot.value as? String) != nil
{
let make = String(describing: self.ref.child("Reports").child("make"))
let model = String(describing: self.ref.child("Reports").child("model"))
self.reportList.append(make + model)
self.reportsTableView.reloadData()
}
}
// Do any additional setup after loading the view.
)}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}

I'm not able to test this, but I have feeling that you were trying to cast the snapshot as a string. Instead, you should cast it as a Dictionary so you can easily retrieve the data by key.
Try this code. It sets the snapshot as a dictionary, and from there you are able to retrieve the make and model:
override func viewDidLoad() {
super.viewDidLoad()
ref = Database.database().reference()
handle = ref.child("Reports").observe(.childAdded, with: { (snapshot) in
if let reports = snapshot.value as? NSDictionary {
var make = reports?["make"] as? String
var model = reports?["model"] as? String
self.reportList.append(make + model)
self.reportsTableView.reloadData()
}
}
)}
Going further, you can create a Report class:
class Report: NSObject {
var make: String?
var model: String?
}
You can then set the make and model from the snapshot to create a new Report object.
var make = reports?["make"] as? String
var model = reports?["model"] as? String
let newReport = Report()
newReport.setValuesForKeys(reports)
I hope this works, if not I'll look again.

Related

Anyone know how to make username appear on shop?

Making a shopping cart app and come to a problem. I have a login page where if logged in it will store in core data and login but I want to make the username appear on the table view on another view controller
LoginVC:
import UIKit
import CoreData
class LoginVC: UIViewController {
#IBOutlet weak var username: UITextField!
#IBOutlet weak var password: UITextField!
var context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view
fetchData()
}
#IBAction func login(_ sender: Any) {
for acc in userList {
if username.text == acc.username && password.text == acc.password {
currentUser = username.text!
try! context.save()
performSegue(withIdentifier: "DisplayShop1", sender: nil)
}
/*else if username.text == "" || password.text == "" || username.text != acc.username || password.text != acc.username {
let alert = UIAlertController(title: "Alert", message: "Please enter the right credentials", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Dismiss", style: .default, handler: nil))
present(alert, animated: true, completion: nil)
}*/
}
}
func fetchData(){
userList = try! context.fetch(User.fetchRequest())
}
}
ListingShopVC
import UIKit
import CoreData
class ListingShopVC: UIViewController, UITableViewDelegate, UITableViewDataSource{
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var usernameloggedin: UILabel!
#IBOutlet weak var creditsdisplay: UILabel!
var context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
var myUser:[User] = []
var mySecond:[Product] = []
var mySecondF:[Product] = []
var id:String = ""
var name:String = ""
var price:Double = 0.0
var image:String = ""
var details:String = ""
#IBOutlet weak var searchBar: UISearchBar!
override func viewDidLoad() {
super.viewDidLoad()
fetch()
tableView.delegate = self
tableView.dataSource = self
extracted()
usernameloggedin.text = "Welcome \(userList)"
creditsdisplay.text = "You have \(userList)"
}
// MARK: - Table view data source
func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return mySecond.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "hello", for: indexPath) as! TableCellData
// Configure the cell...
cell.shopTitle.text = mySecond[indexPath.row].name
cell.shopPrice.text = "$" + String(mySecond[indexPath.row].price) + "0"
cell.shopDesc.text = mySecond[indexPath.row].description
if let imageURL = URL(string: mySecond[indexPath.row].image) {
DispatchQueue.global().async {
let data = try? Data(contentsOf: imageURL)
if let data = data {
let image = UIImage(data: data)
DispatchQueue.main.async {
cell.shopImageView.image = image
}
}
}
}
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
id = mySecond[indexPath.row].id
name = mySecond[indexPath.row].name
price = mySecond[indexPath.row].price
image = mySecond[indexPath.row].image
//print("At table \(image)")
details = mySecond[indexPath.row].description
performSegue(withIdentifier: "toDetails", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender:Any?){
if segue.identifier == "toDetails"{
let vc = segue.destination as! ProductDetail
vc.productID = id
vc.productName = name
vc.productPrice = price
vc.productPicture = image
vc.productDetails = details
print(vc.productDetails)
}
}
func extracted(){
guard let url = URL(string: "http://rajeshrmohan.com/sport.json")
else {return}
let task = URLSession.shared.dataTask(with: url){
(data,response,error) in
guard let dataResponse = data,
error == nil else {
print(error?.localizedDescription ?? "Response Error")
return
}
do {
let decoder = JSONDecoder()
let model:[Product] = try decoder.decode([Product].self, from: dataResponse)
//print(model)
for i in 0..<model.count{
self.mySecond.append(model[i])
}
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
catch let parsingError {
print("Error", parsingError)
}
}
task.resume()
}
#IBAction func logOut(_ sender: Any) {
}
func fetch(){
userList = try! context.fetch(User.fetchRequest())
tableView.reloadData()
}
}
The top part https://i.stack.imgur.com/9RahD.jpg
I just want to make it appear on on top also it seems my login page and codes doesn't work well If I put a if empty so if any probable suggestions will be appreciated
As far as I understand you can't pass data between pages. Add this to the page you got the userList from.
#IBAction func okAction(_ sender: Any) {
let controller = storyboard?.instantiateViewController(withIdentifier: "DisplayShop1") as! ListingShopVC
controller.userList = userList
controller.modalPresentationStyle = .fullScreen
present(controller, animated: true, completion: nil)
}
If you add this to the page you want to transfer the userList of later, you can call the userList.
var userList: String = ""
You almost have it. Instead of passing the username into the string, you're passing the entire list of Core Data objects which get formatted as a string, but not in the way you want. You should get the username for the user, then pass that into the string:
let username = userList.first?.username ?? ""
usernameloggedin.text = "Welcome \(username)"
creditsdisplay.text = "You have \(username)"
That said, here's a couple critiques to make this work even more reliably.
I would move this part of the code to your function to load from the database. That way if you ever reload the data from the database, it will get the correct user, and the name will be updated appropriately.
You should pick one user that is doing the shopping, and only one user. Currently you are getting a list of users and keeping the list of users. There's also no sorting to determine which user is used, so it could change mid-shop. To fix that, I suggest you create a new property to store the current user that is of type User, rather than [User], and only load one user from the database. The fetch request will return an array, so you need to get and keep only the first one. Additionally, to make this even more reliable, you might consider having your first view check if there is a user logged in, get that user from the database (which you are already doing), and pass that user into the shop view using dependency injection. There's plenty of tutorials out there for how to handle that, but basically you get a reference to the second view in the prepareForSegue call and set the user property on that second view to the desired user, thereby 'injecting your dependency'.

Not able to load data in segmented control tableview from Firestore database

I am trying to populate table view w.r.t the segmented control by getting data from Firestore database, but due to some reason I am not being able to do so, I am trying an example available online which has preset data, but here I am retrieving data from the Firestore database.
It is not giving any runtime error as well, simply not loading the data. Below is the screenshot of the view controller of the code. Kindly assist
class FirstSegementViewController: UIViewController {
#IBOutlet var segmentControl:UISegmentedControl!
#IBOutlet var tableView: UITableView!
var s1Post:[s1] = []
var s2Post:[s2] = []
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
retrieveAllPosts()
}
func retrieveAllPosts(){
let postsRef = Firestore.firestore().collection("posts").limit(to: 50)
postsRef.getDocuments { (snapshot, error) in
if let error = error {
print(error.localizedDescription)
} else {
if let snapshot = snapshot {
for document in snapshot.documents {
let data = document.data()
//self.postKey = document.documentID
let username = data["post_author_username"] as? String ?? ""
let postTitle = data["postTitle"] as? String ?? ""
let postcategory = data["postcategory"] as? String ?? ""
let postContent = data["postContent"] as? String ?? ""
let postAuthorProfilePicUrl = data["post_user_profile_pic_url"] as? String ?? ""
let postAuthorSpinnerC = data["post_author_spinnerC"] as? String
let newSourse = s1(_documentId: document.documentID, _username: username, _postTitle: postTitle, _postcategory: postcategory, _postContent: postContent, _postuserprofileImagUrl: postAuthorProfilePicUrl, _postAuthorSpinncerC: postAuthorSpinnerC)
self.s1Post.append(newSourse)
// print(self.postKey)
}
self.tableView.reloadData()
}
}
}
}
#IBAction func indexChanged(_ sender: UISegmentedControl) {
switch segmentControl.selectedSegmentIndex
{
case 0:
retrieveAllPosts()
// label1.text = "First Segment Selected"
case 1:
retrieveAllPosts()
// label1.text = "Second Segment Selected"
default:
break
}
//self.tableView.reloadData()
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destination.
// Pass the selected object to the new view controller.
}
*/
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destination.
// Pass the selected object to the new view controller.
}
*/
}
extension FirstSegementViewController: UITableViewDelegate, UITableViewDataSource{
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
var value = 0
switch segmentControl.selectedSegmentIndex{
case 0:
value = s1Post.count
break
case 1:
value = s2Post.count
break
default:
break
}
return value
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "MyCell", for: indexPath) as! MyWsPostCell
switch segmentControl.selectedSegmentIndex{
case 0:
cell.s11 = s1Post[indexPath.row]
break
case 1:
cell.s21 = s2Post[indexPath.row]
break
default:
break
}
return cell
}
}
You seem to be setting value for self.s1Post only.

Data is not displayed in TableView from Firebase

I have a 2 problems with displaying data in a table from Firebase.
Nothing displayed in TableView from Firebase
I I can not add a link(child) to a variable
Print is working. I get access to Firebase, but nothing is added to TableView. Please, look at my code and correct where i'm wrong.
It's my model
class Exercises {
var titleExercise = ""
var descriptionExercise = ""
init (titleExercise: String, descriptionExercise: String) {
self.titleExercise = titleExercise
self.descriptionExercise = descriptionExercise
}
}
It's my ViewController
class ExercisesViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
//MARK: Properties
var refWorkout: String = ""
var workout: TrainingProgram?
var ref: DatabaseReference!
#IBOutlet weak var tableView: UITableView!
var exercises = [Exercises]()
//MARK: Methods
override func viewDidLoad() {
super.viewDidLoad()
fetchExercises()
tableView.dataSource = self
tableView.delegate = self
refWorkout = workout!.title
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return exercises.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! ExercisesTableViewCell
let workouts = exercises[indexPath.item]
cell.titleLabel.text = workouts.titleExercise
cell.descriptionLabel.text = workouts.descriptionExercise
return cell
}
func fetchExercises() {
Database.database().reference().child("programs").child("OPEN SPACE").child("exercises").observe(.childAdded) { (snapshot) in
print(snapshot.value)
if let dict = snapshot.value as? [String: AnyObject] {
let newTitle = dict["title"] as! String
let newDescription = dict["description"] as! String
let exerciseTableCell = Exercises(titleExercise: newTitle, descriptionExercise: newDescription)
}
}
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
And I have second question. It also addresses this issue.
As you can see, I have refWorkout = workout!.title Here comes the title from previous ViewController , and refWorkout is a child for Firebase. If I will write next code
ref = Database.database().reference().child("programs").child(refWorkout).child("exercises")
ref.observe(.childAdded) { (snapshot) in
print(snapshot.value)
}
Everything will work. Print will work. But if I insert this code to func fetchExercises() - > It will look like
func fetchExercises() {
Database.database().reference().child("programs").child(refWorkout).child("exercises").observe(.childAdded)...
My app crashed.
Please help me with two questions. Thank you!
My Firebase structure
This is a common mistake, you are reloading the table view too soon and you don't assign/append the result to the data source array
The observe API works asynchronously, put the line to reload the table view into the closure
func fetchExercises() {
Database.database().reference().child("programs").child("OPEN SPACE").child("exercises").observe(.childAdded) { (snapshot) in
print(snapshot.value)
if let dict = snapshot.value as? [String: Any] { // most likely all values are value type
let newTitle = dict["title"] as! String
let newDescription = dict["description"] as! String
let exercise = Exercises(titleExercise: newTitle, descriptionExercise: newDescription)
self.exercises.append(exercise)
}
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
Side note:
You class contains 3 bad practices:
Semantically objects used in collection types should be named in singular form.
Don't declare properties with default values if there is an initializer.
There is too much redundant information in the variable names
And in most cases a struct and even constants are sufficient. I'd recommend
struct Exercise {
let title : String
let description : String
}
In a struct you get the initializer for free.

Can't update a value with Core Data

first i come from France so sorry for my english.
Second, I'm new in developpement and i have develop a code for add data and after show them with Core Data. it works.ok
but after i want update but i have a problem i don't know why i can't update my value. There is an error : "fatal error: unexpectedly found nil while unwrapping an Optional value"
i have try many solution since 1 week, but can't find the problem. Thanks if someone can help me ! even a little help :)
this is my code (swift 2.3) :
for show in table view :
import UIKit
import CoreData
class ProduitTableViewController: UITableViewController {
#IBOutlet var table: UITableView!
var produits = [NSManagedObject]()
func refreshStories(refreshControl: UIRefreshControl) {
produits.removeAll()
fetchData()
self.table.reloadData()
refreshControl.endRefreshing()
}
override func viewDidLoad() {
super.viewDidLoad()
self.fetchData()
self.table.addSubview(self.refreshControl!)
self.refreshControl?.addTarget(self, action: #selector(ProduitTableViewController.refreshStories(_:)), forControlEvents: UIControlEvents.ValueChanged)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func fetchData() {
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext
//2
let fetchRequest = NSFetchRequest(entityName: "Produits")
let sort = NSSortDescriptor(key:"dateAjout", ascending:true)
fetchRequest.sortDescriptors = [sort]
//3
do {
let results = try managedContext.executeFetchRequest(fetchRequest)
produits = results as! [NSManagedObject]
} catch let error as NSError {
print("Donnees non recu \(error), \(error.userInfo)")
}
}
// 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 self.produits.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell")
let produit = produits[indexPath.row]
cell!.textLabel!.text = produit.valueForKey("nom") as? String
/*
let id = produit.valueForKey("id") as? String
let date = produit.valueForKey("date") as? NSDate
let localNotification = UILocalNotification()
localNotification.userInfo = ["id" : id!]
localNotification.soundName = UILocalNotificationDefaultSoundName
localNotification.alertBody = "expiré"
localNotification.fireDate = date
UIApplication.sharedApplication().scheduleLocalNotification(localNotification)
UIApplication.sharedApplication().applicationIconBadgeNumber += 1
*/
return cell!
}
override func tableView(tableView: UITableView, editActionsForRowAtIndexPath indexPath: NSIndexPath) -> [UITableViewRowAction]? {
let supprimer = UITableViewRowAction(style: .Normal, title: "Suppr.") { action, index in
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let moc = appDelegate.managedObjectContext
// 3
moc.deleteObject(self.produits[indexPath.row])
appDelegate.saveContext()
// 4
self.produits.removeAtIndex(indexPath.row)
tableView.reloadData()
}
supprimer.backgroundColor = UIColor.redColor()
let update = UITableViewRowAction(style: .Normal, title: "Modifier") { action, index in
}
update.backgroundColor = UIColor.blueColor()
return [supprimer]
}
override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
// the cells you would like the actions to appear needs to be editable
return true
}
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if segue.identifier == "showDetail" {
if let destination = segue.destinationViewController as? DetailViewController {
let row = table.indexPathForSelectedRow?.row
let produit = produits[row!]
let nom = produit.valueForKey("nom") as? String
let id = produit.valueForKey("id") as? String
let detail = produit.valueForKey("detail") as? String
let date = produit.valueForKey("date") as? NSDate
let time = date
let formatter = NSDateFormatter()
formatter.dateFormat = "dd-MM-YY HH:mm"
let formatteddate = formatter.stringFromDate(time!)
destination.dataNom = nom!
destination.dataId = id!
destination.dataDetail = detail!
destination.dataDate = formatteddate
}
}
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
if table.cellForRowAtIndexPath(indexPath) != nil {
self.performSegueWithIdentifier("showDetail", sender: self)
}
}
}
for show detail of the cell :
import CoreData
import UIKit
class DetailViewController: UIViewController {
#IBOutlet var Label: UILabel!
#IBOutlet var Detail: UITextView!
#IBOutlet weak var Date: UILabel!
#IBOutlet weak var Id: UILabel!
var dataNom = ""
var dataDetail = ""
var dataDate = ""
var dataId = ""
override func viewDidLoad() {
super.viewDidLoad()
Label.text = dataNom
Detail.text = dataDetail
Date.text = dataDate
Id.text = dataId
// Do any additional setup after loading the view
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if segue.identifier == "ModifierDetail" {
if let destination = segue.destinationViewController as? ModifierViewController {
destination.modifierNom = dataNom
destination.modifierId = dataId
destination.modifierDetail = dataDetail
destination.modifierDate = dataDate
}
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
}
And the last for update/modify my detail:
import UIKit
import CoreData
class ModifierViewController: UIViewController {
#IBOutlet weak var Nom: UITextField!
#IBOutlet weak var Detail: UITextView!
#IBOutlet weak var Date: UITextField!
var Produits: NSManagedObject!
var managedContext: NSManagedObjectContext!
var modifierNom = ""
var modifierDetail = ""
var modifierDate = ""
var modifierId = ""
override func viewDidLoad() {
super.viewDidLoad()
Nom.text = modifierNom
Detail.text = modifierDetail
Date.text = modifierDate
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func Annuler(sender: UIBarButtonItem) {
navigationController?.popViewControllerAnimated(true)
}
#IBAction func Modifier(sender: UIButton) {
let fetchRequest = NSFetchRequest(entityName:"Produits")
fetchRequest.predicate = NSPredicate(format: "nom = %#", modifierNom)
do {
let list = try managedContext.executeFetchRequest(fetchRequest) as! [Produit]
if list.count == 0 // Check notificationId available then not save
{
let newManagedObject = NSEntityDescription.insertNewObjectForEntityForName("Produits", inManagedObjectContext: managedContext)
newManagedObject.setValue(modifierNom, forKey: "nom")
}
// success ...
} catch let error as NSError {
// failure
print("Fetch failed: \(error.localizedDescription)")
}
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
}
i have create this other file:
import Foundation
import CoreData
#objc(Produit)
class Produit: NSManagedObject {
#NSManaged var nom:String!
}
As you mentioned in the comment, this line causes the crash:
let list = try managedContext.executeFetchRequest(fetchRequest) as! [Produit]
That line is not safe, because you don't know, if casting to [Prodiut]will always be successful.
In general, you should never force-cast (as!) or force-unwrap (!) something when you don't know 1000%, that it will succeed.
To cast safely, you can use guard:
guard let list = try managedContext.executeFetchRequest(fetchRequest) as! [Produit] else {
//do some error handling here and then return
return
}
After that, you can safely use list.
It's really important to understand, what optionals are and how to handle them safely without crashes.

UI TableView cell opening a new view with multiple data using coredata in swift

as a beginner in iOS and Swift, I have a project that has to have a tableview with multiple cells, in which every cell contains several data types. i.e. Strings, dates etc., where in one view controller, there is the table view for viewing the cells, the second view controller is for creating a cell and entering the data, and the third view is for displaying the same data when clicking the cell. I've decided to store all of that using coredata since I was told it's most efficient and simple for beginners. I've used several tutorials on this matter but none of them handle this type of problem I have. Best example is how the Contact list works on iOS.
The code I've done so far is this:
var titleCellList = [NSManagedObject]()
var infoCellList = [NSManagedObject]()
class CellsViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet var cellsTableView: UITableView!
//MARK: Default Functions
override func viewDidLoad() {
super.viewDidLoad()
title = "\"Lists\""
cellsTableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: "Cell")
}
// Do any additional setup after loading the view.
}
// MARK: UITableViewDataSource
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return TitleCellList.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell") as! UITableViewCell
let cellTitle = titleCellList[indexPath.row]
cell.textLabel!.text = cellTitle.valueForKey("title") as? String
return cell
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func viewDidAppear(animated: Bool) {
cellsTableView.reloadData()
}
//MARK: Storing CoreData
func saveName(name: String) {
//1
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext!
//2
let entity = NSEntityDescription.entityForName("Data", inManagedObjectContext: managedContext)
let title = NSManagedObject(entity: entity!, insertIntoManagedObjectContext:managedContext)
//3
title.setValue(name, forKey: "title")
//4
var error: NSError?
if !managedContext.save(&error) {
println("Could not save \(error), \(error?.userInfo)")
}
//5
titleCellList.append(title)
}
//MARK: Fetching CoreData
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
//1
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext!
//2
let fetchRequest = NSFetchRequest(entityName:"Data")
//3
var error: NSError?
let fetchedResults = managedContext.executeFetchRequest(fetchRequest, error: &error) as? [NSManagedObject]
if let results = fetchedResults {
titleCellList = results
} else {
println("Could not fetch \(error), \(error!.userInfo)")
}
}
// MARK: Table Editing Methods
func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
return true
}
func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle == UITableViewCellEditingStyle.Delete {
let appDel:AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let context:NSManagedObjectContext = appDel.managedObjectContext!
context.deleteObject(titleCellList[indexPath.row] as NSManagedObject)
titleCellList.removeAtIndex(indexPath.row)
context.save(nil)
cellsTableView.reloadData()
}
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
tableView.deselectRowAtIndexPath(indexPath, animated: true)
let row = indexPath.row
println("Row: \(row)")
println(titleCellList[row])
performSegueWithIdentifier("checkCellSegue", sender: self)
}
Second View Controller (the one for creating a cell with data)
class AddNewViewController: UIViewController, UITextFieldDelegate {
#IBOutlet var titleTextField: UITextField!
#IBOutlet var shortInfoTextView: UITextView!
//MARK: Default Functions
override func viewDidLoad() {
super.viewDidLoad()
self.titleTextField.delegate = self
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
self.view.endEditing(true)
}
func textFieldShouldReturn(textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
#IBAction func addDataButtonPressed(sender: UIButton) {
if titleTextField.text != "" {
CellsViewController().saveName(titleTextField.text)
titleTextField.text = ""
shortInfoTextView.text = ""
println("New title Added!")
}else {
println("No empty titles allowed!")
}
}
Now, most of this code is from a tutorial, and when I tried adding other data entity's, it didn't work. In the datamodel I currently have only 1 entity named "Data" which contains 4 models. So, to sum it up, I need to store 4 data models in one entity and load them on a different view controller when clicking on a cell which of course, has a title that the user wrote. And just to note, I've spent hours searching online for an answer so this is my last line so to say.
Any help would be greatly appreciated.
So, here is the one approach I used on this little issue. I basically just pass arguments with the prepareForSegue method, and inside of it I just pass the data I want to use in the other class/VC.
The Code:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
// Setter for Second VC, destination path --> var declarations in Second VC
if segue.identifier == "checkCellSegue" {
let destination = segue.destinationViewController as! SecondViewController
if let indexPath = self.tableView?.indexPathForCell(sender as! UITableViewCell) {
let object = fetchedResultsController?.objectAtIndexPath(indexPath) as? Data
destination.cellTitle = object?.cellTitle
destination.textViewInfo = object?.textViewInfo
destination.timerValue = object?.timerValue
}
}
So, first we declare the destination which is the name of our Second VC or whatever you named it. Then, since I am accessing data trough a TableView cell we need to fetch my CoreData Entity with the indexPath. After that the final declaration is the Model Class which has all the data values from the entity, which will work like a singleton.
destination.cellTitle // --> in the 2.nd VC we declared a new var called cellTitle, var cellTitle:String
object?.cellTitle // --> in the model class "Data.swift" the declaration is #NSManaged var cellTitle:String
So, thats it. I am still a little newbie on iOS so if there are any mistakes, just say so.