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.
Related
I have a coreData NSManagedObject as follows:
public class Records: NSManagedObject {
#NSManaged public var uid: String
#NSManaged public var datetime: Date
}
In addition, I have a helper to retrieve the record by UID:
func getRecordByUid(uid: String) -> Records!{
do {
let fetchRequest : NSFetchRequest<Records> = Records.createFetchRequest()
fetchRequest.predicate = NSPredicate(format: "uid = %#", uid)
let result: [Records] = try container.viewContext.fetch(fetchRequest)
return result.first
} catch {
print(error.localizedDescription)
return nil
}
}
Now, in my view controller I used a core-data object as non-optional (for adding new record or editing existing record purpose) as described below:
class AddRecordViewController: UIViewController {
var container: NSPersistentContainer!
var record: Records!
var currentUid = ""
#IBOutlet weak var dateTextField: PickerBasedTextField!
override func viewDidLoad() {
super.viewDidLoad()
// initialise core data
container = NSPersistentContainer(name: "MyModel")
container.loadPersistentStores { (storeDescription, error) in
self.container.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
if let error = error {
print("Unsolved error \(error.localizedDescription)")
}
}
if let existingRecord = Facade.share.model.getRecordByUid(uid: currentUid) {
record = existingRecord
} else {
record = Records(context: self.container.viewContext)
}
// datePicker
let formatter = DateFormatter()
formatter.dateStyle = .medium
dateTextField.text = formatter.string(from: record.datetime)
...
}
}
The problem is that it cause an error in dateTextField.text = ... line, because it thinks the record is optional, however it isn't a case:
(lldb) po record
▿ Optional<Records>
Fatal error: Unexpectedly found nil while unwrapping an Optional value
What should I do?
I think your code would behave much better if you wrapped the fetch with the create into one method that always returns an object.
Something like
func getOrCreateRecord(uid: String) -> Records{
var record: Records?
do {
let fetchRequest : NSFetchRequest<Records> = Records.createFetchRequest()
fetchRequest.predicate = NSPredicate(format: "uid = %#", uid)
let result: [Records] = try container.viewContext.fetch(fetchRequest)
record = result.first
} catch {
print(error.localizedDescription)
}
return record ?? Records(context: container.viewContext)
}
There still might be an issue with the text field but I still think it makes sense to create a wrapper method for this logic.
I think that dateTextField is probably nil, and the fatal error is related to it. Either that, or Records(context: self.container.viewContext) is a failable initializer that returns a nil object in some cases.
I am trying to retrieve all values excluding null from one attribute from my core data during the start of the viewController. But during the for loop the no always fetches value 0 and doesn't increment ahead. So my results.count is 8, then it displays 0 for 8 times and fetching the same value for the attribute.
func searchMark() -> Int
{
do
{
let mngdCntxt = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "AddedBookmark")
let results = try mngdCntxt.fetch(fetchRequest)
//fetchRequest.returnsObjectsAsFaults = false
for no in 0..<results.count{
if let match = results[no] as? AddedBookmark
{
print(no)
let providerNo = match.value(forKey: "providerNo") as! Int
print("providerNo: \(providerNo)")
return providerNo
}
}
catch{}
return 0
}
The value of providerNo is fetched same through the for loop.
You return too soon, so the loop does not even increment once (and that is also why the loop does not crash when no == results.count):
func searchMark() -> Int {
var output = 0
do {
let mngdCntxt = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "AddedBookmark")
let results = try mngdCntxt.fetch(fetchRequest)
for no in 0...(results.count-1) {
if let match = results[no] as? AddedBookmark {
print(no)
let providerNo = match.value(forKey: "providerNo") as! Int
print("providerNo: \(providerNo)")
output = providerNo
}
}
return output
}
catch{}
return output
}
This function may not be exactly what you expect but it shows you how big the loop should be and when to return
If you are
trying to retrieve all values excluding null from one attribute
that implies that you actually want to return an Int array and your force-unwrapped attribute implies that the attribute is declared as non-optional. According to these two assumptions null is meant to be 0
The logical conclusion is to specify a predicate to return all records whose providerNo != 0, map the items to the providerNo values and return that.
func searchMark() -> [Int]
{
do {
let mngdCntxt = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "AddedBookmark")
fetchRequest.predicate = NSPredicate(format: "providerNo != 0")
let results = try mngdCntxt.fetch(fetchRequest) as! [AddedBookmark]
return results.map{ Int($0.providerNo) }
}
catch {
print(error)
return [Int]()
}
}
in my app while i start my app and i fetch data from coredata. its not giving me data.
but if i not closed my app and save some data in database and without close my app if i fetch data its giving me a data.
and when i close my app. next time again its not giving me data.
Here is my code
self.FetchAllLocalData({ (Available) in
if (Available == "0")
{
for ValueToSave in detailArray!
{
let entity = NSEntityDescription.insertNewObjectForEntityForName("RxOrder", inManagedObjectContext: moc)
print(ValueToSave.id!)
var medicinetype : String = ""
let Id : String = ValueToSave.id!.description
let isRx : String = ValueToSave.isRxMedicine!.description
print(ValueToSave.medicineTypeId)
if (ValueToSave.medicineTypeId != nil)
{
medicinetype = ValueToSave.medicineTypeId!
}
else
{
medicinetype = "0"
}
let medicineName : String = ValueToSave.name!
let orderId : String = ValueToSave.orderId!.description
let price : String = "0"
let quantity : String = ValueToSave.quentity!.description
let strength : String = ValueToSave.strength!
entity.setValue(Id, forKey: "medicineId")
entity.setValue(isRx, forKey: "isRxMedicine")
entity.setValue(medicinetype, forKey: "medicineType")
entity.setValue(medicineName, forKey: "productName")
entity.setValue(self.order_id_RX_Medicine!, forKey: "OrderId")
entity.setValue(price, forKey: "price")
entity.setValue(quantity, forKey: "quantity")
entity.setValue(strength, forKey: "strength")
do{
try moc.save()
}
catch {
fatalError("failed To Save Content\(error)")
}
}
and this one for fetching
func FetchAllLocalData(completion : (Available : String)-> Void) {
let request : NSFetchRequest = NSFetchRequest(entityName: "RxOrder")
do{
//request.predicate = NSPredicate(format: "orderId == \(order_id_RX_Medicine!)")
let fetchedPerson = try moc.executeFetchRequest(request)
DataAvailable = fetchedPerson as! [NSManagedObject]
print(DataAvailable.count)
if (DataAvailable.count > 0)
{
print(DataAvailable[0].valueForKey("orderId"))
for OldData in DataAvailable {
print(DataAvailable.count)
print(order_id_RX_Medicine)
print(OldData.valueForKey("orderId"))
if (OldData.valueForKey("orderId")! as! String == order_id_RX_Medicine)
{
completion(Available: "1")
}
else
{
completion(Available: "0")
}
}
}
else
{
completion(Available: "0")
}
}
catch
{
completion(Available: "0")
fatalError("Something Went Wrong \(error)")
}
}
First, since you are here and there are print statements in your code I am going to assume you are seeing those fire and you are not getting an error when your application runs.
Therefore, it would be helpful to see your Core Data creation code.
It would also be helpful to see how you get the moc into the creation code. Is the creation code in the AppDelegate or somewhere else?
What type of persistent store are you using?
You must try this in your app delegate :
func applicationWillTerminate(application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
AppDelegate.shared.saveContext()
}
This will save your data before app terminates
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?
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)
}
}
}