Unable to save values to Core Data - ios

I am trying to store data to Core-Data in AppDelegate itself and then fetch that data from ViewController. For some reason, it is not getting saved in Core-Data. I have tried searching for this issue on the internet but did not get any specific solution. I have pasted the code below -
AppDelegate.swift
func saveContext () {
let context = persistentContainer.viewContext
print(context)
let entity = NSEntityDescription.entity(forEntityName: "Msg", in: context)
print(entity)
let new = NSManagedObject(entity: entity!, insertInto: context)
print(new)
print(getData.alert_message)
new.setValue(getData.alert_message, forKey: "title")
do {
try context.save()
print("save")
print(new.value(forKey: "title"))
} catch {
let nserror = error as NSError
fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
}
}
Console Output

You can fetch record from the entity by NSFetchRequest
Try with below method, You just need to pass name of the entity as argument of the function.
func getAllRecordFromTableWhere(_ tableName: String) -> [NSManagedObject]? {
let fetchRequest = NSFetchRequest<NSManagedObject>(entityName: tableName)
do {
return try managedContext.fetch(fetchRequest)
} catch let error as NSError {
print("Could not fetch. \(error), \(error.userInfo)")
}
return nil
}
and call function like
if let arrayOfData = DBHelper.getAllRecordFromTableWhere("Msg") {
print(arrayOfData)
}

Hear I have Set My Own Solution, Hope This help You
let contex = ((UIApplication.shared.delegate) as! AppDelegate).persistentContainer.viewContext
func SaveData()
{
let entity = NSEntityDescription.insertNewObject(forEntityName: "Employee", into: contex);
entity.setValue(txtname.text, forKey: "empname") // txtname.text is My TexField name
entity.setValue(txtadd.text, forKey: "empadd");
do
{
try contex.save();
}
catch
{
}
}

I think you are not giving values to all attributes of the entity so check that first.
try something like
new.setValue(msgdata, forKey: "msgdata")
//msgdata is what you want to save on Coredata entity
//try something like this

In my case i was trying to update data saved in core data. but every time it was returning the old data ( not the updated version )
Saved successfully message was being printed in console same as yours
Here are some simple steps that finally solved my problem
fetch data with a NSFetchRequest
save data into a temp variable
delete old data from core data
modify data from temp variable
and finally save context
Here is my update data function
func updateUserData(_ userDetails: UserModel) {
let request = UserDetails.fetchRequest() as NSFetchRequest<UserDetails>
do {
let result = try persistentContainer.viewContext.fetch(request)
if let data = result.first?.userData as Data? {
var loginModel = LoginModel(data: data)
loginModel?.user = userDetails
self.clearUserData() //deleting old data
self.userLoginData = loginModel?.jsonData
}
}catch let error as NSError {
debugPrint(error.localizedDescription)
}
}

Related

Unable to save/retrieve data from core data store in swift

