Swift - Core Data Relationship - Album Style - ios

I have been working on establishing a working Core Data relationship for days now. I have made a bit of progress, but nothing substantial, so I am hoping someone much smarter than I would be able to tackle this and help me out.
For a bit of context, I am essentially creating a photo album app. Inside the album, there would be photos, with a caption on each photo. I have structured an entity "ScannedItem" that has attributes "savedTitle", "savedText", and "savedImage".
savedTitle = The title of the image.
savedText = The caption of the image.
savedImage = The image itself.
I am not sure how I would go about adding a "ScannedItem" (the entity holding the previous attributes) to my "Album" entity.
For more information, a "ScannedItem", and all of its attributes, may only be in one album, so I believe this would be a 'To-One' relationship.
I have included images of how I have structured my entities and relationships here.
Below is my code for saving the "Scanned Item Entity".
let appDel: AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let context: NSManagedObjectContext = appDel.managedObjectContext
let saveData = NSEntityDescription.insertNewObjectForEntityForName("ScannedItem", inManagedObjectContext: context)
let imageRep = UIImagePNGRepresentation(image)!
saveData.setValue(textTitle, forKey: "savedTitle")
saveData.setValue(scannedText, forKey: "savedText")
saveData.setValue(imageRep, forKey: "savedImage")
do{
try context.save()
}catch{
print("Failed")
}
How would I go about saving that ScannedItem to an Album? I have been struggling with the code for days and it's nearly driven me insane. Any help would be greatly appreciated. It's time to ask the experts.
UPDATE
do{
let request = NSFetchRequest(entityName: "ScannedItem")
let results = try context.executeFetchRequest(request)
let saveAlbum = NSEntityDescription.insertNewObjectForEntityForName("Album", inManagedObjectContext: context)
saveAlbum.setValue(results, forKey: "album")
let albumRequest = NSFetchRequest(entityName: "Album")
let albumResults = try context.executeFetchRequest(albumRequest)
if albumResults.count > 0{
for item in albumResults as! [NSManagedObject]{
let albumPrint = item.valueForKey("album")
print("Album Test: " + (albumPrint! as! String))
}
}
if results.count > 0{
for item in results as! [NSManagedObject]{
let titleSaved = item.valueForKey("savedTitle")
let textSaved = item.valueForKey("savedText")
//let imageSaved = item.valueForKey("savedImage")
//let titleSaved = item.valueForKey("savedTitle")
print("Title" + (titleSaved! as! String))
print("Text" + (textSaved! as! String))
//print(imageSaved!)
}
}
}catch{
print("Failed")
}
Now I am receiving an error stating that "the entity Album is not key value coding-compliant for the key "album". I do not know what to do, I have been toying with it for hours.

On Album, change scanItem to scanItems and make it to-many because you can have many items in an album.
Change albumRelationship to album and set it to to-one.
You need to know or fetch the album you want to add to and then just set:
saveData.setValue(album, forKey: "album")
Core data will automatically set the other end of the relationship for you.

Related

How can I edit my data in Core Data Swift 5?

I'm developing an iOS contacts app. I don't know how to use Core Data but I learned a little bit and was able to create data and display it on a tableview. But now I need some help. I'm looking for code that queries a Contact name in Core Data to check if this contact already exists. If it does, I need to edit this data, if not I need to create this data. Can someone help me?
I'm using this simple code to create data:
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let context = appDelegate.persistentContainer.viewContext
let entity = NSEntityDescription.entity(forEntityName: "Contact", in: context)
let newEventWithTickets = NSManagedObject(entity: entity!, insertInto: context)
var name = contact.name
var email = contact.email
newEventWithTickets.setValue(name, forKey: "name")
newEventWithTickets.setValue(email, forKey: "email")
do {
try context.save()
} catch {
print("Failed saving")
}
An approach that you can consider is using NSPredicate. Essentially, you create conditions (name & email) to check in your managedContext to find entities that much the specific query.
Here is a rough draft on how to approach it:
let fetchRequest = NSFetchRequest<>(entityName: )
fetchRequest.predicate = NSPredicate(format:)
do {
data = try coreDataStack.managedContext.fetch(fetchRequest)
} catch {
print ("Did not fetch")
}
Once you get the data, use a conditional to compare what was inputed and what your query sends back.

