In my swift code below the goal is to save a boolean value into core data. I am getting a compile error stating Thread 1: "Unacceptable type of value for attribute: property = "bool"; desired type = NSNumber; given type = __SwiftValue; value = true.". I don't know what to do next. I also added a photo of my core data attributes. I have 2 classes a base class and a helper class.
pic
class ViewController: UIViewController {
var user : [User]? = nil
CoredataHandler.saveObject(username: "Yahs", password: "213", bool: true)
}
class CoredataHandler : NSManagedObject {
private class func getContext() -> NSManagedObjectContext {
let appDelegate = UIApplication.shared.delegate as! AppDelegate
return appDelegate.persistentContainer.viewContext
}
class func saveObject(username: String,password: String,bool: DarwinBoolean) -> Bool{
let context = getContext()
let entity = NSEntityDescription.entity(forEntityName: "User", in: context)
let managedObject = NSManagedObject(entity: entity!, insertInto: context)
managedObject.setValue(username, forKey: "username")
managedObject.setValue(password, forKey: "password")
managedObject.setValue(bool, forKey: "bool")
do {
try context.save()
return true
} catch{
return false
}
}
}
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.
IN my swift 5 code below the goal would be to fetch the first item from core data entity Info attribute username. I am attempting to d that right now I am getting a error at titleLabel.text = String(user![0]) stating
no exact matches in call to initializer
Somehow I know I will need to attach .username somehow but I dont know what to do next at this point.
class ViewController: UIViewController {
var user : [User]? = nil
var titleLabel = UILabel()
override func viewDidLoad() {
super.viewDidLoad()
user = CoredataHandler.fetchObject()
titleLabel.text = String(user![0])
}
}
class CoredataHandler : NSManagedObject {
private class func getContext() -> NSManagedObjectContext {
let appDelegate = UIApplication.shared.delegate as! AppDelegate
return appDelegate.persistentContainer.viewContext
}
class func saveObject(username: String,password: String,tf: Bool) -> Bool{
let context = getContext()
let entity = NSEntityDescription.entity(forEntityName: "User", in: context)
let managedObject = NSManagedObject(entity: entity!, insertInto: context)
managedObject.setValue(username, forKey: "username")
managedObject.setValue(password, forKey: "password")
managedObject.setValue(tf, forKey: "tf")
do {
try context.save()
return true
} catch{
return false
}
}
class func fetchObject() -> [User]?
{
let context = getContext()
var user : [User]? = nil
do {
user = try context.fetch(User.fetchRequest())
return user
} catch {
return user
}
}
}
My swift code saves 3 names to core data entity "username". I want to use uitextfield delegate to pull a specific string. So when the user enters 2 in the textfield. On the label labelName the name jessica Biel should appear. So the user enters a number into a textfield a string appears on the label. If number 1 is enter the 1st NSManagedObject into the core data entity userName. Link to project https://github.com/redrock34/jessicaBiel
import UIKit
import CoreData
class ViewController: UIViewController,uitextfielddele {
#IBOutlet var labelName : UILabel!
#IBOutlet var enterT : UITextField!
// MARK: Variables declearations
let appDelegate = UIApplication.shared.delegate as! AppDelegate //Singlton instance
var context:NSManagedObjectContext!
// MARK: View Controller life cycle methods
override func viewDidLoad() {
super.viewDidLoad()
openDatabse()
}
// MARK: Methods to Open, Store and Fetch data
func openDatabse()
{
context = appDelegate.persistentContainer.viewContext
let entity = NSEntityDescription.entity(forEntityName: "Users", in: context)
let newUser = NSManagedObject(entity: entity!, insertInto: context)
let newUser2 = NSManagedObject(entity: entity!, insertInto: context)
let newUser3 = NSManagedObject(entity: entity!, insertInto: context)
saveData(UserDBObj: newUser, UserDBObj2: newUser2, UserDBObj3: newUser3)
}
func saveData(UserDBObj:NSManagedObject,UserDBObj2:NSManagedObject,UserDBObj3:NSManagedObject)
{
UserDBObj.setValue("kim kardashian", forKey: "username")
UserDBObj2.setValue("jessica biel", forKey: "username")
UserDBObj3.setValue("Hailey Rienhart", forKey: "username")
print("Storing Data..")
do {
try context.save()
} catch {
print("Storing data Failed")
}
fetchData()
}
func fetchData()
{
print("Fetching Data..")
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "Users")
request.returnsObjectsAsFaults = false
do {
let result = try context.fetch(request)
for data in result as! [NSManagedObject] {
let userName = data.value(forKey: "username") as! String
print("User Name is : "+userName)
}
} catch {
print("Fetching data Failed")
}
}
}
First of all be aware that on each launch of the app the three records are inserted again and again so you'll get a bunch of duplicates.
As the names are apparently not related to any order add an unique identifier like an integer attribute index – or whatever name is reasonable – and then fetch the data with a predicate NSPredicate(format: "index == %ld", Int(enterT.text!) ?? 0).
You have to do that because Core Data saves the objects unordered.
I want to fetch all items from Core Data, but sorted from lastest to oldest. My Entity "Dice" has attribute "timestamp" of type Date.
This is how I save and fetch:
class func saveResults(_ withResults: Int) -> Bool {
let context = getContext()
let entity = NSEntityDescription.entity(forEntityName: "Dice", in: context)
let manageObject = NSManagedObject(entity: entity!, insertInto: context)
let date = Date()
manageObject.setValue(withResults, forKey: "result")
manageObject.setValue(date, forKey: "timestamp")
do {
try context.save()
return true
} catch {
return false
}
}
class func fetchObject() -> [Dice]? {
let context = getContext()
var dices:[Dice]? = nil
do {
dices = try context.fetch(Dice.fetchRequest())
return dices
} catch {
return dices
}
}
Does anyone know how to sort it. All solutions are confusing and showing the sort from old to new.
You need to specify an appropriate sort descriptor.
I recommend to make the function throw and hand over the error.
class func fetchObject() throws -> [Dice] {
let context = getContext()
let fetchRequest : NSFetchRequest<Dice> = Dice.fetchRequest()
fetchRequest.sortDescriptors = [NSSortDescriptor(key: "timestamp", ascending: false)]
return try context.fetch(fetchRequest)
}
Note: The fetch operation returns [Dice]. A type cast is not needed.
You can do like this
class func fetchObject() -> [Dice]? {
var dices:[Dice]? = nil
let context = getContext()
let fetchDice: NSFetchRequest<Dice> = Dice.fetchRequest()
let sortDescriptor = [NSSortDescriptor.init(key: "timestamp", ascending: false)]
fetchDice.sortDescriptors = sortDescriptor
do
{
dices = try context.fetch(fetchDice)
return dices
}catch(let error) {
print(error.localizedDescription)
return dices
}
}
I would like to create a function that would handle any type of object which have overriden init functions. As far I've got working with only overriden init function but if i want to do smth else with these objects every time i have to write extra code with some logic parts. To avoid that and makes code clean i have to make generic functions witch going to have as an argument T.Type.
This is my sample code which shows working parts :
import Foundation
import CoreData
extension Sample {
#nonobjc public class func fetchRequest() -> NSFetchRequest<Sample> {
return NSFetchRequest<Sample>(entityName: "Sample");
}
#NSManaged public var smth: String
#NSManaged public var smth1: Double
convenience init(smth: String, smth1: Double, insertIntoManagedObjectContext _context: NSManagedObjectContext!) {
let _enitity = NSEntityDescription.entity(forEntityName: "Sample", in: _context)
self.init(entity: _entity, insertInto: _context)
self.smth = smth
self.smth1 = smth1
}
}
And then I initialize the object like this :
let _context = DataBaseController.getContext()
let _sample: Sample = Sample(smth: smth, smth1: smth1 insertIntoManagedObjectContext: _context)
DataBaseController.saveContext()
By following exapmle from here : Example
I've implemented these functions :
func addRecord<T: NSManagedObject>(_ type : T.Type) -> T {
let _entityName = T.description()
let _context = DataBaseController.persistentContainer.viewContext
let _entity = NSEntityDescription.entity(forEntityName: _entityName, in: _context)
let _record = T(entity: _entity!, insertInto: _context)
return _record
}
func recordsInDataBase<T: NSManagedObject>(_ type : T.Type) -> Int {
let _records = allRecords(T.self)
return _records.count
}
func allRecords<T: NSManagedObject>(_ type : T.Type, sort: NSSortDescriptor? = nil) -> [T] {
let _context = DataBaseController.persistentContainer.viewContext
let _request = T.fetchRequest()
do {
let _results = try _context.fetch(_request)
return _results as! [T]
} catch {
print("Error : \(error)")
return []
}
}
My question is : How could I invoke my overriden init function from the class with passing also these 2 extra arguments which is smth and smth1?
let _sample = DataBaseController.Instance.addRecord(...)
Thanks in advance!
EDIT :
Is it going to be like this ? :
let _sample = DataBaseController.Instance.addRecord(Sample.self.init(smth: smth, smth1: smth1, insertIntoManagedObjectContext: _context))
You can't do that.
In the example you linked, it is showing how to save and query the database using neat little functions. You can't magically pass extra parameters to the functions and expect them to be accepted and used to create the new "record".
The answerer actually showed you how to use the functions if you scroll down a bit:
let name = "John Appleseed"
let newContact = addRecord(Contact.self)
newContact.contactNo = 1
newContact.contactName = name
That is how you use addRecord. You call addRecord, pass in WhateverType.self and assign the return value to a variable. After that, you set the variable's property directly, not by using the initializer.
I have come up with this (not very elegant) solution:
extension NSManagedObject {
func initialize(with properties: [String: Any]) {
}
}
class Sample : NSManagedObject {
override func initialize(with properties: [String : Any]) {
self.smth = properties["smth"] as! String
self.smth1 = properties["smth1"] as! Double
}
#NSManaged var smth: String
#NSManaged var smth1: Double
convenience init(properties: [String: Any], insertIntoManagedObjectContext _context: NSManagedObjectContext!) {
let _enitity = NSEntityDescription.entity(forEntityName: "Sample", in: _context)
self.init(entity: _enitity!, insertInto: _context)
}
}
Now all NSManagedObject has this initialize(with:) method. So in the addRecord method, you can call that to initialize a new managed object with desired properties:
func addRecord<T: NSManagedObject>(_ type : T.Type, properties: [String: Any]) -> T {
let _entityName = T.description()
let _context = DataBaseController.persistentContainer.viewContext
let _entity = NSEntityDescription.entity(forEntityName: _entityName, in: _context)
let _record = T(entity: _entity!, insertInto: _context)
_record.initialize(with: properties)
return _record
}
Example usage:
addRecord(Sample.self, properties: ["smth": "Hello", "smth1": 1])
The bad thing about this is of course that there is no type safety. Every property you pass to addRecord is of type Any.