I am trying to make a simple app to store and retrieve User data.
Have a RegisterViewController.swift with fields (name,loginUsername,loginPassword,photo) which Users enter to register.
I have a User.swift class to save this information in an object
Using CoreData, I have setup "Users" entity with the 4 fields as attributes.
I have referred the following links to save and retrieve data to the Users table.
https://medium.com/xcblog/core-data-with-swift-4-for-beginners-1fc067cca707.
https://www.bobthedeveloper.io/blog/beginner-guide-to-core-data-in-swift
However, I do not see any data being saved or retrieved when I debug. Neither do I see any exceptions.
Following is the snippet of code
let name = self.name.text;
let loginUsername = self.loginUsername.text;
let loginPassword = self.loginPassword.text;
let photo = self.photo.image;
print("Setting user data in the User object");
user = User(name: name!, loginUsername: loginUsername!, loginPassword: loginPassword!, photo: photo)!;
print("store data in DB")
//Setup delegate and context
let appDelegate = UIApplication.shared.delegate as! AppDelegate;
let context = appDelegate.persistentContainer.viewContext
let entity = NSEntityDescription.entity(forEntityName: "Users", in: context);
let newUser = NSManagedObject(entity: entity!, insertInto: context)
//Set values for the attributes
newUser.setValue(name, forKey: "name")
newUser.setValue(loginUsername, forKey: "loginUsername")
newUser.setValue(loginPassword, forKey: "loginPassword")
//TODO: store the photo
//Save data
do {
print("Trying to same")
try context.save()
} catch {
print("Failed to save user")
let nserror = error as NSError
fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
}
//Method 1: to get Users after saving
do {
usersSaved = try context.fetch(Users.fetchRequest())
} catch {
print("Failed to fetch users")
let nserror = error as NSError
fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
}
//Method 2: to get Users after saving
var usersSaved: [Users] = []
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "Users");
request.returnsObjectsAsFaults = false
do {
let result = try context.fetch(request)
print("Trying to fetch")
for data in result as! [NSManagedObject] {
print(data.value(forKey: "name") as! String)
}
} catch {
print("Failed")
}
This is my User.swift class
class User {
//MARK: Properties
var name: String;
var loginUsername: String;
var loginPassword: String;
var photo: UIImage? = nil;
//MARK: Initialization
init?(name: String, loginUsername: String, loginPassword: String, photo: UIImage?) {
//Initialization should fail if there is no name or username or password
if (name.isEmpty || loginUsername.isEmpty || loginPassword.isEmpty) {
return nil;
}
//Initialize stored properties
self.name = name;
self.loginUsername = loginUsername;
self.loginPassword = loginPassword;
self.photo = photo;
}
}
I have looked online for answers but was unable to figure it out. Any pointers or suggestions would really help me progress..
I am answering my own question for those who might face this issue. Thank you #Paulw11 for the suggestions. Xcode acts weird and I had to do Project -> Clean multiple times and delete DerivedData folder before the CoreData entities got resolved.
This is what I did
func storeUserData() {
let appDelegate = UIApplication.shared.delegate as! AppDelegate;
let context = appDelegate.persistentContainer.viewContext
//store data
let newUser = Users(context: context);
newUser.name = self.name.text;
newUser.loginUsername = self.loginUsername.text;
newUser.loginPassword = self.loginPassword.text;
appDelegate.saveContext();
//retrieve data
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "Users");
request.returnsObjectsAsFaults = false
do {
print("Fetching from DB")
let result = try context.fetch(request)
//printing data
for data in result as! [NSManagedObject] {
print(data.value(forKey: "name") as! String)
print(data.value(forKey: "loginUsername") as! String)
print(data.value(forKey: "loginPassword") as! String)
}
} catch {
print("Failed to fetch")
let nserror = error as NSError
fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
}
}

swift ios how to check fields in core database

how do i check the fields in my core database and also if i wanted to add an entity how would it be possible? below is my code for saving the data , i wanted to add other data aside from name into my core db table using the key groceryitem.
func saveItemInLocalDB(groceryItem : Item) {
let context = getContext()
//retrieve the entity that we just created
let entity = NSEntityDescription.entity(forEntityName: "GroceryItem", in: context)
let item = NSManagedObject(entity: entity!, insertInto: context)
//set the entity values
item.setValue(groceryItem.name, forKey: "name")
item.setValue(false, forKey: "isSelected")
item.setValue(loggedInUserHouseNumber, forKey: "houseNo")
//save the object
do {
try context.save()
print("ang item:", groceryItem.name)
print("saved!")
} catch let error as NSError {
print("Could not save \(error), \(error.userInfo)")
} catch {
}
}
You can try this
let IsFeildAvailable = entity.attributesByName.keys.contains("keyname")
if IsFeildAvailable
{
print("Key/field is exist")
}else{
print("Key/field does not exist")
}

Swift 3 / iOS 10, Proper Core Data Usage

