I'm using a TabBar Controller with 2 tabs - Bills Reminder, Bills Paid.
After setting the status of the records in Bills Reminder, and I went into Bills Paid tab, it isn't showing the bills that have been marked as paid.
I quit the app and relaunch it , then went into Bills Paid tab and it is displaying all the bills that has been paid correctly now.
I'm using viewWillAppear in my Bills Paid View controller.
BillsPaidTableViewController.swift
class BillsPaidTableViewController: UITableViewController, NSFetchedResultsControllerDelegate {
var fetchResultController:NSFetchedResultsController!
override func viewWillAppear(animated: Bool) {
println("Hey I'm loading bills paid")
self.tableView.reloadData()
super.viewWillAppear(false);
self.tableView.tableFooterView = UIView(frame: CGRectZero)
}
/*
comments: viewDidLoad will only load once and will not load after subsequent tapping on the the tab
override func viewDidLoad() {
super.viewDidLoad()
// Uncomment the following line to preserve selection between presentations
self.tableView.tableFooterView = UIView(frame: CGRectZero)
}*/
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 self.fetchedResultsController.sections?.count ?? 0
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete method implementation.
// Return the number of rows in the section.
let sectionInfo = self.fetchedResultsController.sections![section] as! NSFetchedResultsSectionInfo
return sectionInfo.numberOfObjects
}
var fetchedResultsController: NSFetchedResultsController {
if _fetchedResultsController != nil {
return _fetchedResultsController!
}
let fetchRequest = NSFetchRequest()
// Edit the entity name as appropriate.
if let managedObjectContext = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext {
let entity = NSEntityDescription.entityForName("Bills", inManagedObjectContext: managedObjectContext)
fetchRequest.entity = entity
// Set the batch size to a suitable number.
fetchRequest.fetchBatchSize = 20
// Edit the sort key as appropriate.
let predicate = NSPredicate(format: " paid == 1 ")
fetchRequest.predicate = predicate
// 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: managedObjectContext, sectionNameKeyPath: nil, cacheName: nil)
aFetchedResultsController.delegate = self
_fetchedResultsController = aFetchedResultsController
var error: NSError? = nil
if !_fetchedResultsController!.performFetch(&error) {
// 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.
//println("Unresolved error \(error), \(error.userInfo)")
abort()
}
}
return _fetchedResultsController!
}
var _fetchedResultsController: NSFetchedResultsController? = nil
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("BillPaidCell", forIndexPath: indexPath) as! BillPaidTableViewCell
// Configure the cell...
self.configureCell(cell, atIndexPath: indexPath)
return cell
}
func configureCell(cell: BillPaidCell, atIndexPath indexPath: NSIndexPath) {
let object = self.fetchedResultsController.objectAtIndexPath(indexPath) as! NSManagedObject
cell.billName?.text = object.valueForKey("name")!.description
cell.billAmt?.text = convertToDecimal( (object.valueForKey("amount")!.description as NSString).doubleValue , 2, 2)
cell.dateDue?.text = object.valueForKey("date_due")!.description
cell.datePaid?.text = object.valueForKey("date_paid")!.description
}
}
Each time, I click on the Bills Paid tab, it will show the message "Hey I'm loading bills paid", no problem with this part. However, the table isn't loading although I'm using .reloadData().
Where did I go wrong with this part?
You should refetch data when click on paid tab. To do this you can simply add _fetchedResultsController = nil in viewWillAppear function before calling table reloadData.
Related
I currently have a UITableView with 2 sections that uses a NSFetchedResultsController. I am trying to work out how I display different entities in the different sections. I have a FOLDER objects and then also TAG objects. I am wanting to display all of these in each section, i.e. Section 1 all FOLDER, Section 2 all TAGS.
The relationship goes:
FOLDER (one to many)-> MOVIE (many to many)-> TAGS
How do I achieve this? Am I needing 2 separate tableView's or to use a single tableView with 2 different fetch requests? Please help!
EDIT: Fetch and tableView cellForRowAt code.
private let appDelegate = UIApplication.shared.delegate as! AppDelegate
private let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
private var fetchedRC: NSFetchedResultsController<Folder>!
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
refresh()
}
private func refresh() {
do {
let request = Folder.fetchRequest() as NSFetchRequest<Folder>
request.predicate = NSPredicate(format: "name CONTAINS[cd] %#", query)
let sort = NSSortDescriptor(keyPath: \Folder.name, ascending: true)
request.sortDescriptors = [sort]
do {
fetchedRC = NSFetchedResultsController(fetchRequest: request, managedObjectContext: context, sectionNameKeyPath: nil, cacheName: nil)
fetchedRC.delegate = self
try fetchedRC.performFetch()
} catch let error as NSError {
print("Could not fetch. \(error), \(error.userInfo)")
}
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "folderCell", for: indexPath) as! FolderTableViewCell
let folder = fetchedRC.object(at: indexPath)
cell.update(with: folder)
cell.layer.cornerRadius = 8
cell.layer.masksToBounds = true
return cell
}
Use 2 FRC for your 2 sections.
One gets fed by your folder fetchrequest and the other by the tags, all in one tableview. Your tableview delegate methods take care of what you want to access. This is quite easy to handle that way. It only gets more complicated if you have more than just 2 sections.
That way your tableview delegate knows by section == 0 or 1 which FRC to access.
I would like to understand why fetchBatchSize is not working correctly with NSFetchRequest.
On initial fetch, after fault array loaded, the data array are loaded in a loop by batchSize until all data is loaded rather than until just the data required is loaded. using coreData instruments or editing run scheme clearly shows all items data loaded in loops of batchSize number of items until all data loaded instead of only loading data of those rows appearing in tableView.
Expected result:
Expect all items in fault array to be loaded followed by only first batch as data to be loaded.
Actual result:
All items loaded in fault array and then all data loaded in looped batches of batch size prior to any scrolling.
Here is the sample project viewController I have created to demonstrate this:
import UIKit
import CoreData
class BatchTestTableViewController: UITableViewController {
var context: NSManagedObjectContext!
var items: [Item]?
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(UINib(nibName: "BatchTableViewCell", bundle: nil), forCellReuseIdentifier: "BatchTableViewCell")
let appDelegate = UIApplication.shared.delegate as! AppDelegate
self.context = appDelegate.persistentContainer.viewContext
self.initializeItems()
if items != nil {
if items!.count == 0 {
for i in 0..<10000 {
let entityDescription = NSEntityDescription.entity(forEntityName: "Item", in: context)
let item = Item(entity: entityDescription!, insertInto: self.context)
item.objectId = UUID().uuidString
item.itemId = UUID().uuidString
item.itemName = "\(i)"
}
try? context.save()
self.initializeItems()
}
}
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if items != nil {
return items!.count
}
return 0
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "BatchTableViewCell", for: indexPath) as! BatchTableViewCell
let item = self.items![indexPath.row]
cell.itemNameLabel.text = item.itemName
return cell
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 50
}
// MARK: - Helper Methods
func initializeItems() {
let request = NSFetchRequest<Item>(entityName: "Item")
let messageSort = NSSortDescriptor(key: "itemName", ascending: true)
request.fetchBatchSize = 20
request.sortDescriptors = [messageSort]
do {
self.items = try context.fetch(request)
} catch {
print("Could not fetch \(error)")
}
}
}
And here is the read out from CoreData Instruments:
From the above image of coredata instrument it can be seen that all data is loaded in 20 batch size loop until all 10,000 items are loaded prior to any scrolling of the tableView.
fetchBatchSize doesn't limit your data fetching. It enables fetchRequest to fetch data in a batch of 20 in your case. You can use this feature to restrict the working set of data in your application. In combination with fetchLimit, you can create a subrange of an arbitrary result set. You should use fetchLimit together
request.fetchLimit = 20
From Apple doc
When the fetch is executed, the entire request is evaluated and the identities of all matching objects recorded, but only data for objects up to the batchSize will be fetched from the persistent store at a time. The array returned from executing the request is a proxy object that transparently faults batches on demand. (In database terms, this is an in-memory cursor.)
Attached at very bottom of this question is my inventory controller file. My problem is I'm getting duplicate results in all the sections. I narrowed down the reason to
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
My code in that function does not account for how many rows there are in each section. As such I'm just printing out the same duplicate results every section.
The actual question is listed after the images below...
Refer to images below:
I also have the ability to change the index from my settings menu so it can index by numbers, like 0-9. Refer to image below:
That said, I currently load the data from Core Data. Attached is reference image of the entities I use and there relationships.
The Question:
My question is, how can I get the results from coreData to be sorted into the A,B,C type sections or 1,2,3 sections so that navigating the table will be simple.
My hunch is the line that says let inventoryRecords = try moc.executeFetchRequest(inventoryFetchRequest) as? [Inventory] needs a sort descriptor to sort based on how I like, but how I then take the data and put into the correct array structure to split into the sections I need...I have no idea.
globals.swift
import Foundation
import CoreData
//Array of Inventory & Store Core Data Managed Objects
var g_inventoryItems = [Inventory]()
var g_storeList = [Store]()
var g_appSettings = [AppSettings]()
var g_demoMode = false
InventoryController.swift
import UIKit
import CoreData
class InventoryController: UIViewController, UISearchBarDelegate, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var searchBar: UISearchBar!
#IBOutlet weak var inventoryTable: UITableView!
var numberIndex = ["0","1","2","3","4","5","6","7","8","9"]
var letterIndex = ["A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"]
var moc = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext //convinience variable to access managed object context
// Start DEMO Related Code
func createInventoryDummyData(number: Int) -> Inventory{
let tempInventory = NSEntityDescription.insertNewObjectForEntityForName("Inventory", inManagedObjectContext: moc) as! Inventory
tempInventory.name = "Test Item # \(number)"
tempInventory.barcode = "00000000\(number)"
tempInventory.currentCount = 0
tempInventory.id = number
tempInventory.imageLargePath = "http://website.tech//uploads/inventory/7d3fe5bfad38a3545e80c73c1453e380.png"
tempInventory.imageSmallPath = "http://website.tech//uploads/inventory/7d3fe5bfad38a3545e80c73c1453e380.png"
tempInventory.addCount = 0
tempInventory.negativeCount = 0
tempInventory.newCount = 0
tempInventory.store_id = 1 //belongs to same store for now
//Select a random store to belong to 0 through 2 since array starts at 0
let aRandomInt = Int.random(0...2)
tempInventory.setValue(g_storeList[aRandomInt], forKey: "store") //assigns inventory to one of the stores we created.
return tempInventory
}
func createStoreDummyData(number:Int) -> Store{
let tempStore = NSEntityDescription.insertNewObjectForEntityForName("Store", inManagedObjectContext: moc) as! Store
tempStore.address = "100\(number) lane, Miami, FL"
tempStore.email = "store\(number)#centraltire.com"
tempStore.id = number
tempStore.lat = 1.00000007
tempStore.lng = 1.00000008
tempStore.name = "Store #\(number)"
tempStore.phone = "123000000\(number)"
return tempStore
}
// End DEMO Related Code
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
print("InventoryController -> ViewDidLoad -> ... starting inits")
//First check to see if we have entities already. There MUST be entities, even if its DEMO data.
let inventoryFetchRequest = NSFetchRequest(entityName: "Inventory")
let storeFetchRequest = NSFetchRequest(entityName: "Store")
do {
let storeRecords = try moc.executeFetchRequest(storeFetchRequest) as? [Store]
if(storeRecords!.count<=0){
g_demoMode = true
print("No store entities found. Demo mode = True. Creating default store entities...")
var store : Store //define variable as Store type
for index in 1...3 {
store = createStoreDummyData(index)
g_storeList.append(store)
}
}
let inventoryRecords = try moc.executeFetchRequest(inventoryFetchRequest) as? [Inventory]
if(inventoryRecords!.count<=0){
g_demoMode = true
print("No entities found for inventory. Demo mode = True. Creating default entities...")
var entity : Inventory //define variable as Inventory type
for index in 1...20 {
entity = createInventoryDummyData(index)
g_inventoryItems.append(entity)
}
print("finished creating entities")
}
}catch{
fatalError("bad things happened \(error)")
}
print("InventoryController -> viewDidload -> ... finished inits!")
}
override func viewWillAppear(animated: Bool) {
print("view appearing")
//When the view appears its important that the table is updated.
//Look at the selected Store & Use the LIST of Inventory Under it.
inventoryTable.reloadData()//this is important to update correctly for changes that might have been made
}
// 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.
print("inventoryItemControllerPrepareForSegueCalled")
if segue.identifier == "inventoryInfoSegue" {
let vc = segue.destinationViewController as! InventoryItemController
if let cell = sender as? InventoryTableViewCell{
vc.inventoryItem = cell.inventoryItem! //sets the inventory item accordingly, passing its reference along.
}else{
print("sender was something else")
}
}
}
func tableView(tableView: UITableView, sectionForSectionIndexTitle title: String, atIndex index: Int) -> Int {
//This scrolls to correct section based on title of what was pressed.
return letterIndex.indexOf(title)!
}
func sectionIndexTitlesForTableView(tableView: UITableView) -> [String]? {
//Use correct index on the side based on settings desired.
if(g_appSettings[0].indextype=="letter"){
return letterIndex
}else{
return numberIndex
}
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
//TODO: Need to figure out how many rows for ...column A,B,C or 1,2,3 based on indexType using~
//To do this we need to organize the inventory results into a section'ed array.
if(g_appSettings[0].selectedStore != nil){
return (g_appSettings[0].selectedStore?.inventories!.count)! //number of rows is equal to the selected stores inventories count
}else{
return g_inventoryItems.count
}
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("InventoryTableCell", forIndexPath: indexPath) as! InventoryTableViewCell
if(g_appSettings[0].selectedStore != nil){
//Get the current Inventory Item & Set to the cell for reference.
cell.inventoryItem = g_appSettings[0].selectedStore?.inventories?.allObjects[indexPath.row] as! Inventory
}else{
//This only happens for DEMO mode or first time.
cell.inventoryItem = g_inventoryItems[indexPath.row]//create reference to particular inventoryItem this represents.
}
cell.drawCell() //uses passed inventoryItem to draw it's self accordingly.
return cell
}
func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
if(g_appSettings[0].indextype == "letter"){
return letterIndex[section]
}else{
return numberIndex[section]
}
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
if(g_appSettings[0].selectedStore != nil){
if(g_appSettings[0].indextype=="letter"){
return letterIndex.count
}else{
return numberIndex.count
}
}else{
return 1//only one section for DEMO mode.
}
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
//dispatch_async(dispatch_get_main_queue()) {
//[unowned self] in
print("didSelectRowAtIndexPath")//does not recognize first time pressed item for some reason?
let selectedCell = self.tableView(tableView, cellForRowAtIndexPath: indexPath) as? InventoryTableViewCell
self.performSegueWithIdentifier("inventoryInfoSegue", sender: selectedCell)
//}
}
#IBAction func BarcodeScanBarItemAction(sender: UIBarButtonItem) {
print("test of baritem")
}
#IBAction func SetStoreBarItemAction(sender: UIBarButtonItem) {
print("change store interface")
}
func searchBar(searchBar: UISearchBar, textDidChange searchText: String) {
print("text is changing")
}
func searchBarCancelButtonClicked(searchBar: UISearchBar) {
print("ended by cancel")
searchBar.text = ""
searchBar.resignFirstResponder()
}
func searchBarSearchButtonClicked(searchBar: UISearchBar) {
print("ended by search")
searchBar.resignFirstResponder()
}
func searchBarTextDidEndEditing(searchBar: UISearchBar) {
print("ended by end editing")
searchBar.resignFirstResponder()
}
#IBAction func unwindBackToInventory(segue: UIStoryboardSegue) {
print("unwind attempt")
let barcode = (segue.sourceViewController as? ScannerViewController)?.barcode
searchBar.text = barcode!
print("barcode="+barcode!)
inventoryTable.reloadData()//reload the data to be safe.
}
}
//Extention to INT to create random number in range.
extension Int
{
static func random(range: Range<Int> ) -> Int
{
var offset = 0
if range.startIndex < 0 // allow negative ranges
{
offset = abs(range.startIndex)
}
let mini = UInt32(range.startIndex + offset)
let maxi = UInt32(range.endIndex + offset)
return Int(mini + arc4random_uniform(maxi - mini)) - offset
}
}
Update:: **
So I was looking around and found this article (I implemented it).
https://www.andrewcbancroft.com/2015/03/05/displaying-data-with-nsfetchedresultscontroller-and-swift/
I'm really close now to figuring it out. Only problem is I can get it to auto create the sections, but only on another field, like for example store.name, I can't get it to section it into A,B,C sections or 1,2,3.
This is my code for the fetchedResultsController using the methods described in that article.
//Create fetchedResultsController to handle Inventory Core Data Operations
lazy var fetchedResultsController: NSFetchedResultsController = {
let inventoryFetchRequest = NSFetchRequest(entityName: "Inventory")
let primarySortDescriptor = NSSortDescriptor(key: "name", ascending: true)
let secondarySortDescriptor = NSSortDescriptor(key: "barcode", ascending: true)
inventoryFetchRequest.sortDescriptors = [primarySortDescriptor, secondarySortDescriptor]
let frc = NSFetchedResultsController(
fetchRequest: inventoryFetchRequest,
managedObjectContext: self.moc,
sectionNameKeyPath: "store.name",
cacheName: nil)
frc.delegate = self
return frc
}()
Question is what to put for sectionNameKeyPath: now that will make it section it on A B C and I got this !
Found a stackoverflow post very similar to my issue, but need swift answer.
A-Z Index from NSFetchedResultsController with individual section headers within each letter?
Here is another similar article but all objective-c answers.
NSFetchedResultsController with sections created by first letter of a string
Update::
Found another article I think with my exact issue (How to have a A-Z index with a NSFetchedResultsController)
Ok I figured it out, phew was this confusing and took a lot of research.
Okay, so first thing you have to do is create a transient property on the data model. In my case I called it lettersection. To do this in the entity just create a new attribute and call it lettersection and in graph mode if you select it (double click it), you will see option in inspector for 'transient'. This means it won't be saved to the database and is used more for internal reasons.
You then need to manually set up the variable in the extension area of the model definition. Here is how it looks for me.
import Foundation
import CoreData
extension Inventory {
#NSManaged var addCount: NSNumber?
#NSManaged var barcode: String?
#NSManaged var currentCount: NSNumber?
#NSManaged var id: NSNumber?
#NSManaged var imageLargePath: String?
#NSManaged var imageSmallPath: String?
#NSManaged var name: String?
#NSManaged var negativeCount: NSNumber?
#NSManaged var newCount: NSNumber?
#NSManaged var store_id: NSNumber?
#NSManaged var store: Store?
var lettersection: String? {
let characters = name!.characters.map { String($0) }
return characters[0].uppercaseString
}
}
Once you do this, you simply call this new 'lettersection' with the fetchedResultsController like so...
let frc = NSFetchedResultsController(
fetchRequest: inventoryFetchRequest,
managedObjectContext: self.moc,
sectionNameKeyPath: "lettersection",
cacheName: nil)
and everything will work! It sorts by the name of my inventory items, but groups them by the first letters, for a nice A,B,C type list!
"My question is, how can I get the results from coreData to be sorted into the A,B,C type sections or 1,2,3 sections so that navigating the table will be simple."
Using "Store" as your entity and property "name" to be what you want to sort the records by.
override func viewDidLoad() { super.viewDidLoad()
let fetchRequest = NSFetchRequest()
let entity = NSEntityDescription.entityForName("Store", inManagedObjectContext: managedObjectContext)
fetchRequest.entity = entity
let sortDescriptor = NSSortDescriptor(key: "name", ascending: true)
fetchRequest.sortDescriptors = [sortDescriptor]
do {
let foundObjects = try managedObjectContext.executeFetchRequest(fetchRequest)
locations = foundObjects as! [Location]
} catch {
fatalCoreDataError(error) }
}
You are going to use this function to set the number of sections:
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return letterindex.count // if that is how you want to construct sections
}
I learned this from the Ray Wenderlich e-book "iOS Apprentice". From Lesson 3 - MyLocations. Highly recommend this and their e-book book on CoreData.
I Want to display searched core data result in tableviewcontroller. i know how to display core data in tableview but i want specific data to be displayed in tableviewcontroller.
like,when user select specific city from uipickerview, then results from core data displayed in tableviewcontroller according to specific city.
some codes are listed below.
import UIKit
import CoreData
class MenuhospitalTableViewController: UITableViewController {
private var hospitalcoredata: [Hospitalcoredata] = []
var fetchResultController:NSFetchedResultsController!
override func viewDidLoad() {
super.viewDidLoad()
Load menu items from database
if let managedObjectContextt = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContextt {
let fetchRequest = NSFetchRequest(entityName: "Hospitalcoredata")
var e: NSError?
hospitalcoredata = managedObjectContextt.executeFetchRequest(fetchRequest, error: &e) as! [Hospitalcoredata]
if e != nil {
println("Failed to retrieve record: \(e!.localizedDescription)")
}
}
// Make the cell self size
self.tableView.estimatedRowHeight = 66.0
self.tableView.rowHeight = UITableViewAutomaticDimension
self.tableView.layoutIfNeeded()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// Return the number of sections.
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// Return the number of rows in the section.
return hospitalcoredata.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! MenuhospitalTableViewCell
// Configure the cell...
cell.nameLabel.text = hospitalcoredata[indexPath.row].namee
cell.contactnoLabel.text = hospitalcoredata[indexPath.row].contactnoo
// cell.priceLabel.text = "$\(menuItems[indexPath.row].price as! Double)"
return cell
}
/*
// 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 code to find data from city are given below..
let entityDescription =
NSEntityDescription.entityForName("Register",
inManagedObjectContext: managedObjectContext!)
let request = NSFetchRequest()
request.entity = entityDescription
let pred = NSPredicate(format: "(name = %#)", name.text)
request.predicate = pred
var error: NSError?
var objects = managedObjectContext?.executeFetchRequest(request,
error: &error)
if let results = objects {
if results.count > 0 {
let match = results[0] as! NSManagedObject
name.text = match.valueForKey("name") as! String
contactno.text = match.valueForKey("contactno") as! String
altno.text = match.valueForKey("altno") as! String
emailid.text = match.valueForKey("emailid") as! String
textf.text = match.valueForKey("bloodgroup") as! String
textff.text = match.valueForKey("city") as! String
status.text = "Matches found: \(results.count)"
} else {
status.text = "No Match"
}
}
}
I want to mixup this two codes and display core data result accordingly "city" selection.
You should be using a NSFetchedResultsController to populate your table view. Add a dynamic predicate to its fetch request and performFetch before you update your table view.
After lots of try and help of my project guide(prof. chirag pandya),
Answer:
use preparesegue method in first view controller,
give name of push segue,
use segue name,
create predicate in second view controller and use first viewcontroller.
vary useful for searching and sorting
Code:
1)IN (firstviewcontroller)
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if(segue.identifier=="segueTest"){
var svc = segue.destinationViewController as! MenuhospitalTableViewController
svc.toPass = textff.text
}
}
2)IN (secondviewcontroller)
let pred = NSPredicate(format: "(city = %#)", toPass)
fetchRequest.predicate = pred
I am making an app where the user selects a contact from the address book and adds it to a table view.
I implemented this by creating an addressBook object, using a peoplePicker, storing the ABRecordID object in Core Data as an NSNumber, pulling the ABRecordID from Core Data and converting it into an ABRecordRef, then getting getting the kABPersonFirstNameProperty.
When the app first loads, it runs fine. Names are selected and added to the Core Data database, but after 2-5 names are selected repetitively, the app crashes with "EXC_BAD_ACCESS(code=1, address=0x3...). This occurs in the cellForRowAtIndexPath tableView call towards the bottom of the code block.
I cannot figure out why it crashes after working a few times. Sometimes it will work twice, others five times.
Any help is greatly appreciated. Thank you!!
Here is my code:
import UIKit
import CoreData
import AddressBook
import AddressBookUI
class ViewController: UIViewController, UITableViewDataSource, ABPeoplePickerNavigationControllerDelegate {
#IBOutlet weak var tableView: UITableView!
var contacts = [NSManagedObject]()
#IBAction func addContact(sender: AnyObject) {
// nav controller
let picker = ABPeoplePickerNavigationController()
// delegate so we can get the selected contact and info
picker.peoplePickerDelegate = self
// make only some people selectable, others grayed out
//picker.predicateForEnablingPerson = NSPredicate(format: "%K like %#", ABPersonFamilyNameProperty, "Neuburg")
// make sure we can see contact info
picker.predicateForSelectionOfPerson = NSPredicate(value:true)
// all done
self.presentViewController(picker, animated:true, completion:nil)
}
// called when user picks a contact to get name
func peoplePickerNavigationController(peoplePicker: ABPeoplePickerNavigationController!, didSelectPerson contact: ABRecord!) {
// ABRecordRefID object to use and store
let contactABRecordID: Int32 = ABRecordGetRecordID(contact)
// save the contact into Core Data
self.saveContact(contactABRecordID)
// reload data to table
self.tableView.reloadData()
}
func saveContact(contactABRecordID: Int32) {
// get access to managedObjectContext through the app delegate
let appDelegate = UIApplication.sharedApplication().delegate as AppDelegate
let managedContext = appDelegate.managedObjectContext!
// create new managed object and instert into the managed object context
let entity = NSEntityDescription.entityForName("Contact", inManagedObjectContext: managedContext)
let contact = NSManagedObject(entity: entity!, insertIntoManagedObjectContext: managedContext)
let wrapper = NSNumber(int: contactABRecordID)
// set abrecordid attribute for NSMangedObject using KVC
contact.setValue(wrapper, forKey: "abRecordID")
// commit changes to person and save to disk
var error: NSError?
if !managedContext.save(&error) {
println("Could not save \(error), \(error?.userInfo)")
}
// append the new person to contacts database
contacts.append(contact)
}
// fetch data from Core Data
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
// pull up app delegate and grab a ref to managed object context
let appDelegate = UIApplication.sharedApplication().delegate as AppDelegate
let managedContext = appDelegate.managedObjectContext!
// fetch data from the database
let fetchRequest = NSFetchRequest(entityName: "Contact")
// create error var
var error: NSError?
// return an array of managed objects that meets critera from fetch request
let fetchedResults = managedContext.executeFetchRequest(fetchRequest, error: &error) as [NSManagedObject]?
// make sure fetch worked
if let results = fetchedResults {
contacts = results
} else {
println("Could not fetch \(error), \(error!.userInfo)")
}
}
// MARK: - people picker protocol
// address book object
var addressBook : ABAddressBook!
// check to see if app has access to contacts
func determineStatus() -> Bool {
// assign status of access
let status = ABAddressBookGetAuthorizationStatus()
// handle status
switch status {
case .Authorized:
// create address book if authoriezed
return self.createAddressBook()
case .NotDetermined:
// ask for permission if haven't answered
var ok = false
ABAddressBookRequestAccessWithCompletion(nil) {
(granted:Bool, err:CFError!) in
dispatch_async(dispatch_get_main_queue()) {
if granted {
ok = self.createAddressBook()
}
}
}
// proceed if access is granted
if ok == true {
return true
}
// otherwise return false
self.addressBook = nil
return false
// if access is restricted
case .Restricted:
self.addressBook = nil
return false
case .Denied:
// new iOS 8 feature: sane way of getting the user directly to the relevant prefs
// I think the crash-in-background issue is now gone
let alert = UIAlertController(title: "Need Authorization", message: "Wouldn't you like to authorize this app to use your Contacts?", preferredStyle: .Alert)
alert.addAction(UIAlertAction(title: "No", style: .Cancel, handler: nil))
alert.addAction(UIAlertAction(title: "OK", style: .Default, handler: {
_ in
let url = NSURL(string:UIApplicationOpenSettingsURLString)!
UIApplication.sharedApplication().openURL(url)
}))
self.presentViewController(alert, animated:true, completion:nil)
self.addressBook = nil
return false
}
}
// create address book to use if granted access
func createAddressBook() -> Bool {
// return true if created successfully
if self.addressBook != nil {
return true
}
// handle errors
var err : Unmanaged<CFError>? = nil
let addressBook : ABAddressBook? = ABAddressBookCreateWithOptions(nil, &err).takeRetainedValue()
if addressBook == nil {
println(err)
self.addressBook = nil
return false
}
// assign addresss book with error then return
self.addressBook = addressBook
return true
}
// when app opens. make sure we have permission for contacts
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
}
// =========
// MARK: - table view protocol
// table will have as many rows as contacts in array
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// number of contacts in contacts array
return contacts.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
// each individual cell
let cell = tableView.dequeueReusableCellWithIdentifier("Cell") as UITableViewCell
// contact is one of contacts in database
let contact = contacts[indexPath.row]
// get ABRecordID
let wrapper: NSNumber = contact.valueForKey("abRecordID")! as NSNumber
// turn wrapper into Int32 type
let contactABRecordID: Int32 = Int32(wrapper.integerValue)
// ERROR COMES HERE!!!! get person with record ID
let personABRecordRef: ABRecordRef = ABAddressBookGetPersonWithRecordID(addressBook, contactABRecordID).takeRetainedValue() as ABRecordRef
// get name from person with record ref
let firstName: String = ABRecordCopyValue(personABRecordRef, kABPersonFirstNameProperty).takeRetainedValue() as String
// grab name attribute from managed object
cell.textLabel!.text = firstName
// give cell to table
return cell
}
// =========
// MARK: - standard code
override func viewDidLoad() {
super.viewDidLoad()
// determine if we have access to contacts
self.determineStatus()
// bring up a box in front if we don't
NSNotificationCenter.defaultCenter().addObserver(self, selector: "determineStatus", name: UIApplicationWillEnterForegroundNotification, object: nil)
// set the title
title = "My People"
// register UITableViewCell with tableView
tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: "Cell")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// =========
}
SOLVED!
Here is what I did. I needed to use an unmanged abperson ref and unwrap it.
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
// each individual cell
let cell = tableView.dequeueReusableCellWithIdentifier("Cell") as UITableViewCell
// contact is one of contacts in database
let contact = contacts[indexPath.row]
// grab name attribute from managed object
cell.textLabel!.text = getContactInfoFromCoreData(contact: contact)
// give cell to table
return cell
}
// =========
// MARK: - get contact from Core Data
func getContactInfoFromCoreData(#contact: NSManagedObject) -> String {
// get ABRecordID
let wrapper: NSInteger = contact.valueForKey("abRecordID")! as NSInteger
// turn wrapper into Int32 type
let contactABRecordID: ABRecordID = Int32(wrapper)
// get person ref with record ID
let unmanagedPersonABRecordRef: Unmanaged<ABRecordRef>? = ABAddressBookGetPersonWithRecordID(addressBook, contactABRecordID)
// unwrap optional ref to get real ref
if let personRefToUse: ABRecordRef = unmanagedPersonABRecordRef?.takeUnretainedValue() {
// use unwrapped ref to get property
let firstName: String = ABRecordCopyValue(personRefToUse, kABPersonFirstNameProperty).takeRetainedValue() as String
// return to table view
return firstName
}
// not good
return "Something went wrong"
}
// =========
Here is solution implemented in original code. I needed to use an optional and unwrap it.
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
// each individual cell
let cell = tableView.dequeueReusableCellWithIdentifier("Cell") as UITableViewCell
// contact is one of contacts in database
let contact = contacts[indexPath.row]
// grab name attribute from managed object
cell.textLabel!.text = getContactInfoFromCoreData(contact: contact)
// give cell to table
return cell
}
// =========
// MARK: - get contact from Core Data
func getContactInfoFromCoreData(#contact: NSManagedObject) -> String {
// get ABRecordID
let wrapper: NSInteger = contact.valueForKey("abRecordID")! as NSInteger
// turn wrapper into Int32 type
let contactABRecordID: ABRecordID = Int32(wrapper)
// get person ref with record ID
let unmanagedPersonABRecordRef: Unmanaged<ABRecordRef>? = ABAddressBookGetPersonWithRecordID(addressBook, contactABRecordID)
// unwrap optional ref to get real ref
if let personRefToUse: ABRecordRef = unmanagedPersonABRecordRef?.takeUnretainedValue() {
// use unwrapped ref to get property
let firstName: String = ABRecordCopyValue(personRefToUse, kABPersonFirstNameProperty).takeRetainedValue() as String
// return to table view
return firstName
}
// not good
return "Something went wrong"
}
// =========