CoreData: How to fetch child objects? - ios

I am trying to fetch child objects (NameCD) of the parent object (CountryCD), but I have a bug:
CountriesAPI[2441:78546] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Unable to parse the format string "parent == %&"'
Is my relationship okay and how do I fix this error?
func fetchCountries() -> [CountryCD] {
do {
let fetchRequest = NSFetchRequest<CountryCD>(entityName: "CountryCD")
fetchRequest.predicate = NSPredicate(format: "parent == %&", "CountryCD")
fetchRequest.returnsObjectsAsFaults = false
let countries = try viewContext.fetch(fetchRequest)
return countries
} catch {
print("CoreDataService - fetchCountries: \(error)")
return []
}
}
import Foundation
import CoreData
extension NameCD {
#nonobjc public class func fetchRequest() -> NSFetchRequest<NameCD> {
return NSFetchRequest<NameCD>(entityName: "NameCD")
}
#NSManaged public var common: String?
#NSManaged public var official: String?
#NSManaged public var countryCD: CountryCD?
}
extension NameCD : Identifiable {
}
import Foundation
import CoreData
extension CountryCD {
#nonobjc public class func fetchRequest() -> NSFetchRequest<CountryCD> {
return NSFetchRequest<CountryCD>(entityName: "CountryCD")
}
#NSManaged public var altSpellings: [String]?
#NSManaged public var area: Double
#NSManaged public var borders: NSObject?
#NSManaged public var callingCodes: [String]?
#NSManaged public var capital: [String]?
#NSManaged public var flag: String?
#NSManaged public var latlng: [Double]?
#NSManaged public var region: String?
#NSManaged public var nameCD: NSSet?
}
// MARK: Generated accessors for nameCD
extension CountryCD {
#objc(addNameCDObject:)
#NSManaged public func addToNameCD(_ value: NameCD)
#objc(removeNameCDObject:)
#NSManaged public func removeFromNameCD(_ value: NameCD)
#objc(addNameCD:)
#NSManaged public func addToNameCD(_ values: NSSet)
#objc(removeNameCD:)
#NSManaged public func removeFromNameCD(_ values: NSSet)
}
extension CountryCD : Identifiable {
}
EDIT:
Ok I made edit and pasted two new screens. I am not sure about the relationship between CountryCD and NameCD. As you can see there is warning on the left side: abstract entity has no children. Maybe this is main problem?

Related

How to handle optional/missing attributes of related Core Data objects in NDPredicate

I've recently ran into an issue with core data that I'm unable to solve.
I have the following object and relationships Recommendation <---> Item
extension Recommendation {
#nonobjc public class func fetchRequest() -> NSFetchRequest<Recommendation> {
return NSFetchRequest<Recommendation>(entityName: "Recommendation")
}
#NSManaged public var age: Int?
#NSManaged public var tags: Array<String>?
#NSManaged public var object: Item?
}
extension Item {
#nonobjc public class func fetchRequest() -> NSFetchRequest<Item> {
return NSFetchRequest<Recommendation>(entityName: "Item")
}
#NSManaged public var id: String?
#NSManaged public var name: String?
#NSManaged public var recommendation: Recommendation?
}
I want to fetch all Items based on attributes in their related Recommendation.
Some of these attributes could be missing.
How would I create a NSPredicate to fetch objects where the Recommendation.age or Recommendation.tags could be non-existent (applicable to all ages/tags) or should be compared against a provided value?
Should I always provide a Default Value?

How to save a custom list of objects in Core Data?

