CoreData - check if object exists in database - ios

I have viewController with table view and FetchResultsController. ViewController downloads data from web and then save it to CoreData. Downloading happens every launch. I need someway to compare info that I downloaded from web and only the save it to CoreData
How can I do this ?
I had an idea of fetching all objects by fetchedResultsController.performFetch() and then assigning them to array, but I dont understand how to iterate over that array (it us [AnyObject])
Maybe there are more easy ways ?

I figured that out
I need to perform several steps in order to make comparison of content from core with array of custom objects
1) create empty array
var arrayOfReposOnDisk : [RepoObject] = []
2) fetch objects from CoreData
let fetchedData = self.fetchedResultsController.fetchedObjects
3) iterate over fetchedData and convert each value for key-value to my custom object
for i in 0 ..< fetchedData!.count {
let object = fetchedData![i]
let name = object.valueForKey("name") as! String
let description = object.valueForKey("description") as! String
let authorName = object.valueForKey("avatarURL") as! String
let avatarURL = object.valueForKey("authorName") as! String
let forks = object.valueForKey("forks") as! Int
let watches = object.valueForKey("watches") as! Int
let repoObject = RepoObject(name: name, description: description, authorName: authorName, avatarURL: avatarURL, forks: forks, watches: watches)
arrayOfItemsOnDisk.append(repoObject)
}
4) finally, make a comparison
if arrayOfReposOnDisk.contains ({ $0.name == name }) {
print("There is the same name in json as in CoreData, doing nothing")
} else {
self.insertNewObject(name, descriptionText: description, avatarURL: avatarURL, authorName: authorName, forks: forks, watches: watches)
}

You can used fetchedObjects on the fetched results controller. It is an array of [AnyObject]?, but you can cast it to your object type with something like
if let myObjects = fetchedResultsController.fetchedObjects as? [MyObjectType] {
for object in myObjects {
// do some comparison to see if the object is in your downloaded data
}
}
Another way to get the objects is to create a NSFetchRequest and use executeFetchRequest on your managed object context.

Related

iOS Swift iterate over an Array of Dictionary Items from Firestore

I am trying to iterate over the following dictionary:
Dictionary in Firebase
This is my code:
Global.sharedInstance.db.collection("usuarios").getDocuments { (snapshot, error) in
if error != nil {
print("error de lectura usuarios...")
} else {
if let snapshot = snapshot {
for document in snapshot.documents {
let data = document.data()
let txtIdentificador = data["identificador"] as? String ?? ""
let txtBio = data["bio"] as? String ?? ""
let txtNombre = data["nombre_usuario"] as? String ?? ""
let txtFotoPerfil = data["foto_perfil"] as? String ?? ""
var arrFotos = data["fotos"] as? [String: [String:String]]
}
}
}
}
I am able to retrieve the first few lines, like the id, the biography, name, etc.
But when I try to access the array of dictionary I have no idea.
This is the main idea:
I have a set of users, which I iterate over with the first loop 'for document in documents...", then each user has a set of photos. I want to iterate over the 3 photos, and in each iteration I want to retrieve the fields, so I can create a object called Image and associate the user with the 'hasUpload(Image)'.
I would like to know how to iterate over X photos an in each iteration retrieve the fields.
Something like this:
var arrFotos = data["fotos"] as? [String: [String:String]]
for foto in arrFotos {
for (key,value) in foto {
}
}
I get the error: For-in loop requires '[String : [String : String]]?' to conform to 'Sequence'; did you mean to unwrap optional?
A similar StackOverflow case can be found here and this is how they resolved it:
You can either do this, where x is the index and token is the element:
for (x, token) in transcriptCandidate.enumerated() {
}
Or this if you don't need the index:
for token in transcriptCandidate {
}

Realm List not stored in Swift 4.2 in release config

