I want to make simple Contact by using core-data and tableView for practicing CoreData.
So I have watched youtube and write it's code.
Finally, I thought that I got this, I made by myself, but tableView doesn't contain any data, I could compile though. Could anyone tell me what is wrong? and hopefully tell me how to check the stored data in core-data?
import UIKit
import CoreData
class ViewController: UIViewController {
var context = (UIApplication.sharedApplication().delegate as!AppDelegate).managedObjectContext
var stores : Contact? = nil
#IBOutlet weak var nameTextField: UITextField!
#IBOutlet weak var phoneTextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
if stores != nil {
nameTextField.text = stores?.name
phoneTextField.text = stores?.phone
context?.save(nil)
} }
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
#IBAction func saveTapped(sender: AnyObject) {
let context = self.context
// Get the description of the entity
if stores != nil { let storeDescription = NSEntityDescription.entityForName("stores", inManagedObjectContext: context!)
// Then, We Create the Managed Object to be inserted into the cored data
stores = Contact(entity: storeDescription!, insertIntoManagedObjectContext: context)
}
// set the attributes
stores?.name = nameTextField.text
stores?.phone = phoneTextField.text
context!.save(nil) // Save The object
let alert = UIAlertView(title: "저장 완료", message: "\(nameTextField.text)님이 전화번호부에 저장 되었습니다", delegate: nil, cancelButtonTitle: "OK")
alert.show()
}
}
And here is my tableviewcontroller.
import UIKit
import CoreData
class TableViewController: UITableViewController , NSFetchedResultsControllerDelegate{
let context = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext
var frc : NSFetchedResultsController = NSFetchedResultsController()
var stores = [Contact]()
override func viewDidLoad() {
super.viewDidLoad()
frc.delegate = self
frc.performFetch(nil)
// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem()
}
override func viewWillAppear(animated: Bool) {
var error:NSError?
let request = NSFetchRequest(entityName: "Contact")
stores = context?.executeFetchRequest(request, error: &error) as! [Contact]
self.tableView.reloadData()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// 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 stores.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! UITableViewCell
// Configure the cell...
let save = stores[indexPath.row]
cell.textLabel!.text = save.name
cell.detailTextLabel!.text = save.phone
return cell
}
func getFetchedResultsController() ->NSFetchedResultsController {
frc = NSFetchedResultsController(fetchRequest: listFetchRequest(), managedObjectContext: context!, sectionNameKeyPath: nil, cacheName: nil)
return frc
}
func listFetchRequest() -> NSFetchRequest {
let fetchRequest = NSFetchRequest(entityName: "Contact")
let sortDescriptor = NSSortDescriptor(key: "name", ascending: true)
fetchRequest.sortDescriptors = [sortDescriptor]
return fetchRequest
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
if segue.identifier == "edit"
{
let destViewController = segue.destinationViewController as! ViewController
let indexPath = self.tableView.indexPathForSelectedRow()
let row = indexPath?.row
destViewController.stores = stores[row!]
}
}
}
Update ----Since I fixed my viewController, It works. It would be like
import UIKit
import CoreData
class ViewController: UIViewController {
var context = (UIApplication.sharedApplication().delegate as!AppDelegate).managedObjectContext
var stores : Contact? = nil
#IBOutlet weak var nameTextField: UITextField!
#IBOutlet weak var phoneTextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
if stores != nil {
nameTextField.text = stores?.name
phoneTextField.text = stores?.phone
context?.save(nil)
} }
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
#IBAction func saveTapped(sender: AnyObject) {
if stores != nil {
edit()
}else {
addNew()
}
navigationController?.popViewControllerAnimated(true)
}
func addNew() {
let description = NSEntityDescription.entityForName("Contact", inManagedObjectContext: context!)
let stores = Contact(entity: description!, insertIntoManagedObjectContext: context)
stores.name = nameTextField.text
stores.phone = phoneTextField.text
context?.save(nil)
}
func edit() {
stores!.name = nameTextField.text
stores!.phone = phoneTextField.text
context?.save(nil)
}
}
1. How to locate and View the SQLlite file.
You can find out the path of the .sqlite file using the below function.
func applicationDirectoryPath() -> String {
return NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true).last! as! String
}
Then use any third party tools to view the content of the Sqlite database like SQLLite Manager (a firefox addon)
2. Displaying data in TableView.
In your tableViewController, instead of using stores variable Use NSFetchedResultController (frc) to retrieve and show the data in TableView. Check out this link for using NSFetchedResultController with TableViewController.
Related
I am trying to show an information which is in Core Data, on UITableViewCell.
I could get the information, but the information wasn't shown on UITableViewCell.
When I set the information on CoreData, I use Modal View then.
I tried to use UITableView.reload() but I couldn't show the information on UITableViewCell.
Please let me know how to show the information when I back from modal view.
This class is about showing the information on UItableView.
import UIKit
import CoreData
protocol FriendListTableViewDelegate {
func reloadTable()
}
class FriendListViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, FriendListTableViewDelegate{
#IBOutlet weak var friendListTableView: UITableView!
var friends:[FriendBasicInfo] = []
override func viewDidLoad() {
super.viewDidLoad()
getData()
}
// Disable to effect the reload
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
reloadTable()
}
// fetch the information from CoreData
func getData() {
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
do {
friends = try context.fetch(FriendBasicInfo.fetchRequest())
} catch {
print("error")
}
}
func reloadTable() {
friendListTableView.reloadData()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return friends.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let friendCell = tableView.dequeueReusableCell(withIdentifier: "FriendListCell") as! FriendListTableViewCell
let friendName = friendCell.viewWithTag(1) as? UILabel
let friendImage = friendCell.viewWithTag(2) as? UIImageView
friendName?.text = friends[indexPath.row].name
friendImage?.image = friends[indexPath.row].photo?.toImage()
return friendCell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 80
}
}
This class is to get the information from CoreData
import UIKit
import XLPagerTabStrip
import Eureka
import CoreData
import ImageRow
class InputFriendInforViewController: FormViewController, IndicatorInfoProvider {
var itemInfo: IndicatorInfo = "Info"
var friendPhoto: UIImage?
var friendName: String = ""
var friendBirthday: Date?
var friendGender: String = ""
var friendListTableViewDelegate: FriendListTableViewDelegate!
override func viewDidLoad() {
super.viewDidLoad()
form +++
Section("Friend Information")
<<< ImageRow(){
$0.title = "Image"
$0.sourceTypes = [.PhotoLibrary, .SavedPhotosAlbum, .Camera]
$0.value = UIImage(named: "noImage")
$0.onChange { [unowned self] row in
self.friendPhoto = row.value!
}
}
<<< TextRow(){ row in
row.title = "Name"
row.placeholder = "Enter Name here"
}.onChange { name in
self.friendName = name.value!
}
<<< DateRow(){ row in
row.title = "Birthday"
row.value = Date(timeIntervalSinceReferenceDate: 0)
}.onChange {date in
self.friendBirthday = date.value!
}
<<< PushRow<String>(){row in
row.title = "Gender"
row.options = ["Male","Female","Other"]
}.onChange {gender in
self.friendGender = gender.value!
}
+++ Section()
<<< ButtonRow() {
$0.title = "SAVE"
}.onCellSelection {_, _ in
self.saveInfo()
}
}
// MARK: - IndicatorInfoProvider
func indicatorInfo(for pagerTabStripController: PagerTabStripViewController) -> IndicatorInfo {
return itemInfo
}
// save friend Info for Core Data
func saveInfo (){
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }
let managedContext = appDelegate.persistentContainer.viewContext
let friendEntity = NSEntityDescription.entity(forEntityName: "FriendBasicInfo", in: managedContext)!
let friendInfo = NSManagedObject(entity: friendEntity, insertInto: managedContext)
// make unique user ID
let friendUid = NSUUID().uuidString
// Image Data UIImage to png Data
let pngImage = self.friendPhoto?.toPNGData()
friendInfo.setValue(friendUid, forKey: "userID")
friendInfo.setValue(pngImage, forKey: "photo")
friendInfo.setValue(self.friendName, forKey: "name")
friendInfo.setValue(self.friendBirthday, forKey: "birthday")
friendInfo.setValue(self.friendGender, forKey: "gender")
do {
try managedContext.save()
self.dismiss(animated: true, completion:nil)
} catch let error as NSError {
print("Could not save. \(error), \(error.userInfo)")
}
}
}
This class is about UITableViewCell
import UIKit
class FriendListTableViewCell: UITableViewCell {
#IBOutlet weak var sampleImageView: UIImageView!
#IBOutlet weak var sampleLabel:UILabel!
}
V/r,
As you are using an extra data source array just reloading the table view doesn't consider the new inserted item.
There are a few options
Use NSFetchedResultsController. It updates the UI automatically when the context was saved.
On dismiss insert the new item into the data source array and a new row into the table view.
Observe NSManagedObjectContextDidSaveNotification and insert the item as described in 2.
Refetch the entire data and reload the table view.
The options are in order of efficiency. Version 1 is the most efficient one.
Side note:
viewWithTag is horribly old-fashioned. You got outlets, use them for example
cell.sampleLabel!.text = friends[indexPath.row].name
Your FriendListViewController TableView will reflect any updates to any FriendBasicInfo Entity which was fetched within getData() method. To present a new inserted FriendBasicInfo Entities to the database you have to execute a new fetch with getData() method.
Solution:
func reloadTable() {
getData()
friendListTableView.reloadData()
}
Alternative solution
Advanced monitoring of a fetched entities can be done with NSFetchedResultsController Delegate, this controller will automatically update the FriendListViewController tableview for any updated, inserted or deleted entities.
I have singleton shopping cart in my project like this var fromSharedFood = SingletonCart.sharedFood.food. I am getting all food data from MainVC to DetailVC -> MyCartVC. I have table view in MainVC. I want to save MainVC table view datas to CoreData.
My project was offline. Now, it communicates with web api. I used Singleton for data transition from MainVC to DetailVC to MyCartVC. Now, if user logged in the system I need to save him/her Cart with core data or etc.
i.e. User add a food to cart and log out him/her Cart must be saved when re-login.
I tried with UserDefaults self.myCartUserDefaults.set(myCartTableView.dataSource, forKey: "userCart") but it is not make sense.
I created CoreData entities for food name and price.
Here is MyCartVC
import UIKit
import CoreData
class MyCartViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
var fromDetailFoodNames = ""
var fromDetailFoodPrices = ""
var backgroundView: UIView?
#IBOutlet weak var myCartTableView: UITableView!
#IBOutlet weak var totalPriceLabel: UILabel!
private let persistentContainer = NSPersistentContainer(name: "MyCartData")
var food: Food?
var fromSharedFood = SingletonCart.sharedFood.food
//TODO: - Approve my cart
#IBAction func approveCart(_ sender: Any) {
}
override func viewDidLoad() {
super.viewDidLoad()
self.tabBarController?.tabBar.isHidden = false
myCartTableView.reloadData()
}
override func viewWillAppear(_ animated: Bool) {
self.myCartTableView.reloadData()
if foodCoreData.count == 0 {
myCartTableView.setEmptyView(title: "Sepetinizde ürün bulunmamaktadır", message: "Seçtiğiniz yemekler burada listelenir.")
}
else {
myCartTableView.restore()
self.tabBarController?.viewControllers![1].tabBarItem.badgeValue = "\(foodCoreData.count)"
guard let appDelegate =
UIApplication.shared.delegate as? AppDelegate else {
return
}
let managedContext =
appDelegate.persistentContainer.viewContext
let fetchRequest =
NSFetchRequest<NSManagedObject>(entityName: "MyCartData")
do {
foodCoreData = try managedContext.fetch(fetchRequest)
print("COREDATA FETCH EDİLDİ")
} catch let error as NSError {
print("Could not fetch. \(error), \(error.userInfo)")
}
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if fromSharedFood.count != 0 {
tableView.restore()
}
return fromSharedFood.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let foodName = fromSharedFood[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: "myCartCell", for: indexPath) as! MyCartTableViewCell
cell.myCartFoodNameLabel.text = foodName.ProductTitle
self.tabBarController?.viewControllers![1].tabBarItem.badgeValue = "\(fromSharedFood.count)"
cell.myCartFoodPriceLabel.text = foodName.PriceString
return cell
}
func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
return true
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
fromSharedFood.remove(at: indexPath.row)
tableView.beginUpdates()
tableView.deleteRows(at: [indexPath], with: .automatic)
if fromSharedFood.count == 0 {
myCartTableView.reloadData()
self.tabBarController?.viewControllers![1].tabBarItem.badgeValue = nil }
else {
self.tabBarController?.viewControllers![1].tabBarItem.badgeValue = "\(fromSharedFood.count)"
}
myCartTableView.restore()
}
tableView.endUpdates()
}
}
}
EDIT:
My data come from DetailVC with addBasket() button. First of all, I tried save DetailVC label datas to core data. After that fetched from MyCartVC but did not get any response.
Here is DetailVC:
import UIKit
import CoreData
class DetailViewController: UIViewController, TagListViewDelegate {
#IBOutlet weak var foodTitle: UILabel!
#IBOutlet weak var foodSubTitle: UILabel!
#IBOutlet weak var foodPrice: UILabel!
#IBOutlet weak var foodQuantity: UILabel!
#IBOutlet weak var detailFoodImage: UIImageView!
#IBOutlet weak var tagListView: TagListView!
var window: UIWindow?
var detailFoodName = ""
var detailFoodPrice = ""
var detailPhotoData = String()
var searchFoods: String!
var priceFood: Double!
var foodCoreData: [NSManagedObject] = []
var food: Food?
override func viewDidLoad() {
super.viewDidLoad()
foodQuantity.text = "1"
foodTitle.text = food?.ProductTitle ?? ""
foodPrice.text = food?.PriceString
foodSubTitle.text = food?.Description
tagListView.delegate = self
setupIngredientsTag()
self.tabBarController?.tabBar.isHidden = true
self.navigationController?.navigationItem.title = "Sipariş Detayı"
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let viewController = storyboard.instantiateViewController(withIdentifier: "FoodOrder")
self.window?.rootViewController = viewController
}
func save(foodName: String, foodPrice: String) {
guard let appDelegate =
UIApplication.shared.delegate as? AppDelegate else {
return
}
let managedContext =
appDelegate.persistentContainer.viewContext
let entity =
NSEntityDescription.entity(forEntityName: "MyCartData",
in: managedContext)!
let foods = NSManagedObject(entity: entity,
insertInto: managedContext)
foods.setValue(foodName, forKeyPath: "fromDetailFoodNames")
foods.setValue(foodPrice, forKeyPath: "fromDetailFoodPrices")
do {
try managedContext.save()
foodCoreData.append(foods)
print("COREDATA KAYDEDİLDİ!")
} catch let error as NSError {
print("Could not save. \(error), \(error.userInfo)")
}
}
//TODO:- Add to basket
#IBAction func addBasket(_ sender: Any) {
SingletonCart.sharedFood.food.append(food!)
self.performSegue(withIdentifier: "toMyCart", sender: nil)
self.navigationController?.navigationBar.isHidden = false
self.tabBarController?.tabBar.isHidden = false
self.isLoading(true)
guard let nameToSave = foodTitle.text else { return }
guard let priceToSave = foodPrice.text else { return }
self.save(foodName: nameToSave, foodPrice: priceToSave)
}
#IBAction func cancelButtonClicked(_ sender: UIBarButtonItem) {
self.navigationController?.popViewController(animated: true)
}
#IBAction func favoriteButtonClicked(_ sender: UIBarButtonItem) {
}
override func viewWillAppear(_ animated: Bool) {
self.navigationController?.navigationBar.isHidden = false
}
override func viewWillDisappear(_ animated: Bool) {
self.navigationController?.navigationBar.isHidden = true
}
}
SingletonCart
import Foundation
import UIKit
class SingletonCart {
static let sharedFood = SingletonCart()
var food: [Food] = []
private init() {}
}
Expected output is when user logout save him/her shopping cart.
You've got a couple of concepts wrong from what I see. Core Data Programming Guide will help you a lot to get how it works and how to save data.
For your table listings you should use an NSFetchedResultsController instead of managing a collection yourself.
Then when adding a new model from a detail View Controller you should create a new background context, create the entity, set its values and then save it.
appDelegate.persistentContainer.performBackgroundTask { (context) in
let entity =
NSEntityDescription.entity(forEntityName: "MyCartData",
in: managedContext)!
let foods = NSManagedObject(entity: entity,
insertInto: managedContext)
foods.setValue(foodName, forKeyPath: "fromDetailFoodNames")
foods.setValue(foodPrice, forKeyPath: "fromDetailFoodPrices")
_ = try? managedContext.save()
}
This will save this object to the persistent store, them refresh your view context and your NSFetchedResultsController will update your tableView controller automatically
I have now tried everything (as far as I know), I even made the whole thing from scratch, but it still doesn´t work.
I have made a search bar which can search for data in firebase and display them in a tableview. If the user clicks on a profile in the search bar, a new viewcontroller shows with information about that user.
The problem is that if you start searching, then clicks on a profile, it shows the profile which started on that position in the tableview before the search happened.
This is what I see without searching, it displays the 2 profiles in firebase which is correct:
Now, when I search for the profile "Lars Larsen" it filters like it should:
However, if I now choose the profile by clicking on "Lars Larsen" it shows the profile for "Jonas Larsen", which was at the top before the search?
This is the code for my searchViewController:
import UIKit
import FirebaseDatabase
class SearchTableViewController: UITableViewController,
UISearchResultsUpdating {
let searchController = UISearchController(searchResultsController: nil)
#IBOutlet var findKunder: UITableView!
var loggedInUser: user?
var usersArray = [NSDictionary?]()
var filteredUsers = [NSDictionary?]()
var databaseRef = Database.database().reference()
override func viewDidLoad() {
super.viewDidLoad()
searchController.searchResultsUpdater = self
searchController.dimsBackgroundDuringPresentation = false
definesPresentationContext = true
tableView.tableHeaderView = searchController.searchBar
databaseRef.child("Buyers").queryOrdered(byChild: "Personnr").observe(.childAdded, with: { (snapshot) in
let key = snapshot.key
let snapshot = snapshot.value as? NSDictionary
snapshot?.setValue(key, forKey: "Personnr")
self.usersArray.append(snapshot)
//Insert rows
self.findKunder.insertRows(at: [IndexPath(row:self.usersArray.count-1,section:0)], with: UITableViewRowAnimation.automatic)
}) { (error) in
print(error)
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSections(in 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
if searchController.isActive && searchController.searchBar.text != ""{
return filteredUsers.count
}
return self.usersArray.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
let user : NSDictionary?
if searchController.isActive && searchController.searchBar.text != ""{
user = filteredUsers[indexPath.row]
}
else
{
user = self.usersArray[indexPath.row]
}
cell.textLabel?.text = user? ["Navn"] as? String
cell.detailTextLabel?.text = user?["Telefonnr"] as? String
// Configure the cell...
return cell
}
func updateSearchResults(for searchController: UISearchController) {
filterContent(searchText: self.searchController.searchBar.text!)
}
func filterContent(searchText:String)
{
self.filteredUsers = self.usersArray.filter{ user in
var fNavn = false
var personNr = false
var searchBil = false
var telefonNr = false
var korekortNr = false
if let Navn = user!["Navn"] as? String {
fNavn = Navn.lowercased().contains(searchText.lowercased())
}
if let Bil = user!["Bil"] as? String {
searchBil = Bil.lowercased().contains(searchText.lowercased())
}
if let Personnr = user!["Personnr"] as? String {
personNr = Personnr.lowercased().contains(searchText.lowercased())
}
if let Kørekortnr = user!["Kørekortnr"] as? String {
korekortNr = Kørekortnr.lowercased().contains(searchText.lowercased())
}
if let Telefonnr = user!["Telefonnr"] as? String {
telefonNr = Telefonnr.lowercased().contains(searchText.lowercased())
}
return fNavn || personNr || searchBil || korekortNr || telefonNr
}
tableView.reloadData()
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?)
{
let showUserProfileViewController = segue.destination as! userProfileViewController
showUserProfileViewController.loggedInUser = self.loggedInUser
if let indexPath = tableView.indexPathForSelectedRow {
let user = usersArray[indexPath.row]
showUserProfileViewController.otherUser = user
}
}
}
This is the code I use to show the profiles:
import UIKit
import Firebase
class ProfileViewController: UIViewController {
//Outlets
var loggedInUser:User?
var otherUser:NSDictionary?
var databaseRef:DatabaseReference!
var loggedInUserData: NSDictionary?
#IBOutlet weak var Biler: UILabel!
#IBOutlet weak var Navn: UILabel!
#IBOutlet weak var kundeInfo: UILabel!
#IBOutlet weak var bilInfo: UILabel!
#IBOutlet weak var telefonNr: UILabel!
#IBOutlet weak var korekortNr: UILabel!
#IBOutlet weak var personNr: UILabel!
#IBOutlet weak var Interesse: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
self.databaseRef = Database.database().reference()
databaseRef.child("Buyers").child(self.otherUser?["Personnr"] as! String).observe(.value, with: { (snapshot) in
let uid = self.otherUser?["Personnr"] as! String
self.otherUser = snapshot.value as? NSDictionary
self.otherUser?.setValue(uid, forKey: "Personnr")
self.Navn.text = self.otherUser?["Navn"] as? String
self.bilInfo.text = self.otherUser?["Bil"] as? String
self.telefonNr.text = self.otherUser?["Telefonnr"] as? String
self.Interesse.text = self.otherUser?["Interesse"] as? String
self.personNr.text = self.otherUser?["Personnr"] as? String
self.korekortNr.text = self.otherUser?["Kørekortnr"] as? String
}
// Do any additional setup after loading the view.
)}
Please let me know if you need any other information. I hope you can help.
If the search bar is active and it does contain text, you should pass a user to ProfileViewController from the filteredUsers array and not usersArray.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
[...]
if let indexPath = tableView.indexPathForSelectedRow {
if searchController.isActive && searchController.searchBar.text != "" {
showUserProfileViewController.otherUser = filteredUsers[indexPath.row]
} else {
showUserProfileViewController.otherUser = usersArray[indexPath.row]
}
}
}
On a side note, you shouldn't us NSDictionary in Swift. Use Swift's Dictionary instead (for your case, if would be [String: Any]).
If I'm understanding your code correctly, your issue is here:
override func prepare(for segue: UIStoryboardSegue, sender: Any?)
{
let showUserProfileViewController = segue.destination as! userProfileViewController
showUserProfileViewController.loggedInUser = self.loggedInUser
if let indexPath = tableView.indexPathForSelectedRow {
let user = usersArray[indexPath.row]
showUserProfileViewController.otherUser = user
}
}
let user = usersArray[indexPath.row] // this is wrong
Since you filtered the results if the search bar is active you have to use your updated datasource
let user = filteredUsers[indexPath.row]
Basically, you're passing the wrong information before you segue.
I apologize in advance if this is confusing. I'm new to Swift with no prior experience and learning.
Basically, I have a UITableView that pulls its data form another viewController with four text fields. The data is saved and added to the tableView (only displaying the two of the fields in the cell). When the cell is selected it segues back to the viewController and adds the data in the appropriate text fields. This also utilized CoreData and NSFetchResultsController. All of this is working properly so far, just wanted to give a little back ground on whats going on.
The Problem...
I am trying to get the tableView to display the data in two sections. I want the first section (section 0) to display the data added to the list created by adding the text fields on the viewController. However, the tableView adds first row as the first section (with the header correct) and then adds the second row (and on) as the second section (section 1)(with section header). I want to add all new items from the viewController to section 0, leaving section 1 blank for when I figure out how to cross items off section 0 and move to section 1 by selecting the row (Coming in the future).
I am new to this site as well and could not figure out how to post my code. If anyone needs to look through it you may have to walk me through adding it. Also, I have trouble converting Objective C to Swift please keep that in mind while answering. Thank you!! and I apologize for being difficult.
(tableView Controller "SList")
class ShoppingList: UIViewController, NSFetchedResultsControllerDelegate, UITableViewDataSource, UITableViewDelegate {
let moc = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext
var frc : NSFetchedResultsController = NSFetchedResultsController()
func itemFetchRequest() -> NSFetchRequest{
let fetchRequest = NSFetchRequest(entityName: "SList")
let primarySortDescription = NSSortDescriptor(key: "slitem", ascending: true)
let secondarySortDescription = NSSortDescriptor(key: "slcross", ascending: true)
fetchRequest.sortDescriptors = [primarySortDescription, secondarySortDescription]
return fetchRequest
}
func getFetchRequetController() ->NSFetchedResultsController{
frc = NSFetchedResultsController(fetchRequest: itemFetchRequest(), managedObjectContext: moc, sectionNameKeyPath: "slitem", cacheName: nil)
return frc
}
#IBOutlet weak var tableView: UITableView!
#IBAction func AddNew(sender: AnyObject) {
frc = getFetchRequetController()
frc.delegate = self
do {
try frc.performFetch()
} catch _ {
print("Failed to perform inital fetch.")
return
}
self.tableView.reloadData()
}
override func viewDidLoad() {
super.viewDidLoad()
frc = getFetchRequetController()
frc.delegate = self
do {
try frc.performFetch()
} catch _ {
print("Failed to perform inital fetch.")
return
}
self.tableView.reloadData()
//TableView Background Color
self.tableView.backgroundColor = UIColor.clearColor()
tableView.reloadData()
self.navigationItem.leftBarButtonItem = self.editButtonItem()
self.tableView.separatorColor = UIColor.blackColor()
}
override func viewDidDisappear(animated: Bool) {
frc = getFetchRequetController()
frc.delegate = self
do {
try frc.performFetch()
} catch _ {
print("Failed to perform inital fetch.")
return
}
self.tableView.reloadData()
}
//TableView Data
func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
let managedObject:NSManagedObject = frc.objectAtIndexPath(indexPath) as! NSManagedObject
moc.deleteObject(managedObject)
do {
try moc.save()
} catch _ {
print("Failed to save.")
return
}
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
let numberOfSections = frc.sections?.count
return numberOfSections!
}
//table section headers
func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String?{
let sectionHeader = "Items - #\(frc.sections![section].numberOfObjects)"
let sectionHeader1 = "Crossed Off Items - #\(frc.sections![section].numberOfObjects)"
if (section == 0) {
return sectionHeader
}
if (section == 1){
return sectionHeader1
}else{
return nil
}
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let numberOfRowsInSection = frc.sections?[section].numberOfObjects
return numberOfRowsInSection!
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)
let items = frc.objectAtIndexPath(indexPath) as! SList
cell.backgroundColor = UIColor.clearColor()
cell.textLabel?.text = "\(items.slitem!) - Qty: \(items.slqty!)"
cell.textLabel?.font = UIFont.systemFontOfSize(23)
return cell
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func controllerDidChangeContent(controller: NSFetchedResultsController) {
tableView.reloadData()
}
//segue to add/edit
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if segue.identifier == "edit" {
let cell = sender as! UITableViewCell
let indexPath = tableView.indexPathForCell(cell)
let SListController:SLEdit = segue.destinationViewController as! SLEdit
let items:SList = frc.objectAtIndexPath(indexPath!) as! SList
SListController.item = items
}
}
}
(ViewController "SLEdit")
class SLEdit: UIViewController {
let moc = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext
#IBOutlet weak var slitem: UITextField!
#IBOutlet weak var sldesc: UITextField!
#IBOutlet weak var slqty: UITextField!
#IBOutlet weak var slprice: UITextField!
var item: SList? = nil
override func viewDidLoad() {
super.viewDidLoad()
if item != nil{
slitem.text = item?.slitem
sldesc.text = item?.sldesc
slqty.text = item?.slqty
slprice.text = item?.slprice
}
// "x" Delete Feature
self.slitem.clearButtonMode = UITextFieldViewMode.WhileEditing
self.sldesc.clearButtonMode = UITextFieldViewMode.WhileEditing
self.slqty.clearButtonMode = UITextFieldViewMode.WhileEditing
self.slprice.clearButtonMode = UITextFieldViewMode.WhileEditing
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func dismissVC() {
navigationController?.popViewControllerAnimated(true)
}
#IBAction func saveButton(sender: AnyObject) {
if item != nil {
edititems()
} else {
createitems()
}
dismissVC()
}
func createitems() {
let entityDescription = NSEntityDescription.entityForName("SList", inManagedObjectContext: moc)
let item = SList(entity: entityDescription!, insertIntoManagedObjectContext: moc)
item.slitem = slitem.text
item.sldesc = sldesc.text
item.slqty = slqty.text
item.slprice = slprice.text
if slitem.text == nil{
createitems()
}else{
edititems()
}
do {
try moc.save()
} catch _ {
return
}
}
func edititems() {
item?.slitem = slitem.text!
item?.sldesc = sldesc.text!
item?.slqty = slqty.text!
item?.slprice = slprice.text!
do {
try moc.save()
} catch {
return
}
}
}
I think the problem lies in your FRC configuration:
frc = NSFetchedResultsController(fetchRequest: itemFetchRequest(), managedObjectContext: moc, sectionNameKeyPath: "slitem", cacheName: nil)
Because sectionNameKeyPath is slitem, the FRC creates a new section for each different value of slitem.
If the slcross attribute is used to indicate that the item has been crossed off the list, specify that as the sectionNameKeyPath:
frc = NSFetchedResultsController(fetchRequest: itemFetchRequest(), managedObjectContext: moc, sectionNameKeyPath: "slcross", cacheName: nil)
You will also need to modify the sorting of the fetch request (it MUST be sorted so that all items in a given section are together):
let primarySortDescription = NSSortDescriptor(key: "slcross", ascending: true)
let secondarySortDescription = NSSortDescriptor(key: "slitem", ascending: true)
fetchRequest.sortDescriptors = [primarySortDescription, secondarySortDescription]
I have been trying to set up, what seems like it should be, a simple app that allows a user to update a food item with a price and store where the price was found. The main issue I know is trying to blend Swift with Objective-C even though Apple hasn't worked out the kinks yet for Swift and it's ever changing.
Anyways I have set up my AllowedTableViewController as follows
class AllowedTableViewController: UITableViewController {
var myAllowedList : Array<AnyObject> = []
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(animated: Bool) {
let appDel: AppDelegate = UIApplication.sharedApplication().delegate as AppDelegate
let context: NSManagedObjectContext = appDel.managedObjectContext!
let freq = NSFetchRequest(entityName: "FoodAllowed")
myAllowedList = context.executeFetchRequest(freq, error: nil)!
tableView.reloadData()
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "update" {
var indexPath: NSIndexPath = self.tableView.indexPathForSelectedRow()!
var selectedItem: NSManagedObject = myAllowedList[indexPath.row] as NSManagedObject
let IVC: AllowedViewController = segue.destinationViewController as AllowedViewController
IVC.name = selectedItem.valueForKey("name")as String
IVC.store = selectedItem.valueForKey("store")as String
IVC.price = selectedItem.valueForKey("price")as String
IVC.existingItem = selectedItem
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// 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 myAllowedList.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
//Configure the Cell
let CellID: NSString = "Allowed"
var cell: UITableViewCell = tableView.dequeueReusableCellWithIdentifier(CellID) as UITableViewCell
var data: NSManagedObject = myAllowedList[indexPath.row] as NSManagedObject
cell.textLabel.text = (data.valueForKeyPath("name")as String)
var pri = data.valueForKeyPath("price")as String
var str = data.valueForKeyPath("store")as String
cell.detailTextLabel?.text = "\(pri) name/s - \(str)"
return cell
}
//Override to support conditional editing of the table view
override func tableView(tableView: UITableView?, canEditRowAtIndexPath indexPath: NSIndexPath?) -> Bool {
//Return NO if you do not want the specified item to be editable
return true
}
//Override to support editing the table view
override func tableView(tableView: UITableView?, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath?) {
let appDel: AppDelegate = UIApplication.sharedApplication().delegate as AppDelegate
let context: NSManagedObjectContext = appDel.managedObjectContext!
if editingStyle == UITableViewCellEditingStyle.Delete {
if let tv = tableView {
context.deleteObject(myAllowedList[indexPath!.row] as NSManagedObject)
myAllowedList.removeAtIndex(indexPath!.row)
tv.deleteRowsAtIndexPaths([indexPath!.row], withRowAnimation: UITableViewRowAnimation.Fade)
}
var error: NSError? = nil
if !context.save(&error) {
abort()
}
}
}
}
My user editable text field code in the view controller are as follows.
{
class AllowedViewController: UIViewController {
#IBOutlet var textFieldname: UITextField!
#IBOutlet var textFieldstore: UITextField!
#IBOutlet var textFieldprice: UITextField!
var name: String = ""
var store: String = ""
var price: String = ""
var existingItem: NSManagedObject!
override func viewDidLoad() {
super.viewDidLoad()
if existingItem == nil {
textFieldname.text = name
textFieldstore.text = store
textFieldprice.text = price
}
// Do any additional setup after loading the view.
}
func saveTapped(sender: AnyObject) {
//Reference to our app delegate
let appDel: AppDelegate = UIApplication.sharedApplication().delegate as AppDelegate
//Reference NS Managed Object Context
let contxt: NSManagedObjectContext = appDel.managedObjectContext!
let en = NSEntityDescription.entityForName("FoodAllowed", inManagedObjectContext: contxt)!
//Check if item exists
if (existingItem != nil) {
existingItem.setValue(textFieldname.text as String, forKey: "name")
existingItem.setValue(textFieldstore.text as String, forKey: "store")
existingItem.setValue(textFieldprice.text as String, forKey: "price")
}else {
//Create instance of pur data model and intialize
var newItem = DataModel(entity: en, insertIntoManagedObjectContext: contxt)
//Map our properties
newItem.name = [textFieldname.text]
newItem.store = [textFieldstore.text]
newItem.price = [textFieldprice.text]
//Save our content
contxt.save(nil)
println(newItem)
//Navigate back to root view controll
self.navigationController?.popToRootViewControllerAnimated(true)
}
}
func cancelTapped(sender: AnyObject) {
//Navigate back to root view controll
self.navigationController?.popToRootViewControllerAnimated(true)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
}
My NSManaged DataModel is
{
#objc(DataModel)
class DataModel: NSManagedObject {
//properties feeding the attributes in our entity
//must match the entity attributes
#NSManaged var name: [String]
#NSManaged var store: [String]
#NSManaged var price: [String]
}
}
When I run the app in the simulator I get the following error
'NSInvalidArgumentException', reason: 'Unacceptable type of value for attribute: property = "name"; desired type = NSString; given type = Swift._NSSwiftArrayImpl; value = (Apples).'
What am I doing wrong?
The error message indicates that "name", "store" and "price" are String properties
of your Core Data entity, but you have defined them as [String], i.e. an array
of strings. It should be
#objc(DataModel)
class DataModel: NSManagedObject {
#NSManaged var name: String
#NSManaged var store: String
#NSManaged var price: String
}
And consequently
newItem.name = textFieldname.text // not: [textFieldname.text]
// ...
Better yet, let Xcode generate the managed object subclasses
(Editor -> Create NSManagedObject Subclass ... in the Xcode menu).