adding objects in array creates duplicate values in swift - ios

I have an array of custom objects but when I add items to array it creates duplicate of last item add in array.
Below is my code, please suggest where is the mistake, this small thing not able to get it.
var tempArr:[AnimalViewModel] = [AnimalViewModel]()
do {
var objAnimal = Animal()
var result = try managedContext.fetch(fetchRequest)
for ds in result as! [NSManagedObject] {
objAnimal.name = (ds.value(forKey: "name")) as! String
objAnimal.type = (ds.value(forKey: “type”)) as! String
Var objAVM = AnimalViewModel(aniModel: objAnimal)
tempArr.append(objAVM)
}
} catch {
print(" Error ")
}
The array tempArr contains all duplicate element as last inserted element even objAnimal contains different values.
Thanks,

First of all never print a meaningless literal string like "Error" in a catch block. Print always the error instance.
Animal is obviously a class (reference type). You are creating one instance and the properties are updated in the loop. As always the same instance is used the values are overwritten and you get result.count items with the same contents.
Create new instances inside the loop and replace Entity with the real entity name
var tempArr = [AnimalViewModel]()
do {
let result = try managedContext.fetch(fetchRequest) as! [Entity] // let !
for ds in result {
let objAnimal = Animal() // let !
objAnimal.name = ds.name
objAnimal.type = ds.type
let objAVM = AnimalViewModel(aniModel: objAnimal) // let !
tempArr.append(objAVM)
}
} catch {
print(error)
}
And please notice and fix the warnings about never mutated variables

Related

Realm - how to find a string in a List