I've just built the latest version of my app, and have run into a problem where the Lists on all of my realm objects are not being stored.
Here is some sample code:
Object:
public class ReportItem: Object {
#objc dynamic var id: String!
#objc dynamic var someDate: Date?
// This contains another List as one of its properties
let list = List<OtherRealmObject>()
override public class func primaryKey() -> String {
return "id"
}
convenience public init(id: String, date: Date) {
self.init()
self.id = id
self.date = date
}
}
This object is being created by a json mapper from the response of a network request:
// Convert json to dictionary then
guard let id = json["id"] as? String else {
return nil
}
let date = json["date"] as? Date
let objects = json["someObjects"] as? [String: Any]
let someRealmObjects = [OtherRealmObject]()
objects.forEach { object in
// Create some realm object
someRealmObjects.append(newSomeRealmObject)
}
let reportItem: ReportItem?
if let date = date?.convertToDateFromString() {
reportItem = ReportItem(id: id, date: date)
} else {
return nil
}
reportItem!.list.append(objectsIn: someRealmObjects)
return reportItem!
Then this is passed back to my view controller, and stored like so:
// Report item is the item we just created in the json mapper
someNetworkOperation.success = { reportItem in
DispatchQueue.main.sync {
let realm = try! Realm()
try! realm.write {
realm.add(reportItem, update: true)
}
}
}
The item is then retrieved somewhere else, however list is empty, and when I try and filter I get the error This method may only be called on RLMArray instances retrieved from an RLMRealm. For some reason my list is not being persisted when I add the report object to the database.
This used to work, however in the last week or so it has stopped working. I'm wondering if it's to do with updating to Swift 4.2/Xcode 10. Also, my code just works fine in debug, not in release. Has anyone else run into this issue?
This was because during the Swift 4.2 conversion Reflection Metadata Level was somehow set to None instead of All. 🤦‍♂️
After Realm's latest update, the syntax has changed.
The to-many relationship should now be preceded by #Persisted. It also cannot be a let constant:
#Persisted var items = List<Item>()

Send two Arrays of Strings and Ints as matchData

I am working on a turn based game with Game Center. I want to send an Array of Strings and an Array of Ints as matchData. I know how to create both, but I only know how to send one of them...
This is how i create the String Array:
var strings = [String]()
let data = NSKeyedArchiver.archivedDataWithRootObject(strings)
This is how i create the Int Array:
var array : [Int] = []
let data = NSData(bytes: array, length: array.count * sizeof(Int))
This is how I send the data i create
currentMatch?.endTurnWithNextParticipants([nextParticipant], turnTimeout: 20, matchData: data, completionHandler: { (error) in
if error != nil {
print(error)
} else {
//Data sent
}
}
})
The easiest way is probably to wrap both in a dictionary and then serialize the dictionary:
let data = NSKeyedArchiver.archivedDataWithRootObject([
"strings":strings,
"numbers":array
])
Then to recover the original data, you can use:
guard let recovered = NSKeyedUnarchiver.unarchiveObjectWithData(data) as? [String:AnyObject],
let strings = recovered["strings"] as? [String],
let array = recovered["numbers"] as? [Int] else {
// recovery failed... deal with it
}
Get your matchData using GKTurnBasedMatch.loadMatchDataWithCompletionHandler: and then use that match data if it exists in the completion block.

Updating CoreData adds a lot of nil values

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?

Array keeps returning nil even when values are stored Swift Firebase

Hi I am new to Swift and Firebase here and I am trying to append the objects I get into the array I have created. But when I return the array, it returns nil even when I print the array it shows the values are stored inside.I tried to assign it to another array but it still returns nil. Please help me as I am currently a newbie to Swift and Firebase and need some guidance. Thanks in advance.
func retrieveData() -> Array<AnyObject>{
var ref = Firebase(url: "my_firebase_url")
ref.queryOrderedByChild("datetime").observeEventType(.Value, withBlock: {
snapshot in
if let i = snapshot.value as? NSDictionary {
for item in i {
if let value = item.value as? NSDictionary {
self.adArray.append(value)
println(self.adArray[0])
println(self.adArray.count)
self.codeNum = value["code"] as! String
self.datetime = value["datetime"] as! String
self.nameEvent = value["name"] as! String
println("code is \(self.codeNum) and name is \(self.nameEvent) at \(self.datetime)")
}
}
}
})
println(adArray.count)
return adArray }

Resources