I am pretty new in the Core Data and trying to save an array made of custom object but there is small problem: I don't know how to do it the right way. The application retrieves data from the network and writes it to the Core Data database. It contains several arrays and objects that are custom objects. There is no problem with writing strings, ints etc. But how do I save a list of custom objects? The objects used to get data from the web are a bit different to the ones used to work with Core Data, so I can't just assign them. Of course, this can be done in the next loop, but isn't there anything better?
I am completely new to Core Data and I am looking for the best solutions for slightly more complex operations. Is Core Data worth using for complex operations?
CoreDataService:
protocol CoreDataServiceProtocol {
func saveCountries(countries: [Country])
func fetchCountries() -> [CountryCoreData]
}
final class CoreDataService : CoreDataServiceProtocol {
private var persistentContainer: NSPersistentContainer = {
let persistentContainer = NSPersistentContainer(name: "CountriesAPI")
persistentContainer.loadPersistentStores { _, error in
print(error?.localizedDescription ?? "")
}
return persistentContainer
}()
var viewContext: NSManagedObjectContext {
return persistentContainer.viewContext
}
func saveCountries(countries: [Country]) {
for country in countries {
let newCountry = CountryCoreData(context: viewContext)
for currency in country.currencies {
newCountry.addToCurrencyCoreData(currency)
}
//Below, I can easily create a new object that I want to assign as a parent property
var translation = TranslationCoreData(context: viewContext)
translation.br = country.translations?.br
newCountry.addToTranslationCoreData(translation)
//If there is no relationship between the models this way is pretty nice, but in my case there are several relationships
newCountry.setValue(country.alpha2Code, forKey: "alpha2Code")
newCountry.setValue(country.alpha3Code, forKey: "alpha3Code")
newCountry.setValue(country.altSpellings, forKey: "altSpellings")
newCountry.setValue(country.area, forKey: "area")
newCountry.setValue(country.borders, forKey: "borders")
newCountry.setValue(country.callingCodes, forKey: "callingCodes")
newCountry.setValue(country.capital, forKey: "capital")
newCountry.setValue(country.cioc, forKey: "cioc")
// newCountry.setValue(country.currencies, forKey: "currencies")
newCountry.setValue(country.demonym, forKey: "demonym")
newCountry.setValue(country.flag, forKey: "flag")
newCountry.setValue(country.gini, forKey: "gini")
//newCountry.setValue(country.id, forKey: "id")
// newCountry.setValue(country.languages, forKey: "languages")
newCountry.setValue(country.name, forKey: "name")
newCountry.setValue(country.nativeName, forKey: "nativeName")
newCountry.setValue(country.numericCode, forKey: "numericCode")
newCountry.setValue(country.population, forKey: "population")
newCountry.setValue(country.region, forKey: "region")
// newCountry.setValue(country.regionalBlocs, forKey: "regionalBlocs")
newCountry.setValue(country.subregion, forKey: "subregion")
newCountry.setValue(country.timezones, forKey: "timezones")
newCountry.setValue(country.topLevelDomain, forKey: "topLevelDomain")
// newCountry.setValue(country.translations, forKey: "translations")
}
do {
try viewContext.save()
print("Success")
} catch {
print("Error saving: \(error.localizedDescription)")
}
}
Model generated by CoreDataxcdatamodel - CountryCoreData:
import Foundation
import CoreData
extension CountryCoreData {
#nonobjc public class func fetchRequest() -> NSFetchRequest<CountryCoreData> {
return NSFetchRequest<CountryCoreData>(entityName: "CountryCoreData")
}
#NSManaged public var alpha2Code: String?
#NSManaged public var alpha3Code: String?
#NSManaged public var altSpellings: [String]?
#NSManaged public var area: Double
#NSManaged public var borders: [String]?
#NSManaged public var callingCodes: [String]?
#NSManaged public var capital: String?
#NSManaged public var cioc: String?
#NSManaged public var currencies: [CurrencyCoreData]?
#NSManaged public var demonym: String?
#NSManaged public var flag: String?
#NSManaged public var gini: Double
#NSManaged public var id: UUID?
#NSManaged public var languages: [LanguageCoreData]?
#NSManaged public var name: String?
#NSManaged public var nativeName: String?
#NSManaged public var numericCode: String?
#NSManaged public var population: Int16
#NSManaged public var region: String?
#NSManaged public var regionalBlocks: [RegionalBlockCoreData]?
#NSManaged public var subregion: String?
#NSManaged public var timezones: [String]?
#NSManaged public var topLevelDomain: [String]?
#NSManaged public var translations: [TranslationCoreData]?
#NSManaged public var currencyCoreData: NSSet?
#NSManaged public var languageCoreData: NSSet?
#NSManaged public var regionalBlockCoreData: NSSet?
#NSManaged public var translationCoreData: NSSet?
}
// MARK: Generated accessors for currencyCoreData
extension CountryCoreData {
#objc(addCurrencyCoreDataObject:)
#NSManaged public func addToCurrencyCoreData(_ value: CurrencyCoreData)
#objc(removeCurrencyCoreDataObject:)
#NSManaged public func removeFromCurrencyCoreData(_ value: CurrencyCoreData)
#objc(addCurrencyCoreData:)
#NSManaged public func addToCurrencyCoreData(_ values: NSSet)
#objc(removeCurrencyCoreData:)
#NSManaged public func removeFromCurrencyCoreData(_ values: NSSet)
}
// MARK: Generated accessors for languageCoreData
extension CountryCoreData {
#objc(addLanguageCoreDataObject:)
#NSManaged public func addToLanguageCoreData(_ value: LanguageCoreData)
#objc(removeLanguageCoreDataObject:)
#NSManaged public func removeFromLanguageCoreData(_ value: LanguageCoreData)
#objc(addLanguageCoreData:)
#NSManaged public func addToLanguageCoreData(_ values: NSSet)
#objc(removeLanguageCoreData:)
#NSManaged public func removeFromLanguageCoreData(_ values: NSSet)
}
// MARK: Generated accessors for regionalBlockCoreData
extension CountryCoreData {
#objc(addRegionalBlockCoreDataObject:)
#NSManaged public func addToRegionalBlockCoreData(_ value: RegionalBlockCoreData)
#objc(removeRegionalBlockCoreDataObject:)
#NSManaged public func removeFromRegionalBlockCoreData(_ value: RegionalBlockCoreData)
#objc(addRegionalBlockCoreData:)
#NSManaged public func addToRegionalBlockCoreData(_ values: NSSet)
#objc(removeRegionalBlockCoreData:)
#NSManaged public func removeFromRegionalBlockCoreData(_ values: NSSet)
}
// MARK: Generated accessors for translationCoreData
extension CountryCoreData {
#objc(addTranslationCoreDataObject:)
#NSManaged public func addToTranslationCoreData(_ value: TranslationCoreData)
#objc(removeTranslationCoreDataObject:)
#NSManaged public func removeFromTranslationCoreData(_ value: TranslationCoreData)
#objc(addTranslationCoreData:)
#NSManaged public func addToTranslationCoreData(_ values: NSSet)
#objc(removeTranslationCoreData:)
#NSManaged public func removeFromTranslationCoreData(_ values: NSSet)
}
extension CountryCoreData : Identifiable {
}
In my opinion first change these things
Instead of this
newCountry.setValue(country.alpha2Code, forKey: "alpha2Code")
add this line
newCountry.alpha2Code = country.alpha2Code
Do this way on your Save countries function
func saveCountries(countries: [Country]) {
for country in countries {
let newCountry = CountryCoreData(context: viewContext)
newCountry.alpha2Code = country.alpha2Code//add this line
}
saveContext()
}
Create ViewContext function so you can reuse this!
func saveContext(){
do {
try viewContext.save()
print("Success")
} catch {
print("Error saving: \(error.localizedDescription)")
}
}
Using Transformable type you can save array directly in the CoreData without for loop
https://stackoverflow.com/a/29827564/8201581