Core Data Memory Crash [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 6 years ago.
Improve this question
I am using Core Data for the first time in my project and I feel there is a serious problem in my approach. What I am doing is that I fetch data from server (data includes pngs as well). Save it in core data locally. Then at app launch, I load the entire data in an array. Then this array is used where ever I need it. I think I am following a very bad approach. Can anyone guide me what should be a better approach? Should I only query Core Data when data is needed instead of loading everything in memory at start?
When data is being populated to the array, I can see memory increasing in Xcode and after a certain value, it crashes.
Here is my code for saving data:
func saveDataLocally () {
let moContext = ((UIApplication.shared.delegate) as! AppDelegate).managedObjectContext
let entity = NSEntityDescription.entity(forEntityName: "FoodPlace", in: moContext)
for foodPlaceData in self.downloadedData_ {
let foodPlace = NSManagedObject(entity: entity!, insertInto: moContext) as! FoodPlace
foodPlace.objectId = foodPlaceData.objectId_
foodPlace.name = foodPlaceData.name_
foodPlace.address = foodPlaceData.address_
foodPlace.keywords = foodPlaceData.keywords_
foodPlace.baseFavourites = Int64(foodPlaceData.baseFavourites_)
foodPlace.startingTime = foodPlaceData.startingTime_
foodPlace.endingTime = foodPlaceData.endingTime_
foodPlace.category = foodPlaceData.category_
foodPlace.basePrice = foodPlaceData.basePrice_
foodPlace.dealTitle = foodPlaceData.dealTitle_
foodPlace.versionNumber = Int64(foodPlaceData.versionNumber_)
foodPlace.menuItems = NSKeyedArchiver.archivedData(withRootObject: foodPlaceData.menuItems_)
foodPlace.location = NSKeyedArchiver.archivedData(withRootObject: foodPlaceData.location_)
foodPlace.deals = NSKeyedArchiver.archivedData(withRootObject: foodPlaceData.deals_)
foodPlace.foodPlacePhotos = NSKeyedArchiver.archivedData(withRootObject: foodPlaceData.foodPlacePhotos_)
moContext.insert(foodPlace)
}
do {
try moContext.save()
}
catch let error {
print("error saving = \(error.localizedDescription)")
}
}
where menuItems is a Dictionary which contains text as well as png images. Also, deals and foodPlacePhotos only contain png images.
Here is the code for fetching:
func loadDataLocally () {
let moContext = ((UIApplication.shared.delegate) as! AppDelegate).managedObjectContext
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "FoodPlace")
do {
let results = try moContext.fetch(request)
let savedFoodPlaceData = results as! [FoodPlace]
downloadedData_ = []
for foodPlace in savedFoodPlaceData {
let objectId = foodPlace.objectId
let name = foodPlace.name
let address = foodPlace.address
let keywords = foodPlace.keywords
let baseFavourites = foodPlace.baseFavourites
let startingTime = foodPlace.startingTime
let endingTime = foodPlace.endingTime
let category = foodPlace.category
let menuItems = NSKeyedUnarchiver.unarchiveObject(with: foodPlace.menuItems!) as? [Dictionary<String,AnyObject>]
let location = NSKeyedUnarchiver.unarchiveObject(with: foodPlace.location!) as? Dictionary<String,Double>
let deals = NSKeyedUnarchiver.unarchiveObject(with: foodPlace.deals!) as? [UIImage]
let basePrice = Float(foodPlace.basePrice)
let dealTitle = foodPlace.dealTitle
let versionNumber = foodPlace.versionNumber
let foodPlacePhotos = NSKeyedUnarchiver.unarchiveObject(with: foodPlace.foodPlacePhotos!) as? [UIImage]
let data = FoodPlaceData(objectId: objectId!, name: name!, address: address!, category: category!, keywords: keywords!, baseFavourites: Int(baseFavourites), startingTime: startingTime!, endingTime: endingTime!, menuItems: menuItems!, location: location!, deals: deals!,basePrice: basePrice,dealTitle: dealTitle!,versionNumber: Int(versionNumber),foodPlacePhotos: foodPlacePhotos!)
downloadedData_.insert(data, at: downloadedData_.count)
}
}
catch let error {
print("error fetching = \(error.localizedDescription)")
}
}
and here is the code for deleting data:
func deleteAllLocalData () {
let moContext = ((UIApplication.shared.delegate) as! AppDelegate).managedObjectContext
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "FoodPlace")
fetchRequest.returnsObjectsAsFaults = false
do {
let results = try moContext.fetch(fetchRequest)
for managedObject in results {
let managedObjectData : NSManagedObject = managedObject as! NSManagedObject
moContext.delete(managedObjectData)
}
try moContext.save()
} catch let error {
print("Delete all data in FoodPlace error : \(error) \((error as NSError).userInfo)")
}
}
Difficult to be prescriptive without a lot more detail about your code. But a few thoughts:
rather than storing the PNG data in CoreData itself, consider storing it in the file system directly, and using CoreData to store only the filename for the PNG.
Alternatively, if you really want the PNGs in CoreData, consider adding a separate entity for the PNG, and adding a one-one relationship from your current entity to the new one.
Either of the above will avoid all the PNG data being loaded into memory when you load the array. You can then load/unload the PNGs (either from the file system or from the related entity) as needed.
In addition, consider using:
NSFetchedResultsController and/or
fetchBatchSize
These will help to avoid all the objects being loaded into memory.