Here's my object:
class Cat: Object {
let toys = List<String>()
}
How can I find a toy in the toys array and delete it?
if let foundToy = cat.toys.filter(???).first {
try! realm.write {
realm.delete(foundToy)
}
}
Realm does not support queries on a List of primities (yet).
EDIT: Release 10.7 added support for filters/queries as well as aggregate functions on primitives so the below info is no longer completely valid. However, it's still something to be aware of.
You will need to define a Realm ToyClass and have a property of String.
See Array of Primitives: Support queries #5361
So create a ToyClass
class ToyClass: Object {
#objc dynamic var toy_name = ""
}
and update your CatClass List
class CatClass: Object {
let toys = List<ToyClass>()
}
There are lots of way to delete but if you know the name of the toy, you can delete it directly from Realm.
IMPORTANT - this will remove the first object that matches the filter criteria completely from Realm which includes the object and the reference to it in the list. Note that it's going to delete whatever object matches the first object so if you have two objects called 'toy 1' it will delete one of them - data stored in realm is 'unsorted' so the result may not be what you want.
if let toyToDelete = realm.objects(ToyClass.self).filter("toy_name == 'toy 1'").first {
try! realm.write {
realm.delete(toyToDelete)
}
}
If you just want to remove the first object that matches the criteria (which may be dangerous) from the list but keep the object in Realm, you can do this
let cat = realm.objects(CatClass.self).first!
if let toyToDelete = cat.toys.filter("toy_name == 'toy 1'").first {
try! realm.write {
cat.toys.realm?.delete(toyToDelete)
}
}
You should really add a primary key to your objects so you can tell realm specifically which object to find/delete.
class ToyClass: Object {
#objc dynamic var toy_id = UUID().uuidString
#objc dynamic var toy_name = ""
override static func primaryKey() -> String? {
return "toy_id"
}
}
EDIT: Some testing code to demonstrate firstIndex potentially not working
Set up the cat and two toys
let cat0 = CatClass()
cat0.cat_name = "cat 0"
let toy0 = ToyClass()
toy0.toy_name = "toy 0"
let toy1 = ToyClass()
toy1.toy_name = "toy 1"
cat0.toys.append(toy0)
cat0.toys.append(toy1)
try! realm.write {
realm.add(cat0)
}
Then retrieve cat0 and attempt to get the index of toy 1
let cat = realm.objects(CatClass.self).filter("cat_name == 'cat 0'").first!
let toyToBeDeleted = cat.toys.filter("toy_name == 'toy 1'").first!
print(toyToBeDeleted) //prints toy 1
let index = cat.toys.firstIndex(of: toyToBeDeleted)
print(index) //prints nil
If you are keeping an array of distinct String (i.e., the toys array doesn't contain duplicates), you can just delete the first String found:
if let toyIndex = cat.toys.firstIndex(of: toyNameToBeDeleted) {
try! realm.write {
cat.toys.remove(at: toyIndex)
 }
}
If you are trying to delete all String objects == to a certain toy name, do this instead:
try! realm.write {
cat.toys = cat.toys.filter { $0 != toyNameToBeDeleted }
}

FMDB / sqlite query problem with function

I am really new to swift and trying to understand ios programming. Basically I have a table where "id" and "fact" columns. id is primary key'd and here is the swift fuction I am trying to query the values
func getItemsFromRow(fact_cid: String) ->[FactsData]{
var rowData: [FactsData]!
let rawQ = "select * from facts where \(facts_id)=?"
if (openDatabase()){
do{
let results = try database.executeQuery(rawQ, value(forKey: fact_cid))
while results.next(){
let currentFact = FactsData(factid: results.string(forColumn: facts_id), factsdata: results.string(forColumn: facts_fld))
if rowData == nil {
rowData = [FactsData]()
}
rowData.append(currentFact)
}
}
}
return rowData
}
But this line gives me error
let results = try database.executeQuery(rawQ, value(forKey: fact_cid))
Error is
Cannot invoke 'executeQuery' with an argument list of type '(String, Any?)'
I am trying to pass the id as string. Not sure what I am doing wrong here.
Any help is much appreciated.
Change it to
let results = try database.executeQuery(rawQ, values: [fact_cid])
since the second parameter should be an array

Loop in different type of array to find matched one

I'm trying to add a favorite button to my application using CoreData.
I have tableView and added favorite button inside of it to save the label when I press that specific row. I saved it successfully. But I want to populate the CoreData just once for that specific row.
var checkFav = [Fav]()
I created an array with Type Fav which is name of my class for CoreData to populate items I have to check they appear just once.
let result = try context.fetch(Fav.fetchRequest())
checkFav = result as! [Fav]
if checkFav.isEmpty{
let fav = Fav(context: context)
fav.name = name
appDelegate.saveContext()
}
Above you see i populated the array.
do{
let result = try context.fetch(Fav.fetchRequest())
checkFav = result as! [Fav]
if checkFav.isEmpty{
let fav = Fav(context: context)
fav.name = name
appDelegate.saveContext()
}
else{
let checkName = array[rowSelected]
for value in checkFav{
if value.name == checkName{
print("You already have this name ")
}
else {
let fav = Fav(context: context)
fav.name = name
appDelegate.saveContext()
}
}
}
} catch{
print("Error")
}
Let's says I have two name "john","martha" in CoreData if I press to button of "martha" it shouldn't add again. But because of my loop when it sees "john" in the array it thinks this name isn't matching so it's saving "martha" (same name) to CoreData.
How can I check my checkFav array which contains the upcoming name if it contains don't save it again. But if not, add to CoreData. I'm really confused.
Your way to check for an empty entity cannot work if you want to save multiple records.
Add a predicate to the fetch request and a result limit of 1 to check for the specific name.
This method takes the name as parameter and adds a record if the name does not exist. It returns true if an entry is added otherwise false. Further it passes a potential error.
func addFavorite(for name : String) throws -> Bool {
let request : NSFetchRequest<Fav> = Fav.fetchRequest()
request.predicate = NSPredicate(format: "name = %#", name)
request.resultsLimit = 1
if let _ = try context.fetch(request).first {
return false // record exists
} else {
let fav = Fav(context: context)
fav.name = name
appDelegate.saveContext()
return true // record added
}
}

FetchRequst issue with data fault

When I was inserting data to one entity of CoreData, All the rows are inserted successfully(Saved).
But when I try to fetch the data using FetchRequest, Only one row of data is coming even if number of rows inserted are 3 or 4 or anything(more than 1).
Remaining rows are not getting fetched. And when I print fetch results,
It says - Error
0:<EquipmentDetails: 0x6000000bad60>
(entity: EquipmentDetails; id: 0xd000000000040000
coredata:/EquipmentDetails/p1> **data:fault>)**
I didn't get what was going in backend of core data?
code for Insertion
func insertEqipToLocalDb()
{
let mobileNo : String = UserDefaults.standard.string(forKey: "phoneNumber")!
let equipDetailsItem = NSEntityDescription.insertNewObject(forEntityName: "EquipmentDetails", into:managedObjContext) as! EquipmentDetails
for (index,item) in array_IDEquip.enumerated()
{
equipDetailsItem.mobileNumber = mobileNo
equipDetailsItem.type = array_typeEquip[index]
equipDetailsItem.name = array_nameEquip[index]
equipDetailsItem.startDate = array_sDateEquip[index]
equipDetailsItem.endDate = array_eDateEquip[index]
equipDetailsItem.equpID = Int16(item)
equipDetailsItem.serviceDatesStr = array_serviceDateEquip[index]
}
do
{
try managedObjContext.save()
UserDefaults.standard.set("AlreadyInstalled", forKey: "statusInstallation")
}
catch
{
Exception.insertExceptionDetails(errorMsg: error as NSError, context: managedObjContext)
}
}
//code for fetching
let request = NSFetchRequest<NSFetchRequestResult>()
let entity = NSEntityDescription.entity(forEntityName:"EquipmentDetails", in: managedObjContext)
request.entity = entity
do
{
let fetchResults = try managedObjContext.fetch(request)
for r in fetchResults
{
typeEquipArray.append((r as AnyObject).value(forKey: "type") as! String)
}
}
catch let error as NSError
{
Exception.insertExceptionDetails(errorMsg: error, context: managedObjContext)
}
On this line:
let equipDetailsItem = NSEntityDescription.insertNewObject(forEntityName: "EquipmentDetails", into:managedObjContext) as! EquipmentDetails
You create one instance. In the loop that follows, you set values for the type, name, etc properties over and over again on that same instance. Then you save changes, which include just that one object. If you want a difference instance of EquipmentDetails for each pass through the loop, you need to create the instance inside the loop.
The "fault" message is not an error unless you tried to access the property values and found that they were not present. It's part of how Core Data works. See the answer that Harshal Valanda linked in the comments for more detail.

iterating an array to extract a value from firebase database in swift

might sound like a basic question--but I'm not seeing where I am going wrong..
I end up with either of these two scenarios:
I keep getting the error "Could not cast value of type __NSCFNumber to NSSTring". if I use extractedSku = skuList[i]!.value["sku"] as! String
If I remove as! String it saves it, but it isn't saved as a string. How do I get this to be saved as a string?
I have appended data from firebase into an array
skuArray = [AnyObject?]()
in viewDidLoad, I am iterating skuArray to extract the 'sku' and store into a variable.
var skuArray = [AnyObject?]()
var productDetailArray = [AnyObject?]()
data stored in Sku Array is:
[Optional(Snap (aRandomKey) {
active = 1;
sku = 888888;
})]
viewDidLoad:
let skuList = self.skuArray
for var i = 0; i < skuList.count ; ++i{
let extractedSku = skuList[i]!.value["sku"] as! String
// go into database and extract "products" details by sku
self.databaseRef.child("products/\(extractedSku)").observeEventType(.ChildAdded, withBlock: { (snapshot:FIRDataSnapshot) in
self.productDetailArray.append(snapshot)
})
Since the underlying type is NSNumber, use the stringValue property to get a String:
if let extractedSku = (skuList[i]?.value["sku"] as? NSNumber)?.stringValue {
// use extractedSku which is of type String
}

Resources