Assign value related objects core data

I have two entities
#objc(Movies)
public class Movies: NSManagedObject {
}
extension Movies {
#nonobjc public class func fetchRequest() -> NSFetchRequest<Movies> {
return NSFetchRequest<Movies>(entityName: "Movies")
}
#NSManaged public var id: Int64
#NSManaged public var isFav: Bool
#NSManaged public var overview: String?
#NSManaged public var poster: String?
#NSManaged public var release_date: String?
#NSManaged public var title: String?
#NSManaged public var genresID: NSSet?
}
// MARK: Generated accessors for genresID
extension Movies {
#objc(addGenresIDObject:)
#NSManaged public func addToGenresID(_ value: GenresID)
#objc(removeGenresIDObject:)
#NSManaged public func removeFromGenresID(_ value: GenresID)
#objc(addGenresID:)
#NSManaged public func addToGenresID(_ values: NSSet)
#objc(removeGenresID:)
#NSManaged public func removeFromGenresID(_ values: NSSet)
}
#objc(GenresID)
public class GenresID: NSManagedObject {
}
extension GenresID {
#nonobjc public class func fetchRequest() -> NSFetchRequest<GenresID> {
return NSFetchRequest<GenresID>(entityName: "GenresID")
}
#NSManaged public var id: Int64
#NSManaged public var name: String?
#NSManaged public var movieID: Movies?
}
When I click a button an action is triggered to save a movie, and then I would like to save that movie that has been "favored". A movie can have multiples genres (one-to-many relationship).
Method action:
func saveMoviesDB (movie: Movie) {
guard let appDelegate =
UIApplication.shared.delegate as? AppDelegate else {
return
}
let managedContext = appDelegate.persistentContainer.viewContext
let entityMovie = NSEntityDescription.entity(forEntityName: "Movies", in: managedContext)!
let entityGenres = NSEntityDescription.entity(forEntityName: "GenresID", in: managedContext)!
let movieDB = NSManagedObject(entity: entityMovie, insertInto: managedContext)
let genresDB = NSManagedObject(entity: entityGenres, insertInto: managedContext)
movieDB.setValue(movie.id, forKey: "id")
movieDB.setValue(movie.title, forKey: "title")
movieDB.setValue(movie.isFav, forKey: "isFav")
movieDB.setValue(movie.poster, forKey: "poster")
movieDB.setValue(movie.overview, forKey: "overview")
movieDB.setValue(movie.releaseDate, forKey: "release_date")
do{
try managedContext.save()
moviesDB.append(movieDB)
} catch let error as NSError {
print("Could not save. \(error), \(error.userInfo)")
}
}
I don't know how to assign multiple genresID to the Movie. For each movie, there is an Int array containing the Ids of the genres.
EDIT: I decided to remove the genre entity and create a transformable type property in Movies to store the arrays of Int. It went like this:
extension Movies {
#nonobjc public class func fetchRequest() -> NSFetchRequest<Movies> {
return NSFetchRequest<Movies>(entityName: "Movies")
}
#NSManaged public var id: Int64
#NSManaged public var isFav: Bool
#NSManaged public var overview: String?
#NSManaged public var poster: String?
#NSManaged public var release_date: String?
#NSManaged public var title: String?
#NSManaged public var genresID: [NSNumber]?
}
And then I cast it before saving it to the database
let genresID = movie.genre as [NSNumber]