Before upgrading my apps to Swift 3 and iOS 10, I had no problems with using CoreData as a data store for simple objects. Lightweight Migrations were simple, saving and fetching was simple, ect. But ever since the recent upgrade, I have had nothing but trouble with CoreData.
My question is in two parts. First, does anyone know of any good resources to help me learn how CoreData works behind the scenes so I can get better at debugging it? Apple's docs are extremely limiting, and all the articles I read act like the new CoreData is so simple. I have decent experience with relational databases, so CoreData is adding an uncomfortable layer of abstraction for me.
Second, what is wrong with the following code? Lightweight migrations aren't working as they did before iOS 10, using this code. Objects are saving to CoreData (I can interact with them in app after saving), but then disappear after the app is restarted.
lazy var persistentContainer: NSPersistentContainer = {
let description = NSPersistentStoreDescription()
description.shouldInferMappingModelAutomatically = true
description.shouldMigrateStoreAutomatically = true
let container = NSPersistentContainer(name: "MY_APP")
container.persistentStoreDescriptions = [description]
let description = NSPersistentStoreDescription()
description.shouldInferMappingModelAutomatically = true
description.shouldMigrateStoreAutomatically = true
container.persistentStoreDescriptions = [description]
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
return container
}()
// MARK: - Core Data Saving support
func saveContext () {
let context = persistentContainer.viewContext
if context.hasChanges {
do {
try context.save()
} catch {
let nserror = error as NSError
fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
}
}
}
I'm using a separate file to abstract the storing of my objects:
class Repository
{
func getContext () -> NSManagedObjectContext
{
let appDelegate = UIApplication.shared.delegate as! AppDelegate
return appDelegate.persistentContainer.viewContext
}
func delete<T>(_ a: some T)
{
getContext().delete(a as! NSManagedObject)
}
// ----------- Password Repo ----------
func savePassword(name: String, hint: String, un: String) {
let context = getContext()
//retrieve the entity that we just created
let entity = NSEntityDescription.entity(forEntityName: "Password", in: context)
let transc = NSManagedObject(entity: entity!, insertInto: context)
//set the entity values
transc.setValue(name, forKey: "name")
transc.setValue(hint, forKey: "thing")
transc.setValue(un, forKey: "newThing")
//save the object
do {
try context.save()
} catch let error as NSError {
print("Could not save \(error), \(error.userInfo)")
} catch {
}
}
func updatePassword(pas: Password) -> Password
{
let context = getContext()
// sec.is_new = false
// TODO, add updates
// Try updating the model in the DB
do {
try context.save()
} catch {
print(error)
}
return pas
}
func fetchPasswords() -> [Password]
{
let context = getContext()
//create a fetch request, telling it about the entity
let fetchRequest: NSFetchRequest<Password> = Password.fetchRequest() as! NSFetchRequest<Password>
let sortDescriptor = NSSortDescriptor(key: "name", ascending: true)
fetchRequest.sortDescriptors = [sortDescriptor]
do {
//go get the results
let searchResults = try getContext().fetch(fetchRequest)
return searchResults
} catch {
print("Error with request: \(error)")
}
return []
}
// ----------- End Password Repo ----------
// ----------- Hints Repo ----------
func saveHint (name: String, hint: String) {
let context = getContext()
//retrieve the entity that we just created
let entity = NSEntityDescription.entity(forEntityName: "Hint", in: context)
let transc = NSManagedObject(entity: entity!, insertInto: context)
//set the entity values
transc.setValue(value1, forKey: "some_string")
transc.setValue(value2, forKey: "some_thing")
//save the object
do {
try context.save()
} catch let error as NSError {
print("Could not save \(error), \(error.userInfo)")
} catch {
}
}
func fetchHints() -> [Hint]
{
let context = getContext()
//create a fetch request, telling it about the entity
let fetchRequest: NSFetchRequest<Hint> = Hint.fetchRequest() as! NSFetchRequest<Hint>
let sortDescriptor = NSSortDescriptor(key: "my_key", ascending: true)
fetchRequest.sortDescriptors = [sortDescriptor]
do {
//go get the results
let searchResults = try getContext().fetch(fetchRequest)
return searchResults
} catch {
print("Error with request: \(error)")
}
return []
}
}
Then I call this Repository class like so:
Repository().savePassword(name: nameText.text!, hint: hintSoFarLabel.text!, un: "Hey")
The Repository class is working, until I restart the app...
I'm also trying to migrate to a new version of my Core Data Model which simply adds a non-optional String attribute (with default value), which would be a simple lightweight migration in iOS 9/Swift 2. What am I missing about swift 3 lightweight migrations?
I have no doubt that the problem is me not understanding CoreData in iOS 10 well enough. I've been a software engineer for a while, but I've only been working with iOS and Swift for a few months, so please be gentle. And thank's in advance!
Well, I feel like an idiot. I added a non-optional string and didn't give it a default value. I always gave default values to non-string types, but the way Xcode is set up made it appear that it would just give strings a default value of "" if no other was given.
Adding a default value to the new attribute allowed the migration to work in Swift3/iOS10. Noob mistake, but maybe it will help someone here in the future.