only saving last item while saving in core data swift [duplicate]

This question already has answers here:
Core Data in Swift: Only saving last object in a for loop
(4 answers)
Closed 6 years ago.
With this function I want to add attributes in core-data entity but it saves only last item repeatedly.
what am I doing wrong?
func SetFeaturedValues(Array : NSArray){
let appDelegate =
UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext
let entity = NSEntityDescription.entityForName("FeaturedJob",
inManagedObjectContext:managedContext)
let FeaturedJob = NSManagedObject(entity: entity!,
insertIntoManagedObjectContext: managedContext)
print("array is \(Array)")
for i in Array{
if let rowData :NSDictionary = i as? NSDictionary{
FeaturedJob.setValue(rowData["company"], forKey: "company")
FeaturedJob.setValue(rowData["city"], forKey: "city")
FeaturedJob.setValue(rowData["id"], forKey: "id")
FeaturedJob.setValue(rowData["user_logo"], forKey: "user_logo")
FeaturedJob.setValue(rowData["title"], forKey: "title")
do {
try managedContext.save()
self.Featured.append(FeaturedJob)
print("featured job is \(FeaturedJob)")
// people.append(person)
} catch let error as NSError {
print("Could not save \(error), \(error.userInfo)")
}
}
}
}
Ok, so this could still do with some tidying up but...
What's going wrong is that you are creating only one singleNSManagedObject instance called FeaturedJob. Every time you iterate through the array you append its value and save it. But its the same instance! That's why only one instance gets saved.
So put the line with let let FeaturedJob = ... inside the if block just before you start assigning values. That way you're going to get a new one each time.
PS: You are saving the context with every iteration. You probably only want to do that once at the end of the function.

What is the right way to save an array as data in core data?

As title, I have an array of multiple objects and I wish to store them in core data(I understand that we can't save array into core data so I break my code into json[0][i]["ID"].int format), perhaps you will be clearer after reading my code
let appDel:AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let context:NSManagedObjectContext = appDel.managedObjectContext
let newUser = NSEntityDescription.insertNewObjectForEntityForName("Users", inManagedObjectContext: context) as NSManagedObject
socket.on("reply") {data, ack in
let json = JSON(data)
print("database replied")
print(json[0].count)
for var i=0; i<json[0].count; ++i{
newUser.setValue(json[0][i]["ID"].int, forKey: "patientID")
newUser.setValue(json[0][i]["Name"].string, forKey: "patientName")
newUser.setValue(json[0][i]["Mileage"].double, forKey: "patientMileAge")
do{
try context.save()
}catch{
print("Could not save data")
}
}
}
This is what my data would look like
Here comes the troubles, i only get [ID:4, Name:'hung', Mileage:'0.23'] as the result, the former 2 arrays have been replaced.
Why can't I save them as I intended?
let newUser = NSEntityDescription.insertNewObjectForEntityForName("Users", inManagedObjectContext: context) as NSManagedObject
should be inside the loop, because otherwise you create one object and then repeatedly update it and save the change (so the old value is overwritten)
Move this line of code into your loop:
let newUser = NSEntityDescription.insertNewObjectForEntityForName("Users", inManagedObjectContext: context) as NSManagedObject
This should work, write me if it didn't
because you're not adding 3 entries, you're only updating the values for one.
you need to move
let newUser = NSEntityDescription.insertNewObjectForEntityForName("Users", inManagedObjectContext: context) as NSManagedObject
inside your for loop

Core data object not appending to array

I am having a small issue with appending my Core Data object to an array. Everything is being saved correctly in Core Data, but when I checked the array, it was empty. The code looks as follows:
func saveQA(question: String, answer: String) {
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext
let entity = NSEntityDescription.entityForName("Card", inManagedObjectContext: managedContext)
let newQA = Card(entity: entity!, insertIntoManagedObjectContext: managedContext)
newQA.question = question
newQA.answer = answer
do {
try managedContext.save()
playerCards.append(newQA)
}
catch {
print("error")
}
}
What seemed to work for me was changing my array from type Card, to type String. Then appending both newQA.question and newQA.answer separately to playerCards. Although I am unsure this is a valid solution. As I am unsure the question and answer will stay linked to each other that way. Any help is great, thank you in advance.
you should add data to you NSMutableArray like this :
playerCards.addObject(newQA)

Resources