Swift - Core data runtime error - ios

I'm getting this runtime error when trying to create an array of the textLabels in my cells.
My code looks like this:
else {
mySelectedCell.accessoryType = UITableViewCellAccessoryType.Checkmark
mySelectedCell.tintColor = UIColor.blackColor()
if let tx = mySelectedCell.textLabel?.text as Optional?{
var textLabel:String = String()
textLabel = tx!
var tempFriend = Model(entity: en!, insertIntoManagedObjectContext: context)
//Save user to core data
tempFriend.tempUser = textLabel
//Save context
context.save(nil)
//Make list from objects
liste = context.executeFetchRequest(freq, error: nil)!
//Make new list of strings from first list
for var i = 0; i < liste.count; ++i{
var data:NSManagedObject = liste[i] as NSManagedObject
//The next line is where the error appears
showList.append(data.valueForKeyPath("tempUser") as String)
}
//Show list
println(showList)
}
}
}
My error says:
fatal error: unexpectedly found nil while unwrapping an Optional value
(lldb).
I do not understand why something would return "nil" in my code.
Any thoughts would be appreciated.

You're force-unwrapping an optional value that is nil. It's hard to tell which one, since you've omitted the stack trace for the error. Try this:
else {
mySelectedCell.accessoryType = UITableViewCellAccessoryType.Checkmark
mySelectedCell.tintColor = UIColor.blackColor()
if let textLabel = mySelectedCell.textLabel?.text,
let entity = en {
var tempFriend = Model(entity: entity, insertIntoManagedObjectContext: context)
//Save user to core data
tempFriend.tempUser = textLabel
//Save context
context.save(nil)
//Make list from objects
liste = context.executeFetchRequest(freq, error: nil)!
//Make new list of strings from first list
for var i = 0; i < liste.count; ++i{
var data:NSManagedObject = liste[i] as NSManagedObject
//The next line is where the error appears
showList.append(data.valueForKeyPath("tempUser") as String)
}
//Show list
println(showList)
}
}
}

Related

Mapping to Realm Result to NSDictionary Error

I have been using this extension to successfully map my Realm Results to NSDictionary:
extension Object {
func toDictionary() -> NSDictionary {
let properties = self.objectSchema.properties.map { $0.name }
let dictionary = self.dictionaryWithValuesForKeys(properties)
let mutabledic = NSMutableDictionary()
mutabledic.setValuesForKeysWithDictionary(dictionary)
for prop in self.objectSchema.properties as [Property]! {
// find lists
if let nestedObject = self[prop.name] as? Object {
mutabledic.setValue(nestedObject.toDictionary(), forKey: prop.name)
} else if let nestedListObject = self[prop.name] as? ListBase {
var objects = [AnyObject]()
for index in 0..<nestedListObject._rlmArray.count {
let object = nestedListObject._rlmArray[index] as AnyObject
objects.append(object.toDictionary())
}
mutabledic.setObject(objects, forKey: prop.name)
}
}
return mutabledic
}
}
But I am now trying to map :
let allObjectLists = realm.objects(UseItemList.self)
let firstObject = allObjectLists[0].valueForKey("useItems")
let toDict = firstObject?.toDictionary() //error here
How do I fix this, there must be a way to map allObjectLists[0].valueForKey("useItems") to a Dictionary
Here is the Error I get:
2016-11-10 11:45:09.056 CPS Stocker[6187:167500] -[_TtGC10RealmSwift4ListC11CPS_Stocker7UseItem_ toDictionary]: unrecognized selector sent to instance 0x7fa7f3a49650
2016-11-10 11:45:09.253 CPS Stocker[6187:167500] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[_TtGC10RealmSwift4ListC11CPS_Stocker7UseItem_ toDictionary]: unrecognized selector sent to instance 0x7fa7f3a49650'
And here is my UseItemList Object class:
class UseItemList: Object {
dynamic var dateCreated = NSDate()
dynamic var locationUnique = Int()
dynamic var MainActivityReference1 = ""
dynamic var MainActivityReference2 = ""
let useItems = List<UseItem>()
}
Here is how I fixed it :
let firstObjectInList = allObjectLists.first!
let useItemsInFirstObject = firstObjectInList.useItems
for firstUseItem in useItemsInFirstObject {
let dit = firstUseItem.toDictionary()
usedObjectDictionaries.append(dit)
}
You're not accessing an Object there.
That line of code let firstObject = allObjectLists[0].valueForKey("useItems") is pulling out the useItems object, which is a List object. This is why it's reporting there's no method named toDictionary() available for it.
If you're trying to get the first object in useItems to generate a dictionary off that, it should be:
let allObjectLists = realm.objects(UseItemList.self) // Get all 'UseItemList' objects from Realm as a `Results` object
let firstObjectInList = allObjectLists.first! // Get the first UseItemList object from the 'Results' object
let useItemsInFirstObject = firstObjectInList.useItems // Access the 'useItems' List object in the first object
let firstUseItem = useItems.first! // Access the first item from the 'useItems' List object
let toDict = firstItem.toDictionary() // Convert the first item into an array
Obviously you can condense this down into one line of code, but you need to make sure you're accessing all of the elements in the right order or else you won't get to a proper Object at the end. :)

