I am trying to put numbers into core data and then retrieve them back. I can already this with strings and it all works fine but trying to do it with doubles it seems to store them but not retrieve them. Below is my code. I hope someone can help, Thank you in advance if you can.
To retrive...
func getTranscriptions18CW () {
let fetchRequest: NSFetchRequest<TextInputs> = TextInputs.fetchRequest()
do {
//go get the results
let searchResults18CW = try getContext().fetch(fetchRequest)
if indexPageSum == 18 {
for trans in searchResults18CW as [NSManagedObject] {
let result = trans.value(forKey: "cWeight")
if result != nil {
CWeight.text = result! as? String
}
}
}
}catch {
print("Error with request: \(error)")
}
}
then to Save.
func getContext () -> NSManagedObjectContext {
_ = UIApplication.shared.delegate as! AppDelegate
return DataController().managedObjectContext
}
func storeTranscription18CW (pageText: Double, textFileUrlString: String) {
let context = getContext()
//retrieve the entity that we just created
let entity = NSEntityDescription.entity(forEntityName: "TextInputs", in: context)
let transc = NSManagedObject(entity: entity!, insertInto: context)
// set the entity values
if indexPageSum == 18 {
transc.setValue(pageText, forKey: "cWeight")
}
//save the object
do {
try context.save()
print("saved!")
} catch let error as NSError {
print("Could not save \(error), \(error.userInfo)")
} catch {
}
}
then another part to save.
if indexPageSum == 18 {
let CWConvert = Double(CWeight.text!)
storeTranscription18CW(pageText: (CWConvert)!, textFileUrlString: "cWeight")
The expression:
result! as? String
basically means "if result is of type String then return it, otherwise return nil". This works fine for Strings stored in the DB, but assigns nil to CWeight.text for anything else.
You want something like the following:
if let result = trans.value(forKey: "cWeight") {
if let str = result as? String {
CWeight.text = str
}
else {
CWeight.text = "\(result)"
}
}
This will work for the most common data types. The expression "\(result)" uses string interpolation to convert the value stored in result to an appropriate string.
You probably also want to look into generating NSManagedObject subclasses to make this easier to handle.
Related
In my swift code below the code saves an item in core data. The goal is to overwrite that item. I am getting a runtime error at
CoreDataHandler.changeName(user: fetchUser!\[indexNumber\], jessica: "jo")
Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value
I don't know how to wrap in the index number. The goal is it to print judo then jo
import UIKit;import CoreData
class ViewController: UIViewController {
var fetchUser: [UserInfo]? = nil
var indexNumber : Int = 0
override func viewDidLoad() {
super.viewDidLoad()
CoreDataHandler.saveObject2( name: "judo")
getText(textNo: indexNumber)
saveTheItem()
}
#objc func saveTheItem(){
CoreDataHandler.changeName(user: fetchUser![indexNumber], jessica: "jo")
}
func getText(textNo:Int) {
// first check the array bounds
let info = helpText.shareInstance.fetchText()
if info.count > textNo {
if let imageData = info[textNo].name
{
print(imageData)
} else {
// no data
print("data is empty Textss")
}
} else {
// image number is greater than array bounds
print("you are asking out of bounds")
}
}
}
class CoreDataHandler : NSManagedObject {
class func saveObject2( name: String) -> Bool {
let context = getContext()
let entity = NSEntityDescription.entity(forEntityName: "UserInfo", in: context)
let managedObject = NSManagedObject(entity: entity!, insertInto: context)
managedObject.setValue(name, forKey: "name")
do{
try context.save()
return true
}
catch {
return false
}
}
private class func getContext() -> NSManagedObjectContext{
let appD = UIApplication.shared.delegate as! AppDelegate
return appD.persistentContainer.viewContext
}
class func changeName(user: UserInfo,jessica : String) -> Bool
{
let context = getContext()
user.name = jessica
print(jessica)
do{
try context.save()
return true
}
catch{
return false
}
}
}
class helpText: UIViewController{
private class func getContext() -> NSManagedObjectContext {
let appDelegate = UIApplication.shared.delegate as! AppDelegate
return appDelegate.persistentContainer.viewContext
}
static let shareInstance = helpText()
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
func saveName(data: String) {
let imageInstance = UserInfo(context: context)
imageInstance.name = data
do {
try context.save()
} catch {
print(error.localizedDescription)
}
}
func fetchText() -> [UserInfo] {
var fetchingImage = [UserInfo]()
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "UserInfo")
do {
fetchingImage = try context.fetch(fetchRequest) as! [UserInfo]
} catch {
print("Error while fetching the image")
}
return fetchingImage
}
}
No offense but your code is a mess.
And there is a big misunderstanding. Core Data records are unordered, there is no index. To update a record you have to fetch it by a known attribute, in your example by name, update it and save it back.
This is a simple method to do that. It searches for a record with the given name. If there is one, update the attribute with newName and save the record.
The code assumes that there is a NSManagedObject subclass UserInfo with implemented class method fetchRequest.
func changeName(_ name: String, to newName: String) {
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let request : NSFetchRequest<UserInfo> = UserInfo.fetchRequest()
request.predicate = NSPredicate(format: "name == %#", name)
do {
let records = try context.fetch(request)
guard let foundRecord = records.first else { return }
foundRecord.name = newName
try context.save()
} catch {
print(error)
}
}
Regarding your confusing code:
Create CoreDataHandler as singleton (and it must not be a subclass of NSManagedObject). Move the Core Data related code from AppDelegate and the methods to read and write in this class.
I wonder how to get an Dictionary from my Firestore. For normal Arrays I've done it like that:
func returnArray(){
let newpath = Firestore.firestore().collection(path)
newpath.addSnapshotListener { (querySnapshot, error) in
guard let documents = querySnapshot?.documents else {
return
}
self.Array = documents.map { (queryDocumentSnapshot) -> String in
let data = queryDocumentSnapshot.data()
let Name = data["myField"] as? String ?? ""
return Name
}
}
}
That works perfectly fine. My question is now how I have to change my code that its getting an Array out of the Firebase. I had an idea, but it doesn't work:
func returnDictionary(){
let newpath = Firestore.firestore().collection(path)
newpath.addSnapshotListener { (querySnapshot, error) in
guard let documents = querySnapshot?.documents else {
return
}
self.Dictionary = documents.map { (queryDocumentSnapshot) -> // I don't know what to fill here
in
let data = queryDocumentSnapshot.data()
let Name = data["AnzeigeName"] as? String ?? ""
let ID = data["selfID"] as? String ?? ""
return ID: Name
}
}
}
What can I try next?
.map is going to give you an array, but you can use Dictionary's init(uniqueKeysWithValues:) to turn this into a Dictionary:
let dictionary : Dictionary<String,String> = .init(uniqueKeysWithValues: documents.compactMap { queryDocumentSnapshot -> (String,String)? in
let data = queryDocumentSnapshot.data()
if let name = data["AnzeigeName"] as? String, let id = data["selfID"] as? String {
return (id, name)
}
return nil
})
It's important to know that uniqueKeysWithValues will crash if the keys are not in fact unique, so you'd want to check for that first (look at #New Dev's comment about init(_:uniquingKeysWith:) to handle this). I'm also using compactMap to get rid of nil values.
(Note: In Swift, generally variable names are lowercased and type names are uppercased. Going against that pattern can make your code challenging to read for others)
Firestore Example
{
name: "Anonymous"
pet {
name: "Max"
age: "3"
}
}
Query
Firebase.firestore().collection(path).getDocuments { querySnapshot, error in
if let error = error {
print(error.localizedDescription)
} else {
for document in querySnapshot!.documents {
let data = document.data()
let name = data["name"]
let pet = data["pet"] as! Dictionary<String, String>
print("Name: \(name)")
print("Pet-name: \(pet["name"]!)")
print("Pet-age: \(pet["age"]!)")
}
I have a single entity Favourites and it contains values such as id, name but the problem is that it is creating multiple copies of items and I wanted it to only store values if it is unique. How can I do this with Core Data?
Here is my code:
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let newFav = Favourites(context: context)
if let id = self.itemsViewModel.items?.results?[indexPath.item].id {
newFav.id = id
}
if let name = self.itemsViewModel.items?.results?[indexPath.item].name {
newFav.name = name
}
self.saveItem()
I do this in an alert and outside of that closure is my saveItem() function everything works but I am trying to save only one item with one id. I am thinking of checking if that id exists but I am not quite sure how would I do that with CoreData
func saveItem() {
do {
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
try context.save()
} catch {
print("Error saving context \(error)")
}
}
Perform a fetch with a distinct predicate and create a new record if the result is zero, for example
func saveItem(with id : Int32, name : String) {
do {
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let request : NSFetchRequest<Favourites> = Favourites.fetchRequest()
request.predicate = NSPredicate(format: "id == %d AND name == %#", id, name)
let numberOfRecords = try context.count(for: request)
if numberOfRecords == 0 {
let newFav = Favourites(context: context)
newFav.name = name
newFav.id = id
try context.save()
}
} catch {
print("Error saving context \(error)")
}
}
I have an array with a SQLite with about 2000 records and all are listed on a tableview. When one of the records are selected, it goes to the "speciesDetailViewController" were it displays details of that item, including the common name of that species.
Currently, all displayed fields are not editable.
I am now adding the ability for the user to to change one of the fields, their common name and the ability to add notes per species.
The minor change is saved in CoreData as I have no experience with SQLite (hired someone).
I am fairly certain the data is being stored as I have print commands showing so.
My issue seems to be retrieving the data.
Note that as editing this field is optional, not every species will have a record in coreData, only the species that the user updated their common name.
class SpeciesDetailData: NSManagedObject
{
#NSManaged var speciesName: String
#NSManaged var commonName: String
#NSManaged var commonForeignName: String
#NSManaged var speciesNote: String
}
.
var speciesDetailData : SpeciesDetailData?
var speciesDataObject: [NSManagedObject] = []
var speciesNameToSave = String()
#IBAction func ckSaveCommonNameButton(_ sender: Any) {
speciesNameToSave = speciesLabel.text!
self.saveSpeciesName(speciesName: speciesNameToSave)
let commonNameToSave = ckCommonNameTextField.text
self.saveCommonName(commonName: commonNameToSave!)
}
func saveCommonName (commonName: String) {
guard let appDelegate =
UIApplication.shared.delegate as? AppDelegate else {
return
}
let managedContext = appDelegate.persistentContainer.viewContext
let entity = NSEntityDescription.entity(forEntityName: "SpeciesDetailData", in: managedContext)!
let saveEntity = NSManagedObject(entity: entity, insertInto: managedContext)
saveEntity.setValue(commonName, forKey: "commonName")
saveSpeciesName(speciesName: speciesNameToSave)
do {
try managedContext.save()
speciesDataObject.append(saveEntity)
print(commonName)
} catch let error as NSError {
print("Could not save. \(error), \(error.userInfo)")
}
}
func saveSpeciesName (speciesName: String) {
guard let appDelegate =
UIApplication.shared.delegate as? AppDelegate else {
return
}
let managedContext = appDelegate.persistentContainer.viewContext
let entity = NSEntityDescription.entity(forEntityName: "SpeciesDetailData", in: managedContext)!
let saveEntity = NSManagedObject(entity: entity, insertInto: managedContext)
saveEntity.setValue(speciesName, forKey: "speciesName")
do {
try managedContext.save()
speciesDataObject.append(saveEntity)
print(speciesName)
} catch let error as NSError {
print("Could not save. \(error), \(error.userInfo)")
}
}
Here is the retrieving function
func retrieveCoreDataSpecies () {
let context = (UIApplication.shared.delegate
as! AppDelegate).persistentContainer.viewContext
let entity = NSEntityDescription.entity(
forEntityName: "SpeciesDetailData", in: context)
let request: NSFetchRequest<SpeciesDetailData> = SpeciesDetailData.fetchRequest()
request.entity = entity
let pred = NSPredicate(format: "speciesName = %#", specieDetail.specie)
request.predicate = pred
do {
let results = try context.fetch(request as!
NSFetchRequest<NSFetchRequestResult>)
if (results.count > 0) {
let match = results[0] as! NSManagedObject
if speciesDetailData?.commonName != nil {
ckCommonNameTextField.text = match.value(forKey: "commonName") as? String
} else {
}
if ckNotesTextView.text == "" || ckNotesTextView.text == nil {
} else {
ckNotesTextView.text = match.value(forKey: "speciesNote") as! String
}
}
} catch let error {
print("Count not fetch \(error), \(error.localizedDescription)")
}
}
When it gets to the
if speciesDetailData?.commonName != nil
it thinks the record is empty and skips over the needed lines.
Any help is appreciated
You are creating separate objects in the two save functions. In each case you are setting only one of the properties, so after you call saveSpeciesName you will have created an object with species name set, and after you call saveCommonName you will have created a different object with the common name set. You need to set both the species name and the common name on the same object.
I am trying to check the id of a record before I put it into the array, using xcode swift
here is the code. But, i get the following error
Notifications.swift:50:46: Cannot convert value of type 'String?!' to expected argument type 'Notifications'
on this line
*if (readRecordCoreData(result["MessageID"])==false)*
Please can some one help to explain this error
import CoreData
struct Notifications{
var NotifyID = [NSManagedObject]()
let MessageDesc: String
let Messageid: String
init(MessageDesc: String, Messageid:String) {
self.MessageDesc = MessageDesc
self.Messageid = Messageid
// self.MessageDate = MessageDate
}
static func MessagesWithJSON(results: NSArray) -> [Notifications] {
// Create an empty array of Albums to append to from this list
var Notification = [Notifications]()
// Store the results in our table data array
if results.count>0 {
for result in results {
//get fields from json
let Messageid = result["MessageID"] as! String
let MessageDesc = result["MessageDesc"] as? String
let newMessages = Notifications(MessageDesc: MessageDesc!, Messageid:Messageid)
//check with id's from core data
if (readRecordCoreData(result["MessageID"])==false)
{
Notification.append(newMessages)
}
}
}
return Notification
}
//check id
func readRecordCoreData(Jsonid: String) -> Bool {
var idStaus = false
let appDelegate =
UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext
//2
let fetchRequest = NSFetchRequest(entityName: "ItemLog")
//3
do {
let resultsCD = try! managedContext.executeFetchRequest(fetchRequest)
if (resultsCD.count > 0) {
for i in 0 ..< resultsCD.count {
let match = resultsCD[i] as! NSManagedObject
let id = match.valueForKey("notificationID") as! String
if (Jsonid as String! == id)
{
idStaus = true
}
else{
idStaus = false
}
}
}
} catch let error as NSError {
print("Could not fetch \(error), \(error.userInfo)")
}
return idStaus
}
One of your methods is static and the other one is not :
func readRecordCoreData(Jsonid: String) -> Bool
static func MessagesWithJSON(results: NSArray) -> [Notifications]
Depending on what you want to accomplish you could declare both static, none, or replace
//check with id's from core data
if (readRecordCoreData(result["MessageID"])==false)
{
Notification.append(newMessages)
}
By
//check with id's from core data
if (Notifications.readRecordCoreData(Messageid)==false)
{
Notification.append(newMessages)
}
Not sure if the code will work past compilation however as there are many readability issues