(iOS Swift) Fetching record from Core Data return fatal error or nil

I'm starting to code with Swift for iPhone apps, i'm facing this rather confusing obstacle that for some of you might be trivial. I keep getting error message that the record is either fatal error: unexpectedly found nil while unwrapping an Optional value or nil, but when i check the .sqlite the record is there
Let me walk you through
The name of my .xcdatamodeld is ReviewerApp.xcdatamodeld, the same as my app name ReviewApp
ReviewerApp.xcdatamodeld
My class name is Users and my Entity name is User
Class: Users, Entity: User
My Attributes (username, email, password) are all type: String, with properties: optional
Users.swift
import Foundation
import CoreData
#objc(Users)
class Users: NSManagedObject {
// Insert code here to add functionality to your managed object subclass
#NSManaged var username: String?
#NSManaged var email: String?
#NSManaged var password: String?
}
The saveUser() function in signUpController.swift
func saveUser() {
// create an instance of our managedObjectContext
let moc = DataController().managedObjectContext
// we set up our entity by selecting the entity and context that we're targeting
let entity = NSEntityDescription.insertNewObjectForEntityForName("User", inManagedObjectContext: moc) as! Users
// add our data
entity.setValue(usernameTxtFld.text, forKey: "username")
entity.setValue(emailTxtFld.text, forKey: "email")
entity.setValue(passwordTxtFld.text, forKey: "password")
// we save our entity
do {
try moc.save()
//user.append(entity)
print("saved")
} catch {
fatalError("Failure to save context: \(error)")
}
}
The fetch() function in SignInController.swift
let moc = DataController().managedObjectContext
let userFetch = NSFetchRequest(entityName: "User")
do {
let fetchedUser = try moc.executeFetchRequest(userFetch) as! [Users]
print(fetchedUser.first!.username!)
} catch {
fatalError("Failed to fetch person: \(error)")
}
Everytime i save the user registration process, the record is saved in core data. But everytime i am trying to fetch it:
by: print(fetchedUser.first!.username!) , the message in the console is fatal error: unexpectedly found nil while unwrapping an Optional value
by: print(fetchedUser.first?.username) , the message in the console is nil
Thank you very much for the help, in advance
First I would highly suggest you not to using '!' anywhere in your code as it will crash your app (only for static resources). You can use 'if let XXX = YYY as? [...]' to safely cast whatever you want...
Then you are probably not saving anything in your Database. How is your model? Yours Users class must be adequate to the model you built otherwise it won't work (which is a pain), also in your model you have to give your entity its class (in the 'class' field just below 'parent entity').
Also you don't have to use 'entity.setValue(usernameTxtFld.text, forKey: "username")' as you casted your entity beforehand. You could just 'entity.username =usernameTxtFld.text'.
If I didn't help, try to also add the xcdatamodel please.
I think you missed out on
let entityDescription = NSEntityDescription.entityForName("User", inManagedObjectContext: moc)
fetchRequest.entity = entityDescription
do {
let fetchedUser = try moc.executeFetchRequest(fetchRequest)
print(fetchedUser.first!.username!)
} catch {
let fetchError = error as NSError
print(fetchError)
}
The method returns an array of results if the fetch request is successful. Note that Core Data always returns an array if the fetch request is successful, even if we expect one result or if Core Data didn't find any matching records.
For more information and clarification visit - http://code.tutsplus.com/tutorials/core-data-and-swift-managed-objects-and-fetch-requests--cms-25068
The case is closed. There is some problem with the DataController.swift. It was called to save the data, but when it is called to fetch the data it somehow pointing to different place. So, what i did was
Not use the DataController.swift anymore
Equipped my AppDelegate.swift with // MARK: - Core Data Saving support for saving context, because i didn't set my app with including core data in the beginning
func saveContext () {
if managedObjectContext.hasChanges {
do {
try managedObjectContext.save()
} catch {
// Replace this implementation with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
let nserror = error as NSError
NSLog("Unresolved error \(nserror), \(nserror.userInfo)")
abort()
}
}
}
I changed my saving function to
func saveUser() {
let appDelegate =
UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext
//2
let entity = NSEntityDescription.insertNewObjectForEntityForName("Users", inManagedObjectContext: managedContext) as! Users
//3
entity.setValue(usernameTxtFld.text, forKey: "username")
entity.setValue(emailTxtFld.text, forKey: "email")
entity.setValue(passwordTxtFld.text, forKey: "password")
//4
do {
try managedContext.save()
//5
message = "Signing up is successful"
alertMessage(message)
} catch let error as NSError {
print("Could not save \(error), \(error.userInfo)")
}
}
And changed my fetch function to
func fetch() {
let appDelegate =
UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext
//2
let fetchRequest = NSFetchRequest(entityName: "Users")
let predicate = NSPredicate(format: "email == %# AND password == %#", emailTxtFld.text!, passwordTxtFld.text!)
fetchRequest.predicate = predicate
//3
do {
let results =
try managedContext.executeFetchRequest(fetchRequest)
user = results as! [Users]
let row = user.count
if row > 0 {
message = "You're signed in"
//self.performSegueWithIdentifier("signInIdentifier", sender: self)
}else{
message = "Email & password combination is incorrect!"
}
alertMessage(message)
} catch let error as NSError {
print("Could not fetch \(error), \(error.userInfo)")
}
}