Check if AnyObject is empty or does not have a key or if value for that key is empty

In core data, I have an entity called "CachedRecipes" and in it an attribute called "jsonData".
I have a function to get JSON data from this attribute
func getJSONFromCoreData()->AnyObject {
var jsonDataInCoreData:AnyObject = ""
do
{
let fetchRequest = NSFetchRequest(entityName: "CachedRecipes")
let fetchedResults = try self.sharedContext.executeFetchRequest(fetchRequest)
for (var i=0; i < fetchedResults.count; i++)
{
let single_result = fetchedResults[i]
let out = single_result.valueForKey("jsonData")
print(out)
jsonDataInCoreData = out!
}
}
catch
{
print("error")
}
return jsonDataInCoreData
}
I am using a statement in viewDidLoad of the same UIViewController to get the data like this:
let jsonDataFromCoreData = self.getJSONFromCoreData()
How can I check if jsonDataFromCoreData is empty or it doesn't have any key called jsonData and that key doesn't have any value? I need to print out an error if it happens.
Change your function so it returns an optional
func getJSONFromCoreData()->AnyObject? {
var jsonDataInCoreData:AnyObject?
do
{
let fetchRequest = NSFetchRequest(entityName: "CachedRecipes")
let fetchedResults = try self.sharedContext.executeFetchRequest(fetchRequest)
for (var i=0; i < fetchedResults.count; i++)
{
let single_result = fetchedResults[i]
let out = single_result.valueForKey("jsonData")
print(out)
jsonDataInCoreData = out!
}
}
catch
{
print("error")
}
return jsonDataInCoreData
}
And if it returns nil then it doesn't contain that data. You can just unwrap like this:
if let json = getJSONFromCoreData() {
// Has some data
} else {
// No data
}

Updating CoreData adds a lot of nil values

