i have got a table view which gets data from core data.
this works fine, but if i send the command tbl.reloadData it will chrashes my app.
this is my code for get data and reload it:
#IBOutlet weak var tbl: NSTableView!
func requestData() {
let appdelegate = NSApplication.shared().delegate as! AppDelegate
let context = appdelegate.persistentContainer.viewContext
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "Test")
do {
data = try context.fetch(request) as! [Data]
} catch { }
print(data)
tbl.reloadData()
}
print(data) shows me successfully all my data of core data.
but on the line "tbl.reloadData()" the app will crash with this error:
fatal error: unexpectedly found nil while unwrapping an Optional value
2017-05-03 14:24:47.750618+0200 Programm[36302:1990301] fatal error: unexpectedly found nil while unwrapping an Optional value
Only thing that can be nil after that print statement is tbl object. Make sure:
you have connected the IBOutlet properly.
removed old connections from storyboard. The ones you created previously and now renamed the variable name or deleted the variable entirely.
Also for some reason your code has NSTableView make sure you are looking for that and not the one column style UITableView.
Related
I'm making an app that needs to take a managed object array from core data and export it to a csv file that I plan to attach to an email being sent out using the mfMailComposer system. I have the data properly stored in the core data systems and the mail composer functionality seems to be working. I'm reaching a snag when I try to find the correct process by which to export the data.
I have already taken a long look at both of these posts attempting to determine a solution:
from 2012, seems very outdated:
how to export Core Data to CSV
from 2016, more recent, but swift 3 and Xcode 8 have since been released and I worry this has become outdated as well: How to create a CSV file from Core Data (swift)
I have been attempting to try the solutions proposed in the second link, but much of the code gets marked as incorrect when typing it, so I believe it is now obsolete with the upgrade.
The code below is based off of the second post and therefore, likely outdated, but in order to provide a reference of the process I am trying to accomplish...
// Called by the press of xcode UI button
#IBAction func ExportToCSV(_ sender: AnyObject)
{
// Make our mail composer controller and fill it with the proper information
let mailComposeViewController = configuredMailComposeViewController()
// If the composer is functioning properly ...
if MFMailComposeViewController.canSendMail()
{
// ... Present the generated mail composer controller
self.present(mailComposeViewController, animated: true, completion: nil)
}
else
{
// ... Otherwise, show why it is not working properly
self.showSendMailErrorAlert()
}
}
// Used to set up the body of the outgoing email
func configuredMailComposeViewController() -> MFMailComposeViewController
{
// Establish the controller from scratch
let mailComposerVC = MFMailComposeViewController()
mailComposerVC.mailComposeDelegate = self
// Set preset information included in the email
mailComposerVC.setSubject("Generic email subject")
mailComposerVC.setMessageBody("Generic email body", isHTML: false)
// Turn core data for responses into a .csv file
// Pull core data in
var CoreDataResultsList = [NSManagedObject]()
// Register the proper delegate and managed context
let appDelegate =
UIApplication.shared.delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext
// Pull the data from core data
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "ItemResponses")
do {
let results =
try managedContext!.fetch(fetchRequest)
CoreDataResultsList = results as! [NSManagedObject]
} catch let error as NSError {
print("Could not fetch \(error), \(error.userInfo)")
}
// Take the managed object array and turn it into a .csv sring to write in the file
let csvString = writeCoreObjectsToCSV(objects: CoreDataResultsList, named: "Generic name")
let data = csvString.dataUsingEncoding(NSUTF8StringEncoding)
mailComposerVC.addAttachmentData(data, mimeType: "text/csv", fileName: "GenericFilename.csv")
return mailComposerVC
}
// Takes a managed object and writes it to the .csv file ..?
func writeCoreObjectsToCSV(objects: [NSManagedObject], named: String) -> String
{
// Make sure we have some data to export
guard objects.count > 0 else
{
return ""
}
let firstObject = objects[0]
let attribs = Array(firstObject.entity.attributesByName.keys)
// The attires.reduce function is throwing an error about originally using combine as in the second post, used auto fix, but noteworthy.
//Now gives an error that says "No '+' candidates produce the expected contextual result type NSString"
let csvHeaderString = (attribs.reduce("", {($0 as String) + "," + $1 }) as NSString).substringFromIndex(1) + "\n"
// This function says that substring from index has been renamed as well as a few other lines within it
let csvArray = objects.map({object in
(attribs.map({((object.valueForKey($0) ?? "NIL") as AnyObject).description}).reduce("",combine: {$0 + "," + $1}) as NSString).substringFromIndex(1) + "\n"
})
// Again with the reduce issue
let csvString = csvArray.reduce("", combine: +)
return csvHeaderString + csvString
}
New the bottom of the code I have commented in the multiple errors with the suggested code from the second post and the issues pertaining after I use xCode's auto-fix feature.
I would like to thank you in advance for helping me with this issue. I am merely looking for the most up-to-date way to export core data as a .csv file and send it out. Thanks!
I ended up working around it. At first I didn't understand how .csv files were written until I saw the acronym stood for "comma-separated values". Then it all clicked for me and I wrote my own, I did a more manual route for the head entry but the data on the following lines is still auto mated.
Below is the relevant functions in their new working form:
// Called by the press of xcode UI button
#IBAction func ExportToCSV(_ sender: AnyObject)
{
// Make our mail composer controller and fill it with the proper information
let mailComposeViewController = configuredMailComposeViewController()
// If the composer is functioning properly ...
if MFMailComposeViewController.canSendMail()
{
// ... Present the generated mail composer controller
self.present(mailComposeViewController, animated: true, completion: nil)
}
else
{
// ... Otherwise, show why it is not working properly
self.showSendMailErrorAlert()
}
}
// Used to set up the body of the outgoing email
func configuredMailComposeViewController() -> MFMailComposeViewController
{
// Establish the controller from scratch
let mailComposerVC = MFMailComposeViewController()
mailComposerVC.mailComposeDelegate = self
// Set preset information included in the email
mailComposerVC.setSubject("Generic Subject")
mailComposerVC.setMessageBody("Generic Email Body", isHTML: false)
// Turn core data for responses into a .csv file
// Pull core data in
var CoreDataResultsList = [NSManagedObject]()
// Register the proper delegate and managed context
let appDelegate =
UIApplication.shared.delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext
// Pull the data from core data
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "ItemResponses")
do {
let results =
try managedContext!.fetch(fetchRequest)
CoreDataResultsList = results as! [NSManagedObject]
} catch let error as NSError {
print("Could not fetch \(error), \(error.userInfo)")
}
// Take the managed object array and turn it into a .csv sring to write in the file
// In doing this, we are writing just like we would to any string
let csvString = writeCoreObjectsToCSV(objects: CoreDataResultsList)
let data = csvString.data(using: String.Encoding.utf8.rawValue, allowLossyConversion: false)
mailComposerVC.addAttachmentData(data!, mimeType: "text/csv", fileName: "GenericFilename.csv")
return mailComposerVC
}
// Takes a managed object and writes it to the .csv file ..?
func writeCoreObjectsToCSV(objects: [NSManagedObject]) -> NSMutableString
{
// Make sure we have some data to export
guard objects.count > 0 else
{
return ""
}
var mailString = NSMutableString()
mailString.append("Generic Header 1, Generic Header 2, Generic Header 3")
for object in objects
{
// Put "\n" at the beginning so you don't have an extra row at the end
mailString.append("\n\(object.value(forKey: "Generic Core Data Key 1")!),\(object.value(forKey: "Generic Core Data Key 2")!), \(object.value(forKey: "Generic Core Data Key 3")!)")
}
return mailString
}
I'm having an issue where one of my keys is a string containing commas and need a proper way to escape from them. I've heard double quotes is how to do it but inserting them gave me no success.
Regardless, this is one current way to take core data into an array and write it to a string, save it to a .csv file and mail it out. This answers my question, but my next task is reading the data back in. I have no idea how to access the file to do that on an iPad. If you come across this and know how to do that, please let me know! I will probably be making another post on that topic if I can't find a solution and will then drop a new question and a link to that in the replies below this answer.
Thank you!
I recently received this error when fetching data from Core Data:
warning: could not load any Objective-C class information. This will significantly reduce the quality of type information available.
(lldb)
Here is my code:
// MARK: - Initialize Fetch Request
var fetchedResultsController = NSFetchedResultsController<Profile>()
func setFetchRequest() -> NSFetchRequest<Profile> {
let request = Profile.fetchRequest()
let sortDescriptor = SortDescriptor(key: "title", ascending: false)
do {
try moc?.fetch(request)
} catch {
print("Error With Request: \(error)")
}
request.sortDescriptors = [sortDescriptor]
return setFetchRequest()
}
// MARK: - Retrieve Fetch Request
func getFetchRequest() -> NSFetchedResultsController<Profile> {
fetchedResultsController = NSFetchedResultsController(fetchRequest: setFetchRequest(), managedObjectContext: moc!, sectionNameKeyPath: nil, cacheName: nil)
return fetchedResultsController
}
I crashed with this error where I have "try moc?.fetch(request)":
Thread 1 EXC_BAD_ACCESS (code=2, address=0x16fc07feo)
Are these errors connected or is this a bug in Swift 3 / Xcode 8?
You shouldn't take results from the ManagedObjectContext. If you want to use a NSFetchedResultsController class in your app? You'll need to access their methods. And all of the required or optional methods are comes from the NSFetchedResultsControllerDelegate protocol.
Try this
class YourTableViewController: UITableViewController, NSFetchedResultsControllerDelegate {
var fetchedResultsController:NSFetchedResultsController<Profile>!
}
And then create a custom helper function like this one:
`func frc() {
let request:NSFetchRequest<Profile> = Profile.fetchRequest()
let sorter = SortDescriptor(key: "title", ascending: true)
request.sortDescriptors = [sorter]
self.fetchedResultsController = NSFetchedResultsController(fetchRequest: request, managedObjectContext: self.managedObjectContext, sectionNameKeyPath: nil, cacheName: nil)
// make sure the delegate is set to self
self.fetchedResultsController.delegate = self
do {
try self.fetchedResultsController.performFetch()
} catch {}
}
`
From this point you'll need a trigger to perform operations. So let's the system itself should be doing this when you call the viewDidLoad method or you can create a button instead. For example click the button to begin operations.
override func viewDidLoad() {
super.viewDidLoad()
self.frc()
self.tableView.reloadData()
}
It should be works.
Good luck!
Automatic Subclass Generation
Xcode 8 and Swift 3 comes with a new generation of subclassing called as Automatic Subclass Generation! How to create it? Well! So let's create a new Xcode 8 project, choose a Single View Application and then another window will appears called Choose options for your new project:. Give the name for your new project, make sure language is a Swift and Use Core Data check box is checked and then hit Create.
Go to the YourProjectName.xcdatamodeld file and click it. And then add an entity! So let's say your entity name is a Profile and create their Attributes respectively. It's important to know, because this is an Automatic Subclass Generation. Choose your entity and go to the Data Model Inspector ! Choose a Class Definition for the Codegen You can find a Codegen from here.
After selected the Class Definition, you can see Name text field automatically filled by your entity name like so. Again go to the your entity and click it. Click Command + S for save changes firstly and then click Command + B for rebuild, that's it. Automatic Subclass Generation is successfully created.
Remember
If you want to change your model? For example: If you want to add a new Attribute to your model? It's very easy, select a xcdatamodeld file and click your entity. Click the plus sign under Attributes and add your new Attribute. After your changes is completed? Don't forget to save changes. Again click Command + S and then click Command + B
Create A Managed Object
In the Swift 3 you can create a ManagedObject by using subclass initializer. It's very easy to implementing than ever before
let managedObject = Profile(context: self.managedObjectContext!)
You can see it's very easy! How to save values to the managedObject ? So let's say you have a title attribute of your model. Also title is a String.
managedObject.setValue("Well crafted API? Right?", forKey: "title")
or
managedObject.title = "Well crafted API? Right?"
Save values:
do {
try self.managedObjectContext.save()
print(managedObject)
} catch {}
It's works well on the Swift 3.0 beta and Xcode 8.0 beta.
Update
So, this is what I got working for Xcode 8 beta and Swift 3 beta Core Data
var fetchedResultsControler = NSFetchedResultsController<Profile>()
func frc() {
let request: NSFetchRequest<Profile> = Profile.fetchRequest()
let sortDescriptor = SortDescriptor(key: "title", ascending: true)
request.sortDescriptors = [sortDescriptor]
self.fetchedResultsControler = NSFetchedResultsController(fetchRequest: request, managedObjectContext: self.moc!, sectionNameKeyPath: nil, cacheName: nil)
self.fetchedResultsControler.delegate = self
do {
try self.fetchedResultsControler.performFetch()
} catch {
print("Error Fetching Data: \(error)")
}
}
and in viewDidLoad I have self.frc() at the top of the viewDidLoad.
So, in my Profile+CoreDataProperties.swift I copied a method Apple uses in their Master-Detail example when you create a new project:
extension Profile {
#nonobjc class func fetchRequest() -> NSFetchRequest<Profile> {
return NSFetchRequest<Profile>(entityName: "Profile");
}
#NSManaged var title: String?
#NSManaged var titleImage: Data
}
so that my fetch request is "native to my function." Pretty sure that's not the correct way to say that but it's helping me understand what is going on. The fetch request in Apple's example is green instead of blue. And it took me forever to notice that. I clicked on "Event" in Apple's example, and was conveniently taken to the created subclass, which was demonstrated in the Xcode 8 video at WWDC.
The files for e.g. Event.swift and Event+CoreDataProperties.swift are not exposed like they are in Xcode 7.x.x and earlier. You have to click on the entity in the code and you'll be taken to them. Maybe that was my problem? Anyway, I'm fetching data and images like a champ. Thanks a lot for your help #Mannopson!
i know that this has been asked a couple of times but the usual solution does not seem to work for me. I created a CoreData entity and a subclass for it using <ProjectName>.<SubclassName> syntax as class name. Creating a new object like this:
let object = NSEntityDescription.insertNewObjectForEntityForName("User", inManagedObjectContext: CoreDataManager.sharedInstance.managedObjectContext) as User
println("-->\(object)<--")
object.setValue(12, forKey: "userID")
object.setValue("username", forKey: "username")
the console output:
although the object does not get printed in the console it seems to have been in some way created and setting a value on that object refers to a Core Data Object
when i use it without a subclass it works as expected (setting Class name back to default):
let object = NSEntityDescription.insertNewObjectForEntityForName("User", inManagedObjectContext: CoreDataManager.sharedInstance.managedObjectContext) as NSManagedObject
println("-->\(object)<--")
output:
Here's my subclass declaration:
import Foundation
import CoreData
class User: NSManagedObject {
#NSManaged var userID: NSNumber
#NSManaged var username: String
}
and here's the core data model form:
What's wrong with the code? Or do i miss anything?
For printing out the value of an NSManagedObject, do not use:
println(...)
But rather use:
NSLog(...)
Please find an example of using NSLog and an NSManagedObject below:
/**
* Called when the user clicks on the save button.
*/
#IBAction func saveTapped(sender: AnyObject) {
// Reference to our app delegate
let appDel: AppDelegate = UIApplication.sharedApplication().delegate as AppDelegate
// Reference MOC
let context: NSManagedObjectContext = appDel.managedObjectContext!
let en = NSEntityDescription.entityForName("List", inManagedObjectContext: context)
// Create instance of put data model and initialize
var newItem: List = List(entity: en!, insertIntoManagedObjectContext: context)
// Map our properties
newItem.item = textFieldItemName.text
newItem.quantity = textFieldQt.text
newItem.info = textFieldMoreInfo.text
// Save our context
var error: NSError? = nil;
if (context.hasChanges) {
if (!context.save(&error)) { // save failed
println("Save Failed: \(error!.localizedDescription)")
} else {
println("Save Succeeded")
}
}
NSLog("newItem: %#", newItem)
// Navigate back to root ViewController
self.navigationController?.popToRootViewControllerAnimated(true)
}
Note: I do not know the exact reason (bug, or implementation maybe ...) but it turns out that it does not print out a value when we use println(...) function, instead of that it returns an empty String.
Consequently I recommend to all of you guys to use NSLog(...) function instead of println(...) when you want to print out the value of an NSManagedObject.
If you print the expression CoreDataManager.sharedInstance.managedObjectContext twice, do you get a different pointer each time?
It sounds like the managed object context is getting deallocated right after you use it, which indicates that your CoreDataManager.sharedInstance.managedObjectContext property is returning a new managed object context every time, not the same one, or your sharedInstance property is returning a new instance every time.
A managed object's in-memory state is stored as a weak reference to a managed object context. When the context drops out from underneath you (e.g. it's no longer referenced and so is deallocated), your managed object's storage disappears.
A few good indicators that this is happening:
You haven't saved your managed object context yet but a newly created managed object prints as fault
You get weird errors when you try to set a property
You get weird errors when you try to retrieve a property you just set
Include the following at the top of your .swift file
import CoreData
The reason for not working is it doesn't know which library to reference.
Hope it helps
try creating it with this method :
let entityDescripition = NSEntityDescription.entityForName(“User”, inManagedObjectContext: managedObjectContext)
let user = User(entity: entityDescripition, insertIntoManagedObjectContext: managedObjectContext)
I have a very simple UITableView that loads core data records using a NSFetchedResultsController. I have re-written the Objective-C code to Swift. I seem to have an issue when running the Swift code on an iOS 7 simulator but ok on iOS 8. As far as I know Swift is supposed to be backward compatible for iOS 7.
The error I get when running iOS 7 but not 8 is:
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'NSFetchRequest could not locate an NSEntityDescription for entity name 'Category''
For some reason when executing the NSFetchRequest it doesn't like my core data entity Category on iOS 7 but fine on iOS 8, any ideas?
Relevant code:
class CategoriesListTableViewController : CoreDataTableViewController, AddEditCategoryTableViewControllerDelegate {
let kCategoryEntityID = "Category"
let kCategoryCellID = "Category Cell"
let kCategoryEntityParentAttributeID = "parent"
let kCategoryEntityNameAttributeID = "name"
let kCategoriesCacheID = "Categories"
let kAddCategorySegue = "Add Category"
let kEditCategorySegue = "Edit Category"
var moc = NSManagedObjectContext()
override func viewDidLoad() {
super.viewDidLoad()
let app = UIApplication.sharedApplication().delegate as AppDelegate
moc = app.cdh().context
//debugcode
let mom = moc.persistentStoreCoordinator.managedObjectModel
let entities = mom.entitiesByName
let entityNames = entities.description
println("All loaded entities are: \(entityNames)")
self.setupFetchedResultsController()
}
func setupFetchedResultsController() // attaches an NSFetchRequest to this UITableViewController
{
let request = NSFetchRequest(entityName: kCategoryEntityID)
let sortParent = NSSortDescriptor(key: kCategoryEntityParentAttributeID, ascending: true)
let sortName = NSSortDescriptor(key: kCategoryEntityNameAttributeID, ascending: true)
let sortDescriptors = [sortParent, sortName]
request.sortDescriptors = sortDescriptors
self.fetchedResultsController = NSFetchedResultsController(fetchRequest: request, managedObjectContext: moc, sectionNameKeyPath: kCategoryEntityParentAttributeID, cacheName: kCategoriesCacheID)
}
It seems that you might not be creating your managed object context correctly. The fetch request created with an entity name string is not "complete" until it becomes associated with the actual managed object context. The managed object context in turn associates it with the managed object model which contains the necessary entity descriptions.
In this case this should happen during the initialization of the fetched results controller. Check if the correct managed object context is loaded as expected when you create the FRC.
As your different results appear in iOS7 and 8 respectively, consider deleting the app completely from the simulator first. Also, try to clean the project before building again.
I am using the following IB Action to delete data from my Core Data and TableView:
//Events
#IBAction func btnDelTask_Click(sender: UIButton){
let appDel: foodforteethAppDelegate = UIApplication.sharedApplication().delegate as foodforteethAppDelegate
let context: NSManagedObjectContext = appDel.managedObjectContext
/*
let request = NSFetchRequest(entityName: "Food")
myList = context.executeFetchRequest(request, error: nil)
*/
if let tv = tblTasks {
var bas: NSManagedObject!
for bas: AnyObject in myList
{
context.deleteObject(bas as NSManagedObject)
}
myList.removeAll(keepCapacity: false)
tv.reloadData()
}
}
So when I run this in the simulator and click the button to delete then everything seems fine. The table clears. I can go back and forth through the app to different parts of it and return to the table and everything is still clear.
HOWEVER, if I then restart the simulator (without resetting the content and settings), the rows in the table that were previously deleted, suddenly reappear :/
Why is this happening? I don't think it is actually deleting everything from the Core Data which I've found out by commenting out 'myList.removeAll(keepCapacity:false)' and then it doesn't work even normally and nothing deletes.
Any help?