How to save one-many relationship data into core data using Objective-C

I have a newsfeeditem and many users have shared this newsfeeditem.I need to save many users for one newsfeedItem.I had a one-many relationship from newsFeedItem to SlickUser.
but i don't know how I can save or relate SlickUser data to newsFeedItem. sorry im new to core data and im not able to save data using one to many relationship. if anyone can guide me how i can use SlickOwner to save data in newsFeedItem
for(int i =0 ; i < posts.user.count ; i++)
{
SlickUser * user = posts.user[i];
if (self.contactdb) {
[self.contactdb setValue:user.Aprovider forKey:#"Aprovider"];
[self.contactdb setValue:user.name forKey:#"name"];
[self.contactdb setValue:user.userid forKey:#"userid"];
}
else{
NSManagedObject *newdevice = [NSEntityDescription insertNewObjectForEntityForName:#"SlickUser" inManagedObjectContext:context];
[newdevice setValue:user.Aprovider forKey:#"Aprovider"];
[newdevice setValue:user.name forKey:#"name"];
[newdevice setValue:user.userid forKey:#"userid"];
NSError *error = nil;
if (![context sa[here is the image for newsFeedItem ][1]ve:&error])
{
NSLog(#"can't save %# %#", error, [error localizedDescription]);
}
}
}
here is newsFeedItem Entity :
extension NewsFeedDataItem {
#nonobjc public class func fetchRequest() -> NSFetchRequest<NewsFeedDataItem> {
return NSFetchRequest<NewsFeedDataItem>(entityName: "NewsFeedDataItem")
}
#NSManaged public var aprovider: String?
#NSManaged public var lid: String?
#NSManaged public var owner: NSSet?
}
xtension NewsFeedDataItem {
#objc(addOwnerObject:)
#NSManaged public func addToOwner(_ value: SlickUser)
#objc(removeOwnerObject:)
#NSManaged public func removeFromOwner(_ value: SlickUser)
#objc(addOwner:)
#NSManaged public func addToOwner(_ values: NSSet)
#objc(removeOwner:)
#NSManaged public func removeFromOwner(_ values: NSSet)
}
and this is UserSlick entity:
extension SlickUser {
#nonobjc public class func fetchRequest() -> NSFetchRequest<SlickUser> {
return NSFetchRequest<SlickUser>(entityName: "SlickUser")
}
#NSManaged public var aprovider: Int64
#NSManaged public var userid: String?
#NSManaged public var userPic: String?
#NSManaged public var newsFeedPost: NewsFeedDataItem?
}
this is the screenshort for slickUser
What you are missing is additional relationships between your SlickUser and NewsFeedDataItem. You have a single one-to-many relationship defined between your user and item - NewsFeedDataItem.owner -> SlickUser - and the inverse of that relationship SlickUser.newsFeedPost -> NewsFeedDataItem.
Here's the assumptions I'm making about what you are trying to achieve:
An Item is owned by a User [one-to-one]
An Item can can shared by many User [one-to-many]
A User can be the owner a many Item [one-to-many]
A User can share many Item [one-to-many]
What is defined here represents four (4) relationships between your User and Item.
Here's how I've configured everything:
NewsFeedDataItem
import Foundation
import CoreData
#objc(NewsFeedDataItem)
public class NewsFeedDataItem: NSManagedObject {
}
extension NewsFeedDataItem {
#nonobjc public class func fetchRequest() -> NSFetchRequest<NewsFeedDataItem> {
return NSFetchRequest<NewsFeedDataItem>(entityName: "NewsFeedDataItem")
}
#NSManaged public var aprovider: String?
#NSManaged public var lid: String?
#NSManaged public var owner: SlickUser?
#NSManaged public var sharedBy: NSSet?
}
// MARK: Generated accessors for sharedBy
extension NewsFeedDataItem {
#objc(addSharedByObject:)
#NSManaged public func addToSharedBy(_ value: SlickUser)
#objc(removeSharedByObject:)
#NSManaged public func removeFromSharedBy(_ value: SlickUser)
#objc(addSharedBy:)
#NSManaged public func addToSharedBy(_ values: NSSet)
#objc(removeSharedBy:)
#NSManaged public func removeFromSharedBy(_ values: NSSet)
}
SlickUser
import Foundation
import CoreData
#objc(SlickUser)
public class SlickUser: NSManagedObject {
}
extension SlickUser {
#nonobjc public class func fetchRequest() -> NSFetchRequest<SlickUser> {
return NSFetchRequest<SlickUser>(entityName: "SlickUser")
}
#NSManaged public var aprovider: Int64
#NSManaged public var userid: String?
#NSManaged public var userPic: String?
#NSManaged public var userPosts: NSSet?
#NSManaged public var sharedPosts: NSSet?
}
// MARK: Generated accessors for userPosts
extension SlickUser {
#objc(addUserPostsObject:)
#NSManaged public func addToUserPosts(_ value: NewsFeedDataItem)
#objc(removeUserPostsObject:)
#NSManaged public func removeFromUserPosts(_ value: NewsFeedDataItem)
#objc(addUserPosts:)
#NSManaged public func addToUserPosts(_ values: NSSet)
#objc(removeUserPosts:)
#NSManaged public func removeFromUserPosts(_ values: NSSet)
}
// MARK: Generated accessors for sharedPosts
extension SlickUser {
#objc(addSharedPostsObject:)
#NSManaged public func addToSharedPosts(_ value: NewsFeedDataItem)
#objc(removeSharedPostsObject:)
#NSManaged public func removeFromSharedPosts(_ value: NewsFeedDataItem)
#objc(addSharedPosts:)
#NSManaged public func addToSharedPosts(_ values: NSSet)
#objc(removeSharedPosts:)
#NSManaged public func removeFromSharedPosts(_ values: NSSet)
}
The SlickUser.userPosts is a set of Items that are owned by the User.
The SlickUser.sharedPosts is a set of Items that the User has shared.
The NewsFeedDataItem.owner is the owner of the Item.
The NewsFeedDataItem.sharedBy is a set of User that have shared the Item.
For each of the relationship pairs owner <> userPosts and sharedBy <> sharedPosts, you will need to set at least one side of the relationship on your managed object. CoreData will manage the rest.
Does this solution address the needs of your project?

Swift Core Data Many to Many relationship handling with codegen as Manual/None

I Have a many to many relationship from WishList to Product. I made codegen property as Manual/None, so I have created respected classes as below.
class Product: NSManagedObject {
struct constants {
static let ID = "id"
static let NAME = "name"
static let DESCRIPTION = "description"
static let wishLists = "shoppingLists"
}
}
extension Product {
#NSManaged public var id: Int64
#NSManaged public var name: String
#NSManaged public var desc: String
#NSManaged public var wishLists: NSOrderedSet?
}
class WishList: NSManagedObject {
struct constants {
static let ID = "id"
static let NAME = "name"
static let PRODUCT = "products"
}
func addProducts(_values : NSMutableOrderedSet) {
}
func removeProducts(_values : NSMutableOrderedSet) {
}
func addProduct(_values : Product) {
}
func removeProduct(_values : Product) {
}
}
extension WishList {
#NSManaged public var id: Int64
#NSManaged public var name: String
#NSManaged public var products: NSOrderedSet?
}
What I need is, How do I implement convenience methods to achieve below functionalities, since core data does not create on behalf of me because I made codegen as Manual/None.
User can add a product into multiple wish lists
Each wishList can have multiple products.
With Codegen set to Manual/None, you're free to write your own implementation or let Xcode generate an appropriate subclass for you. (Editor -> Create NSManagedObject Subclass...) All the helper methods would be readily available there. (assuming NSOrderedSet)
extension WishList {
#objc(insertObject:inProductsAtIndex:)
#NSManaged public func insertIntoProducts(_ value: Product, at idx: Int)
#objc(removeObjectFromProductsAtIndex:)
#NSManaged public func removeFromProducts(at idx: Int)
#objc(insertProducts:atIndexes:)
#NSManaged public func insertIntoProduct(_ values: [Product], at indexes: NSIndexSet)
#objc(removeProductsAtIndexes:)
#NSManaged public func removeFromProducts(at indexes: NSIndexSet)
#objc(replaceObjectInProductsAtIndex:withObject:)
#NSManaged public func replaceProducts(at idx: Int, with value: Product)
#objc(replaceProductsAtIndexes:withProducts:)
#NSManaged public func replaceProducts(at indexes: NSIndexSet, with values: [Product])
#objc(addProductsObject:)
#NSManaged public func addToProducts(_ value: Product)
#objc(removeProductsObject:)
#NSManaged public func removeFromProducts(_ value: Product)
#objc(addProducts:)
#NSManaged public func addToProducts(_ values: NSOrderedSet)
#objc(removeProducts:)
#NSManaged public func removeFromProducts(_ values: NSOrderedSet)
}
...and vice-versa for Product and its wishlists.

Resources