I am trying to implement custom class to handle core data operations. It works great when creating new values. However when I want to update values I get nil entries in core data. Here is my code so far
/**
Update all records in given entity that matches input records
- parameters:
- entityName: name of entity to fetch
- updateBasedOnKey: name of key which will be used to identify entries that are going to be udpated
- values: NSMutableArray of all elements that are going to be updated
- important: if object with given updateBasedOnKey doesnt exist it will be created
- returns: nothing
*/
func updateRecord(entity: String, updateBasedOnKey: String, values: NSMutableArray){
let entityDescription = NSEntityDescription.entityForName(
entity, inManagedObjectContext: self.managedObjectContext)
let results = getRecords(entity)
for(elements) in values{
var newEntry = NSManagedObject(entity: entityDescription!, insertIntoManagedObjectContext: self.managedObjectContext)
//Determine whether to add new result or update existing
if(results.count > 0){
for result in results{
let entry = result as! NSManagedObject
if let keyValueToCompare = entry.valueForKey(updateBasedOnKey){
if (keyValueToCompare.isEqual(elements.valueForKey(updateBasedOnKey)) ){
//asign newEntry to result if found in entries
newEntry = entry
}
}
}
}
//update entry with new values
for(key, value) in elements as! NSMutableDictionary{
newEntry.setValue(value, forKey: key as! String)
}
//Try to save resulting entry
do {
try newEntry.managedObjectContext?.save()
} catch {
print(error)
}
}
}
/**
Fetch all records of given Entity in Core Data Model
- parameters:
- entityName: name of entity to fetch
- returns: NSArray of all records in given entity
*/
func getRecords(entity:String) -> NSArray{
let entityDescription = NSEntityDescription.entityForName(entity, inManagedObjectContext: self.managedObjectContext)
let fetchRequest = NSFetchRequest()
fetchRequest.entity = entityDescription
var result = NSArray()
do {
result = try self.managedObjectContext.executeFetchRequest(fetchRequest)
} catch {
let fetchError = error as NSError
print(fetchError)
}
return result
}
I think that problem is somewhere in asigning newEntry a NSManagedObject.
Any ideas how to fix this and get rid of nils?
Thanks in advance
EDIT:
this is actual working code created by implementing Wain suggestion
func updateRecord(entity: String, updateBasedOnKey: String, values: NSMutableArray){
let entityDescription = NSEntityDescription.entityForName(
entity, inManagedObjectContext: self.managedObjectContext)
let results = getRecords(entity)
for(elements) in values{
//set to true if value was already found and updated
var newEntry : NSManagedObject?
//Determine whether to add new result or update existing
if(results.count > 0){
for result in results{
let entry = result as! NSManagedObject
if let keyValueToCompare = entry.valueForKey(updateBasedOnKey){
if (keyValueToCompare.isEqual(elements.valueForKey(updateBasedOnKey)) ){
//asign newEntry to result if found in entries
newEntry = entry
}
}
}
}
if newEntry == nil {
newEntry = NSManagedObject(entity: entityDescription!, insertIntoManagedObjectContext: self.managedObjectContext)
}
for(key, value) in elements as! NSMutableDictionary{
newEntry!.setValue(value, forKey: key as! String)
}
}
}
You're right, the problem is that you're creating and inserting a new object each time. Instead you should be passing the object to update or running a fetch request to find it, then updating it.
It looks like your intention is to fetch, and the new entry should just be a reference, not initialised. So:
var newEntry : NSManagedObject?

CoreData add object

I made an app which's using core data. I made a function which saves 1 or 2 values / write data into core data. This is the following method:
func saveName(name: String) {
let myDate:NSDate = NSDate()
let context = self.fetchedResultsController.managedObjectContext
let entity = self.fetchedResultsController.fetchRequest.entity!
let newManagedObject = NSEntityDescription.insertNewObjectForEntityForName(entity.name!, inManagedObjectContext: context) as NSManagedObject
if markCell == true {
newManagedObject.setValue(name, forKey: "markedCell")
markCell = false
}
else {
newManagedObject.setValue(name, forKey: "name")
newManagedObject.setValue(myDate, forKey: "datum")
}
// Save the context.
var error: NSError? = nil
if !context.save(&error) {
abort()
}
}
It occurs a crash in the function cellForRowAtIndexPath if markCell == true. If markCell == false (step into else) all works perfect.
If I run this function:
func saveName(name: String) {
let myDate:NSDate = NSDate()
let context = self.fetchedResultsController.managedObjectContext
let entity = self.fetchedResultsController.fetchRequest.entity!
let newManagedObject = NSEntityDescription.insertNewObjectForEntityForName(entity.name!, inManagedObjectContext: context) as NSManagedObject
newManagedObject.setValue(name, forKey: "markedCell")
markCell = false
newManagedObject.setValue(name, forKey: "name")
newManagedObject.setValue(myDate, forKey: "datum")
// Save the context.
var error: NSError? = nil
if !context.save(&error) {
abort()
}
}
no crash occurs but than I also added a value to markedCell. I only want to add a value into markedCell if the bool is set to true (the user pressed a button -> bool will be set to true and func saveNamewill be called).
Load data from core data (create UITableViewCell):
//Get task
let context = self.fetchedResultsController.managedObjectContext
let object = self.fetchedResultsController.objectAtIndexPath(indexPath) as NSManagedObject
var taskString:NSString
taskString = object.valueForKey("name") as String
cell.textLabel!.text = object.valueForKey("name") as? String
//Set accessory type
var request:NSFetchRequest = NSFetchRequest(entityName: "Person")
request.predicate = NSPredicate(format:"markedCell = %#", taskString)
var results : [NSManagedObject] = context.executeFetchRequest(request, error: nil) as [NSManagedObject]
if (results.count > 0) {
//Element exists
cell.accessoryType = UITableViewCellAccessoryType.DisclosureIndicator
println("Cell is marked")
}
else {
//Doesn't exist
cell.accessoryType = UITableViewCellAccessoryType.None
println("Cell isn't marked")
}
I can bet that the problem comes from the fact that markedCell is declared as optional property in your Core Data model while name or/and datum are not optional.
If this is the case your saving works fine when you enter the else loop because at that point you have:
markedCell == nil //this is allowed in your Core Data model
name != nil
datum != nil
However, when you do not enter into the else loop you have:
markedCell != nil
name == nil
datum == nil
and one of the last two lines is incompatible with your Core Data model. If you want to use your original code you need to ensure that all properties mentioned here are declared as optional.

