Did from the example: https://drive.google.com/open?id=0B3d4jY23eLOoejdRWkE0SG1VMkk
Step 1. I implemented the Core Data "Database". I have now 17 record and want to view all of them through the Cells in TableView ...
I did as like it was in tutorial, but it doesn't work. I wasted a week, and can't find an error :( It loads perfectly, but data is not displayed in cells :( So...
import Foundation
import CoreData
#available(iOS 10.0, *)
class DatabaseController
{
private init() { }
class func getContext() -> NSManagedObjectContext
{
return persistentContainer.viewContext
}
// MARK: - Core Data stack
static var persistentContainer: NSPersistentContainer =
{
let container = NSPersistentContainer.init(name: "Database")
//let container = NSPersistentContainer(name: "Database")
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError?
{
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
return container
}()
// MARK: - Core Data Saving support
class func saveContext ()
{
let context = persistentContainer.viewContext
if context.hasChanges
{
do {
try context.save()
}
catch {
let nserror = error as NSError
fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
}
}
}
}
Mainly 3 files ... save/get/persistant container in "DatabaseController.swift"
Next "AppDelegate.Swift"...
func applicationWillTerminate(_ application: UIApplication)
{
DatabaseController.saveContext()
}
// MARK: - Core Data stack
lazy var applicationDocumentsDirectory: NSURL =
{
let urls = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
return urls[urls.count-1] as NSURL
}()
lazy var managedObjectModel: NSManagedObjectModel =
{
let modelURL = Bundle.main.url(forResource: "Database", withExtension: "momd")!
return NSManagedObjectModel(contentsOf: modelURL)!
}()
lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator? =
{
var coordinator: NSPersistentStoreCoordinator? = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel)
let url = self.applicationDocumentsDirectory.appendingPathComponent("Database.sqlite")
var error: NSError? = nil
var failureReason = "There was an error creating or loading the application's saved data."
do
{
try coordinator!.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: url, options: nil)
}
catch {
coordinator = nil
let dict = NSMutableDictionary()
dict[NSLocalizedDescriptionKey] = "Failed to initialize the application's saved data"
dict[NSLocalizedFailureReasonErrorKey] = failureReason
dict[NSUnderlyingErrorKey] = error
let error = NSError(domain: "YOUR_ERROR_DOMAIN", code: 9999, userInfo: dict as? [AnyHashable : Any])
NSLog("Unresolved error \(String(describing: error)), \(error.userInfo)")
print(error.localizedDescription)
abort()
}
return coordinator
}()
lazy var managedObjectContext: NSManagedObjectContext? =
{
let coordinator = self.persistentStoreCoordinator
if coordinator == nil
{
return nil
}
var managedObjectContext = NSManagedObjectContext()
managedObjectContext.persistentStoreCoordinator = coordinator
return managedObjectContext
}()
Main functions responsible for DataCore
And final file for TableView. Before i made it as DataCore , cells were displayed like this
if indexPath.row == 0 {
cell.postImageView.image = UIImage(named: "ENTP")
cell.postTitleLabel.text = "ENTP"
cell.authorLabel.text = "Oleksandr Zheliezniak"
cell.authorImageView.image = UIImage(named: "author"):
} else if indexPath.row == 1 {
cell.postImageView.image = UIImage(named: "INTJ")
cell.postTitleLabel.text = "INTJ"
cell.authorLabel.text = "Gabriel Theodoropoulos"
cell.authorImageView.image = UIImage(named: "appcoda-300")
} else if indexPath.row == 2 {
cell.postImageView.image = UIImage(named: "ENTP")
cell.postTitleLabel.text = "ENTJ"
cell.authorLabel.text = "Gabriel Theodoropoulos"
cell.authorImageView.image = UIImage(named: "appcoda-300")
} else {
cell.postImageView.image = UIImage(named: "СеКс")
cell.postTitleLabel.text = "и виски"
cell.authorLabel.text = "Кокс Карибский))"
cell.authorImageView.image = UIImage(named: "appcoda-300")
}
Final file as below:
//
// PersonTableViewController.swift
//
// Created by Oleksandr Z 2017(c).
//
import UIKit
import Foundation
import CoreData
#available(iOS 10.0, *)
class PersonTableViewController: UITableViewController, NSFetchedResultsControllerDelegate
{
#IBAction func addButtonPressed(_ sender: Any)
{
switchScreen()
}
#IBOutlet weak var menuButton:UIBarButtonItem!
let moc: NSManagedObjectContext? = (UIApplication.shared.delegate as? AppDelegate)?.managedObjectContext
var fetchedResultsController: NSFetchedResultsController<NSFetchRequestResult>?
var valueToPass:String!
//override var prefersStatusBarHidden: Bool {return true}
override func viewDidLoad()
{
super.viewDidLoad()
// MARK: - CORE DATA fetching out to the TableView
fetchedResultsController?.delegate = self
fetchedResultsController = NSFetchedResultsController(fetchRequest: IdentitiesFetchRequest(), managedObjectContext: self.moc!, sectionNameKeyPath: nil, cacheName: nil)
do
{
try fetchedResultsController?.performFetch()
}
catch let error as NSError
{
fatalError("Failed to initialize FetchedResultsController: \(error)")
}
//Visualization
if self.revealViewController() != nil
{
self.revealViewController().rearViewRevealWidth = 250
self.revealViewController().frontViewShadowRadius = 10
self.revealViewController().frontViewShadowColor = UIColor.black
self.revealViewController().frontViewShadowOpacity = 1
menuButton.target = self.revealViewController()
menuButton.action = #selector(SWRevealViewController.revealToggle(_:))
self.view.addGestureRecognizer(self.revealViewController().panGestureRecognizer())
}
self.clearsSelectionOnViewWillAppear = true
}
func IdentitiesFetchRequest() -> NSFetchRequest<NSFetchRequestResult>
{
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Identity")
let sortDescriptor = NSSortDescriptor(key: "lastModified", ascending: true)
fetchRequest.predicate = nil
fetchRequest.sortDescriptors = [sortDescriptor]
fetchRequest.fetchBatchSize = 20
fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: moc!, sectionNameKeyPath: nil, cacheName: nil)
fetchedResultsController?.delegate = self
do
{
try fetchedResultsController?.performFetch()
}
catch
{
fatalError("Failed to initialize FetchedResultsController: \(error)")
}
return fetchRequest
}
//MARK: NSFetchedResultsController Delegate Functions
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete { }
switch editingStyle
{
case .delete:
do
{
moc?.delete(fetchedResultsController?.object(at: indexPath) as! Identity)
try moc?.save()
}
catch
{
print(error)
}
case .insert:
break
case .none:
break
}
}
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?)
{
switch type
{
case NSFetchedResultsChangeType.insert:
tableView.insertRows(at: NSArray(object: newIndexPath!) as! [IndexPath], with: UITableViewRowAnimation.fade)
break
case NSFetchedResultsChangeType.delete:
tableView.deleteRows(at: NSArray(object: indexPath!) as! [IndexPath], with: UITableViewRowAnimation.fade)
break
case NSFetchedResultsChangeType.move:
tableView.deleteRows(at: NSArray(object: indexPath!) as! [IndexPath], with: UITableViewRowAnimation.fade)
tableView.insertRows(at: NSArray(object: newIndexPath!) as! [IndexPath], with: UITableViewRowAnimation.fade)
break
case NSFetchedResultsChangeType.update:
tableView.cellForRow(at: indexPath! as IndexPath)
break
//default:
// break
}
}
func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>)
{
tableView.beginUpdates()
}
func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>)
{
tableView.endUpdates()
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
// Return the number of sections.
return fetchedResultsController?.sections?.count ?? 0
}
//Detailed viewCell
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
print("You selected cell #\(indexPath.row)!")
/*
// Get Cell Label
let indexPath = tableView.indexPathForSelectedRow!
let currentCell = tableView.cellForRow(at: indexPath)! as UITableViewCell
valueToPass = currentCell.textLabel?.text
performSegue(withIdentifier: "yourSegueIdentifer", sender: self)
*/
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?)
{
if (segue.identifier == "ShowPerson")
{
//var viewController = segue.destination as! PersonDetailViewController
//viewController.passedValue = valueToPass
}
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// Return the number of rows in the section.
return fetchedResultsController?.sections?[section].numberOfObjects ?? 0
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as!PersonTableViewCell
if let cellContact = fetchedResultsController?.object(at: indexPath) as? Identity
{
cell.textLabel?.text = "\(String(describing: cellContact.pName)), \(String(describing: cellContact.lastModified))"
}
let identityClassName:String = String(describing: Identity.self)
//let typeClassName:String = String(describing: Type.self)
let identity:Identity = NSEntityDescription.insertNewObject(forEntityName: identityClassName, into: DatabaseController.getContext()) as! Identity
//let type:Type = NSEntityDescription.insertNewObject(forEntityName: typeClassName, into: DatabaseController.getContext()) as! Type
let fetchRequest:NSFetchRequest<Identity> = Identity.fetchRequest()
do
{
let searchResults = try DatabaseController.getContext().fetch(fetchRequest)
print("number of results: \(searchResults.count)")
for result in searchResults as [Identity]
{
print("\(result.pName!) is \(result.accuracy)% Robesper. Updeted \(result.lastModified!).")
cell.postImageView.image = UIImage(named: "ENTP")
cell.postTitleLabel.text = identity.pName
cell.authorLabel.text = identity.imType
cell.authorImageView.image = UIImage(named: "author")
}
}
catch { print("Error: \(error)") }
return cell
}
// Override to support rearranging the table view.
override func tableView(_ tableView: UITableView, moveRowAt fromIndexPath: IndexPath, to toIndexPath: IndexPath) {
}
// Override to support conditional rearranging of the table view.
override func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool {
// Return NO if you do not want the item to be re-orderable.
return true
}
func switchScreen()
{
let mainStoryboard = UIStoryboard(name: "Main", bundle: Bundle.main)
let vc : UIViewController = mainStoryboard.instantiateViewController(withIdentifier: "PersonDetail") as UIViewController
self.present(vc, animated: false, completion: nil)
}
}
Tried solution: Can't show the core data in tableview Swift but it didn't help. Tried all similar topics but can't solve the problem.
You seem to have two separate CoreData stacks: one set up by the DatabaseController class, which uses the viewContext from a NSPersistentContainer (introduced in iOS10), and another set up in the AppDelegate, which uses what looks like standard boilerplate from the older project templates. Your FRC uses the latter, whilst the rest of your code uses the former.
Although these seem to use the same model file, I suspect they are using different persistent stores. I think if you change this line:
fetchedResultsController = NSFetchedResultsController(fetchRequest: IdentitiesFetchRequest(), managedObjectContext: self.moc!, sectionNameKeyPath: nil, cacheName: nil)
to:
fetchedResultsController = NSFetchedResultsController(fetchRequest: IdentitiesFetchRequest(), managedObjectContext: DatabaseController.getContext(), sectionNameKeyPath: nil, cacheName: nil)
your FRC will be using the same context as the code that creates objects, so you should see some results. There are other problems with your code, so as Tom Harrington says in comments, you should learn how to use the debugger to iron them out.
var identities : [Identity] = []
override func viewDidLoad()
{
super.viewDidLoad()
tableView.dataSource = self
tableView.delegate = self
I tried a lot. There is no matter which solution to use (extensions,lazy,direct).
You should create two variables fore sure : ONE for all data array; SECOND for single data in array.
That is the way how it works basically:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as!PersonTableViewCell
let identity = identities[indexPath.row]
cell.postImageView.image = UIImage(named: <picture name>)
cell.postTitleLabel.text = identity.<Your attribute of entity>
cell.authorLabel.text = identity.<Your attribute of entity>
cell.authorImageView.image = UIImage(named: <picture name>)
return cell
}
Related
I try to send coreData to another view controller when selecting row from UITableViewthrough didSelectRowAt function.
Moving to another view controller through navigationController?.pushViewController in didSelectRowAt works, but I don't know how to send core data specifically.
This post
almost matches what I'm trying to do,
but I don't know how to pass data if I'm not using the storyboard and programmatically load views using navigationController.
Specifically, I want to access and modify the cordData sent from the first controller from the second controller.
This is my code so far.
First View Controller
First VC contains table view
var items = [Item]() // this is coreData variable
...
override func viewWillAppear(_ animated: Bool) {
items = DBManager.share.fetchItems()
homeTableView.reloadData()
}
extension FirstVC : UITableViewDelegate, UITableViewDataSource {
...
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "homeCell", for: indexPath) as? HomeTableCell else { return UITableViewCell() }
let newItem = items[indexPath.row]
cell.homeCellTitle.text = newItem.title
cell.homeCellPrice.text = newItem.price
cell.homeCellPost?.text = newItem.post
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let selectedItem = self.items[indexPath.row]
guard let vc = self.storyboard?.instantiateViewController(withIdentifier: "SecondVC") as? SecondVC else {return}
self.navigationController?.pushViewController(vc, animated: true)
}
}
Second View Controller
class SecondVC: UIViewController {
var selectedItem: Item?
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func editButtonPressed(_ sender: Any) {
// I don't know this part.
}
}
DBManager
handling coreData
import UIKit
import CoreData
class DBManager{
static let share = DBManager()
// MARK: - Core Data stack
lazy var persistentContainer: NSPersistentContainer = {
let container = NSPersistentContainer(name: "RC_week3")
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
fatalError("Unresolved error \(error), \(error.userInfo)")
}
}) //completion Handler
return container
}()
// MARK: - Core Data Saving support
lazy var context = persistentContainer.viewContext
func saveContext () {
if context.hasChanges {
do{
try context.save()
} catch {
let nserror = error as NSError
fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
}
}
}
func fetchItems() -> [Item]
{
var Items = [Item]()
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: Item.description())
do{
Items = try context.fetch(fetchRequest) as! [Item]
}catch{
print("fetching error")
}
return Items
}
}
NSManagedObject instances are reference types, just hand the instance over after instantiating the controller
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let selectedItem = self.items[indexPath.row]
let vc = self.storyboard?.instantiateViewController(withIdentifier: "SecondVC") as! SecondVC
vc.selectedItem = selectedItem // <-- here pass the data
self.navigationController?.pushViewController(vc, animated: true)
}
Side note: guarding the controller it not necessary as the storyboard cannot be modified at runtime. If the code crashes it reveals a design mistake.
And if you replace NSFetchRequest<NSFetchRequestResult> with NSFetchRequest<Item> you get rid of the type cast.
func fetchItems() -> [Item]
{
let fetchRequest = NSFetchRequest<Item>(entityName: Item.description())
do {
return try context.fetch(fetchRequest)
} catch {
print("fetching error", error)
return []
}
}
I save data from MovieDetailViewController in coreData. I try to access coreData data from another controller. When I save the data to coreData and switch to another controller where I output the result in tableView, it doesn't show me the newly saved names there. When I delete the background and reopen it then it shows me the last saved data.
I need something similar to the tableView method tableView.reloadData().
class MovieDetailViewController: UIViewController {
var coreDataMovieName: [NSManagedObject] = []
var movie: Movie!
var detailsImageView: UIImage!
var movieName = ""
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.title = movie.title
//Detalebshi Photos gamochena
if let posterPath = movie.posterPath {
TMDBClient.downloadPosterImage(path: posterPath) { (data, error) in
guard let data = data else {
return
}
let image = UIImage(data: data)
self.imageView.image = image
self.detailsImageView = image
}
}
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else {
return
}
let managedContext = appDelegate.persistentContainer.viewContext
let fetchRequest = NSFetchRequest<NSManagedObject>(entityName: "Favorite")
do {
coreDataMovieName = try managedContext.fetch(fetchRequest)
} catch let error as NSError {
print("Could not fetch. \(error), \(error.userInfo)")
}
}
func save(name: String) {
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else {
return
}
let managedContext = appDelegate.persistentContainer.viewContext
let entity = NSEntityDescription.entity(forEntityName: "Favorite", in: managedContext)!
let person = NSManagedObject(entity: entity, insertInto: managedContext)
person.setValue(name, forKeyPath: "movieName")
do {
try managedContext.save()
coreDataMovieName.append(person)
print("saved \(person)")
} catch let error as NSError {
print("Could not save. \(error), \(error.userInfo)")
}
}
#IBAction func coreDataFavoritesBtnTapped(_ sender: Any) {
save(name: movieName)
}
}
A controller where I try to access Core Data:
class CoreDataFavoritesViewController: UIViewController {
#IBOutlet weak var tableView: UITableView!
var movie: [NSManagedObject] = []
override func viewDidLoad() {
super.viewDidLoad()
title = "The List"
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")
tableView.dataSource = self
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else {
return
}
let managedContext = appDelegate.persistentContainer.viewContext
let fetchRequest = NSFetchRequest<NSManagedObject>(entityName: "Favorite")
do {
movie = try managedContext.fetch(fetchRequest)
} catch let error as NSError {
print("Could not fetch. \(error), \(error.userInfo)")
}
}
}
// MARK: - UITableViewDataSource
extension CoreDataFavoritesViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return movie.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let person = movie[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
cell.textLabel?.text = person.value(forKeyPath: "movieName") as? String
return cell
}
}
You are appending Data after context save
coreDataMovieName.append(person)
do it before
try managedContext.save()
I am using CoreData to save a tableView to the device. I am relatively new to CoreData, and cannot figure this out. I get the error:
'Cannot convert value of type 'String' to expected argument type 'NSManagedObject''
On the line:
favourites.append(addNewMemory.text!)
//MARK:- Core Data
func save(name: String) {
guard let appDelegate =
UIApplication.shared.delegate as? AppDelegate else {
return
}
// 1
let managedContext =
appDelegate.persistentContainer.viewContext
// 2
let entity =
NSEntityDescription.entity(forEntityName: "Memory",
in: managedContext)!
let person = NSManagedObject(entity: entity,
insertInto: managedContext)
// 3
person.setValue(name, forKeyPath: "name")
// 4
do {
try managedContext.save()
favourites.append(person)
} catch let error as NSError {
print("Could not save. \(error), \(error.userInfo)")
}
}
var favourites: [NSManagedObject] = []
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return favourites.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: UITableViewCell.CellStyle.default, reuseIdentifier: "Cell")
/*cell.imageView?.image = UIImage(named: "applelogo")
cell.imageView?.setRounded()
cell.imageView?.clipsToBounds = true
*/
let favMemory = favourites[indexPath.row]
cell.textLabel?.text = favMemory.value(forKeyPath: "name") as? String
return cell
}
#IBAction func addButtonTapped(_ sender: UIButton) {
insertNewCell()
}
func insertNewCell() {
favourites.append(addNewMemory.text!)
let indexPath = IndexPath(row: favourites.count - 1, section: 0)
tableView.beginUpdates()
tableView.insertRows(at: [indexPath], with: .automatic)
tableView.endUpdates()
addNewMemory.text = ""
view.endEditing(true)
}
I expected the app to save the string, but it does not work. How can I fix this?
You are mixing up the text property of the text field and the Core Data entity. Obviously favourites is declared as [NSManagedObject] so you can't append a string. That's what the error message is telling you.
You have to insert a new record in insertNewCell. The easiest solution is to call save and return a Bool from save to indicate that the insertion was successful.
And you are encouraged to use more contemporary API. If there is no Memory subclass create one
var favourites = [Memory]()
...
func save(name: String) -> Bool {
let appDelegate = UIApplication.shared.delegate as! AppDelegate // force unwrapping is perfectly fine
// 1
let managedContext = appDelegate.persistentContainer.viewContext
// 2
let person = Memory(context: managedContext)
// 3
person.name = name
// 4
do {
try managedContext.save()
favourites.append(person)
return true
} catch let error as NSError {
print("Could not save. \(error), \(error.userInfo)")
return false
}
}
and change insertNewCell to
func insertNewCell() {
guard save(name: addNewMemory.text!) else { return }
let indexPath = IndexPath(row: favourites.count - 1, section: 0)
tableView.insertRows(at: [indexPath], with: .automatic)
addNewMemory.text = ""
view.endEditing(true)
}
beginUpdates/endUpdates is pointless.
Trying to create simple application to display data from Core Data to Table View. The code builds successfully - every time it loads it adds new entry, currently more than 20 - but then it crashes when trying to build the table.
Here I have ViewController:
import UIKit
import CoreData
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var habitTable: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
let habitClassName:String = String(describing: Habits.self)
let habit:Habits = NSEntityDescription.insertNewObject(forEntityName: habitClassName, into: DatabaseController.getContext()) as! Habits
habit.habitsPic = ":("
habit.positive = false
habit.theHabit = "do bad"
DatabaseController.saveContext()
let fetchRequest:NSFetchRequest<Habits> = Habits.fetchRequest()
do{
let searchResults = try DatabaseController.getContext().fetch(fetchRequest)
print("number of results: \(searchResults.count)")
}
catch{
print("Error \(error)")
}
}
//CONSTRUCT TABLE VIEW
func numberOfSectionsInTableView(tableView: UITableView)-> Int {
return fetchedResultsController.sections!.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
guard let sections = fetchedResultsController.sections else {
fatalError("No sections in fetchedResultsController")
}
let sectionInfo = sections[section]
return sectionInfo.numberOfObjects
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath)-> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "positiveCell", for: indexPath as IndexPath)
let habit = fetchedResultsController.object(at: indexPath as IndexPath) as! Habits
cell.textLabel?.text = habit.theHabit
cell.detailTextLabel?.text = habit.habitsPic
return cell
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
//FOR DISPLAYING DATA IN TABLE
var fetchedResultsController: NSFetchedResultsController<NSFetchRequestResult>!
func initializeFetchedResultsController() {
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "Habits")
let departmentSort = NSSortDescriptor(key: "theHabit", ascending: true)
let lastNameSort = NSSortDescriptor(key: "positive", ascending: true)
request.sortDescriptors = [departmentSort, lastNameSort]
let moc = DatabaseController.getContext()
fetchedResultsController = NSFetchedResultsController(fetchRequest: request, managedObjectContext: moc, sectionNameKeyPath: nil, cacheName: nil)
fetchedResultsController.delegate = (self as! NSFetchedResultsControllerDelegate)
do {
try fetchedResultsController.performFetch()
} catch {
fatalError("Failed to initialize FetchedResultsController: \(error)")
}
}
}
And DatabaseController:
import Foundation
import CoreData
class DatabaseController {
private init(){
}
class func getContext() -> NSManagedObjectContext{
return DatabaseController.persistentContainer.viewContext
}
let container = NSPersistentContainer(name: "ada")
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
return container
}()
// MARK: - Core Data Saving support
class func saveContext () {
let context = persistentContainer.viewContext
if context.hasChanges {
do {
try context.save()
} catch {
let nserror = error as NSError
fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
}
}
}
}
It will not build the table view, because the fetchedResultsController returns nil. I assume it is problem with "moc" variable, but what?
EDIT
error:
fatal error: unexpectedly found nil while unwrapping an Optional value
line:
guard let sections = fetchedResultsController.sections else {
You declare your fetched results controller as an implicitly unwrapped optional:
var fetchedResultsController: NSFetchedResultsController<NSFetchRequestResult>!
Its initial value is nil.
You also have a method called initializeFetchedResultsController that assigns a value to this property, but you never call that method. In a comment you said that you're calling it in this code:
guard let sections = fetchedResultsController.sections else {
fatalError("No sections in fetchedResultsController")
}
But that code never mentions the initializeFetchedResultsController method, and so it never calls that method.
You have an optional variable but you never assign it a value. So it never gets a value. So it's nil. This isn't a Core Data question, it's basic Swift. Properties don't get values unless you give them values, and you are not doing that.
I am able add all core data and save it alright, and even print it all to the logs. However when I try to fetch the core data and load it on to the table view, only the first couple cells actually load and everything below the loaded cells is just a blank screen.
Here is my view controller:
class MasterViewController: UITableViewController, NSFetchedResultsControllerDelegate {
var loggedIn = false
var managedObjectContext: NSManagedObjectContext? = nil
var detailViewController: DetailViewController? = nil
#IBOutlet var tableViewLoginButton: UIBarButtonItem!
#IBAction func tableViewLoginButtonAction(sender: AnyObject) {
shouldPerformSegueWithIdentifier("loginSegue", sender: nil)
}
override func shouldPerformSegueWithIdentifier(identifier: String?, sender: AnyObject?) -> Bool {
if let ident = identifier {
if ident == "loginSegue" {
if loggedIn == true {
PFUser.logOutInBackgroundWithBlock({ (error) -> Void in
self.checkForUser()
})
return false
}
}
}
return true
}
func checkForUser() {
let currentUser = PFUser.currentUser()
if (currentUser != nil) {
loggedIn = true
tableViewLoginButton.title = "Logout"
} else {
loggedIn = false
tableViewLoginButton.title = "Login"
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
// Code for Login and Sign Up
checkForUser()
coreDataSetup()
}
func coreDataSetup() {
// Code for fetching json data, saving as core data and loading to table view
let appDel: AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let context : NSManagedObjectContext = appDel.managedObjectContext
let url = NSURL(string: "https://www.kimonolabs.com/api/6tewont8?apikey=CPMj8n9tabsEUqUjKEPiHOmVir5gZjfq")
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithURL(url!) { (data, response, error) -> Void in
if error != nil {
print(error)
} else {
// print(NSString(data: data!, encoding: NSUTF8StringEncoding))
do {
let jsonResult = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers) as! NSDictionary
if jsonResult.count > 0 {
if let results = jsonResult["results"] as? NSDictionary {
if let eventData = results["collection1"] as? NSArray {
let request = NSFetchRequest(entityName: "ForumEvents")
request.returnsObjectsAsFaults = false
do {
let results = try context.executeFetchRequest(request)
if results.count > 0 {
for result in results {
context.deleteObject(result as! NSManagedObject)
do { try context.save() } catch {}
}
}
} catch {}
for event in eventData {
if let eventTitle = event["EventTitle"] as? NSDictionary {
if let eventTitleText = eventTitle["text"] as? String {
if let eventDate = event["EventDate"] as? String {
if let eventTicketLink = event["EventTicketLink"] as? NSDictionary {
if let eventLink = eventTicketLink["href"] as? String {
let newEvent : NSManagedObject = NSEntityDescription.insertNewObjectForEntityForName("ForumEvents", inManagedObjectContext: context)
newEvent.setValue(eventTitleText, forKey: "eventTitle")
newEvent.setValue(eventDate, forKey: "eventDate")
newEvent.setValue(eventLink, forKey: "eventLink")
do { try context.save() } catch {}
}
}
}
}
}
}
}
}
}
self.tableView.reloadData()
} catch {}
}
}
task.resume()
}
override func viewWillAppear(animated: Bool) {
self.clearsSelectionOnViewWillAppear = self.splitViewController!.collapsed
super.viewWillAppear(animated)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Segues
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showDetail" {
if let indexPath = self.tableView.indexPathForSelectedRow {
let object = self.fetchedResultsController.objectAtIndexPath(indexPath)
let controller = (segue.destinationViewController as! UINavigationController).topViewController as! DetailViewController
controller.detailItem = object
controller.navigationItem.leftBarButtonItem = self.splitViewController?.displayModeButtonItem()
controller.navigationItem.leftItemsSupplementBackButton = true
}
}
if segue.identifier == "loginSegue" {
checkForUser()
}
}
// MARK: - Table View
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let sectionInfo = self.fetchedResultsController.sections![section]
return sectionInfo.numberOfObjects
// Tried: return self.fetchedResultsController.fetchedObjects!.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)
self.configureCell(cell, atIndexPath: indexPath)
return cell
}
func configureCell(cell: UITableViewCell, atIndexPath indexPath: NSIndexPath) {
let object = self.fetchedResultsController.objectAtIndexPath(indexPath)
cell.textLabel!.text = object.valueForKey("eventTitle")!.description
}
override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
// Return false if you do not want the specified item to be editable.
return false
}
var fetchedResultsController: NSFetchedResultsController {
if _fetchedResultsController != nil {
return _fetchedResultsController!
}
let fetchRequest = NSFetchRequest()
// Edit the entity name as appropriate.
let entity = NSEntityDescription.entityForName("ForumEvents", inManagedObjectContext: self.managedObjectContext!)
fetchRequest.entity = entity
// Set the batch size to a suitable number.
fetchRequest.fetchBatchSize = 20
// Edit the sort key as appropriate.
let sortDescriptor = NSSortDescriptor(key: "eventTitle", ascending: false)
fetchRequest.sortDescriptors = [sortDescriptor]
// Edit the section name key path and cache name if appropriate.
// nil for section name key path means "no sections".
let aFetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: self.managedObjectContext!, sectionNameKeyPath: nil, cacheName: "Master")
aFetchedResultsController.delegate = self
_fetchedResultsController = aFetchedResultsController
do {
try _fetchedResultsController!.performFetch()
} catch {
// Replace this implementation with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
//print("Unresolved error \(error), \(error.userInfo)")
abort()
}
return _fetchedResultsController!
}
var _fetchedResultsController: NSFetchedResultsController? = nil
func controllerWillChangeContent(controller: NSFetchedResultsController) {
self.tableView.beginUpdates()
}
}
Here is a screenshot of how the loaded cells turned out:
I have tried everything and have no idea what to try next!
Thanks in advance for any help.
Make sure the CoreData is correct. Then try this:
Change
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let sectionInfo = self.fetchedResultsController.sections![section]
return sectionInfo.numberOfObjects
}
into
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.fetchResultController.fetchedObjects!.count
}