Architecture of an app using Core Data and Parse

I struggle with with building a To do app that will download activities from Parse , save them with Core Data and then they will be showed on Apple Watch. I'd like to ask if this is a more or less proper approach to do it:
In viewWillLoad we check if there is an internet connection:
if TRUE we loop over activities in core data and compare them with those from Parse
if they compare we do nothing and prepare cells using info from Core Data
if they do not compare we add them to Core data and prepare cells
if FALSE we prepare cells with info from Core Data
I'm trying to implement my way, but have a problem. Data fetched from parse shown only at the second launch of app. The do not fetch and show at the same time.
My properties
var medicines : [Medicine] = [Medicine]()
var frc :NSFetchedResultsController!
var context = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext
let fetchRequest = NSFetchRequest(entityName: "Medicine")
My methods to get the data:
// MARK: - Fetching
func fetchFromParse() {
let entity = NSEntityDescription.entityForName("Medicine", inManagedObjectContext: context)
let query = PFQuery(className: "Medicine")
query.findObjectsInBackgroundWithBlock { (objects, error) -> Void in
if error == nil {
for object in objects! {
let medicine = NSManagedObject(entity: entity!, insertIntoManagedObjectContext: self.context)
if let name = object["medicineName"] as? String,
amount = object["amountQuantity"] as? String {
//save to Core Data
medicine.setValue(name, forKey: "name")
medicine.setValue(amount, forKey: "amount")
do {
try self.context.save()
} catch let error as NSError {
print("Could not save \(error), \(error.userInfo)")
}
}
}
}
}
}
func fetchFromCoreData() {
do {
let results = try context.executeFetchRequest(fetchRequest)
medicines = results as! [Medicine]
} catch let error as NSError {
print("Could not fetch \(error), \(error.userInfo)")
}
}
I call them in viewWillAppear:
if Reachability.isConnectedToNetwork() {
//fetching data from Parse
fetchFromParse()
fetchFromCoreData()
} else {
//fetching data from Core data
fetchFromCoreData()
logOutButton.enabled = false
}
A better approach is to set up a background task to fetch the data with Parse and stick new entries in your core data store, and when new items are detected, to refresh the table on the main thread.

Resources