Saving a Dictionary to Core Data

My app parses podcast RSS feeds. I use 2 entities: Podcasts (to hold podcast-related data) and Episodes (Episodes data like summaries etc). After parsing a feed, I store the list of episodes in an Array called "episodesToDisplay". When a user subscribes to a podcast, I want to save the data held by that array in Core Data. Here is my code which throws an error on the annotated line below:
class Podcasts: UITableViewController {
var currentPodcast: Podcasts!
override func viewDidLoad() {
super.viewDidLoad()
let podcastsEntity = NSEntityDescription.entityForName("Podcasts", inManagedObjectContext: self.managedContext)
let podcastsFetch = NSFetchRequest(entityName: "Podcasts")
var error: NSError?
let result = self.managedContext.executeFetchRequest(podcastsFetch, error: &error) as [Podcasts]?
if let resu = result {
println("res is \(resu.count)")
self.currentPodcast = resu[0] as Podcasts
} else {
println("did not work")
}
}
#IBAction func subscribe(sender: AnyObject) {
for dict: AnyObject in episodesToDisplay {
let episodesEntity = NSEntityDescription.entityForName("Episodes", inManagedObjectContext: self.managedContext)
let episodesToSave = Episodes(entity: episodesEntity!, insertIntoManagedObjectContext: self.managedContext)
var episodes = currentPodcast.episode.mutableCopy() as NSMutableOrderedSet
let btDict = dict as NSDictionary <---------------- Crash
episodesToSave.title = btDict["title"] as String
episodesToSave.summary = btDict["summary"] as String
episodesToSave.link = btDict["link"] as String
episodes.addObject(episodesToSave)
currentPodcast.episode = episodes.copy() as NSOrderedSet
}
// Save
var error:NSError?
if !self.managedContext.save(&error) {
println("could not save \(error)")
}
}
Any ideas please?
The error indicates that your array doesn't contain NSDictionary objects - that is why you get dynamic cast exception when you try and access an element as an NSDictionary.
From your comment it seems that your array actually contains MWFeedItem objects, so all you need to do is change your code to use that object type and then you can access the properties of the MWFeedItem -
#IBAction func subscribe(sender: AnyObject) {
for item: MWFeedItem in episodesToDisplay {
let episodesEntity = NSEntityDescription.entityForName("Episodes", inManagedObjectContext: self.managedContext)
let episodesToSave = Episodes(entity: episodesEntity!, insertIntoManagedObjectContext: self.managedContext)
var episodes = currentPodcast.episode.mutableCopy() as NSMutableOrderedSet
episodesToSave.title = item.title
episodesToSave.summary = item.summary
episodesToSave.link = item.link
episodes.addObject(episodesToSave)
currentPodcast.episode = episodes.copy() as NSOrderedSet
}

Resources