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]
Related
I have this core data issue:
*** -[NSKeyedUnarchiver decodeObjectForKey:]: value for key (id) is not an object. This will become an error in the future.
First I would like to describe what I am trying to do:
I perform a GET request to my API, get an array of elements and add the data I got to Core Data.
Here is my CoreData Entity:
extension PetEntity {
#nonobjc public class func fetchRequest() -> NSFetchRequest<PetEntity> {
return NSFetchRequest<PetEntity>(entityName: "PetEntity")
}
#NSManaged public var category: CategoryDTO?
#NSManaged public var id: Int64
#NSManaged public var name: String?
#NSManaged public var photoUrls: [String]?
#NSManaged public var status: String?
#NSManaged public var tags: [TagDTO]?
}
extension PetEntity : Identifiable {
}
My DTO I use to get Json data in and then map it to my model:
struct PetDTO: Codable {
var id: Int
var category: CategoryDTO?
var name: String?
var photoUrls: [String]?
var tags: [TagDTO]?
var status: StatusDTO
}
public class CategoryDTO: NSObject, Codable {
var id: Int
var name: String?
public enum CodingKeys: String, CodingKey {
case id
case name
}
public required init?(coder: NSCoder) {
id = coder.decodeObject(forKey: CodingKeys.id.rawValue) as? Int ?? 0 // error here
name = coder.decodeObject(forKey: CodingKeys.name.rawValue) as? String
}
}
extension CategoryDTO: NSSecureCoding{
public static var supportsSecureCoding: Bool{
return true
}
public func encode(with coder: NSCoder) {
coder.encode(id, forKey: CodingKeys.id.rawValue)
coder.encode(name, forKey: CodingKeys.name.rawValue)
}
}
#objc(CategoryDtoTransformer)
public final class CategoryDtoTransformer: NSSecureUnarchiveFromDataTransformer {
public static let name = NSValueTransformerName(rawValue: String(describing: CategoryDtoTransformer.self))
public override static var allowedTopLevelClasses: [AnyClass] {
return [CategoryDTO.self, NSString.self, NSArray.self]
}
#objc dynamic
public static func register() {
let transformer = CategoryDtoTransformer()
ValueTransformer.setValueTransformer(transformer, forName: name)
}
}
public class TagDTO: NSObject, Codable {
var id: Int
var name: String?
public enum CodingKeys: String, CodingKey {
case id
case name
}
public required init?(coder: NSCoder) {
id = coder.decodeObject(forKey: CodingKeys.id.rawValue) as? Int ?? 0 // Error here
name = coder.decodeObject(forKey: CodingKeys.name.rawValue) as? String
}
}
extension TagDTO: NSSecureCoding{
public static var supportsSecureCoding: Bool{
return true
}
public func encode(with coder: NSCoder) {
coder.encode(id, forKey: CodingKeys.id.rawValue)
coder.encode(name, forKey: CodingKeys.name.rawValue)
}
}
#objc(TagDtoTransformer)
public final class TagDtoTransformer: NSSecureUnarchiveFromDataTransformer {
public static let name = NSValueTransformerName(rawValue: String(describing: TagDtoTransformer.self))
public override static var allowedTopLevelClasses: [AnyClass] {
return [TagDTO.self, NSString.self, NSArray.self]
}
#objc dynamic
public static func register() {
let transformer = TagDtoTransformer()
ValueTransformer.setValueTransformer(transformer, forName: name)
}
}
enum StatusDTO: String, Codable {
case available
case sold
case pending
}
And here is my CRUD code for adding to database:
class CoreDataPetPersistance: PetPerstitenceProtocol {
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
func add(pet: PetDTO) {
let newPet = PetEntity(context: self.context)
newPet.id = Int64(pet.id)
newPet.name = pet.name
newPet.category = pet.category
newPet.photoUrls = pet.photoUrls
newPet.tags = pet.tags
newPet.status = pet.status.rawValue
do {
try self.context.save()
} catch {
print(error)
}
}
}
Also I have added constraints to my ID to make each pet I get from API unique and registered CategoryDTO and TagDTO in app delegate. It should be fine.
Full error I get:
CoreData: error: -executeRequest: encountered exception = Error Domain=NSCocoaErrorDomain Code=133000 "Attempt to access an object not found in store." UserInfo={objectID=0xa57351074a65fcb3 <x-coredata://FEB02FEE-9B8B-4785-82A1-263F82D4CDBC/PetEntity/p1095>} with userInfo = {
NSCocoaErrorDomain = 133000;
NSUnderlyingError = "Error Domain=NSCocoaErrorDomain Code=133000 \"Attempt to access an object not found in store.\" UserInfo={objectID=0xa57351074a65fcb3 <x-coredata://FEB02FEE-9B8B-4785-82A1-263F82D4CDBC/PetEntity/p1095>}";
}
Please help me fix this and explain why this error happens
Thanks
There are dedicated APIs for scalar types
id = coder.decodeInt64(forKey: CodingKeys.id.rawValue)
Have a look for all available APIs
To match the types declare id as Int64 or convert the type.
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?
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
I just inherited this app and I am trying to add a few fields to a form.
I am receiving this error - it seems to be calling a setPrice method on MedicalDevice.
Now as far as I can tell, I've added price to the class that is expecting it...
#objc(MedicalDevice)
public class MedicalDevice: NSManagedObject {
static let entityName = "MedicalDevice"
#nonobjc public class func fetchRequest() -> NSFetchRequest<MedicalDevice> {
return NSFetchRequest<MedicalDevice>(entityName: MedicalDevice.entityName)
}
convenience init(context moc: NSManagedObjectContext) {
self.init(entity: NSEntityDescription.entity(forEntityName: MedicalDevice.entityName, in: moc)!, insertInto: moc)
}
#NSManaged public var createdAt: Date?
#NSManaged public var deviceID: String?
#NSManaged public var expirationDate: Date?
#NSManaged public var lotNumber: String?
#NSManaged public var modelNumber: String?
#NSManaged public var catalogNumber: String?
#NSManaged public var deviceDescription: String?
#NSManaged public var brandName: String?
#NSManaged public var companyName: String?
#NSManaged public var quantity: String?
#NSManaged public var price: String?
#NSManaged public var location: String?
#NSManaged public var facilityID: String?
}
And addDevice works as such - this is where the exception is being thrown:
func addDevice(for deviceID: String, lotNumber: String, expirationDate: Date, modelNumber: String, catalogNumber: String, description: String, quantity: String, brandName: String, companyName: String, price: String, location: String, facilityID: String) {
let medicalDevice = MedicalDevice(context: self.persistentContainer.viewContext)
medicalDevice.createdAt = Date()
medicalDevice.deviceID = deviceID
medicalDevice.lotNumber = lotNumber
medicalDevice.expirationDate = expirationDate
medicalDevice.modelNumber = modelNumber
medicalDevice.catalogNumber = catalogNumber
medicalDevice.deviceDescription = description
medicalDevice.quantity = quantity
medicalDevice.brandName = brandName
medicalDevice.companyName = companyName
medicalDevice.price = price
medicalDevice.location = location
medicalDevice.facilityID = facilityID
self.saveContext()
}
Now, I don't see any setPrice method (which makes sense, I just added it in the various fields), but I also don't see setX, with X being any of the other fields which were definitely saving beforehand.
What could be causing this? Obviously setPrice is somehow being called, somewhere (generated in some way?) and it doesn't exist, I am guessing.
I have six entities in my xcdatamodeld. Article, Section, References, Bookmarks, BookmarkReferences and BookmarkSections. I am trying to save the bookmarked articles separately to the Bookmarks entity. The Article has inverse set to Sections and References similarly Bookmarks has inverse set to BookmarkReferences and BookmarkSections. When i try to set the values from Article NSManagedObject to Bookmarks NSManagedObj i am getting KVC error. I have made sure that there are no typos in the key names. Have a look at the code and log.
extension Bookmarks {
#nonobjc public class func fetchRequest() -> NSFetchRequest<Bookmarks> {
return NSFetchRequest<Bookmarks>(entityName: "Bookmarks");
}
#NSManaged public var id: String?
#NSManaged public var status: String?
#NSManaged public var createdBy: String?
#NSManaged public var articleType: String?
#NSManaged public var modifiedBy: String?
#NSManaged public var tag: String?
#NSManaged public var sendNotification: Bool
#NSManaged public var bookmark: Bool
#NSManaged public var isRead: Bool
#NSManaged public var isArticleFromSearch: Bool
#NSManaged public var searchString: String?
#NSManaged public var isArticleFromCategory: Bool
#NSManaged public var modifiedDate: String?
#NSManaged public var createdDate: String?
#NSManaged public var category: String?
#NSManaged public var subCategory: String?
#NSManaged public var title: String?
#NSManaged public var general: NSDictionary?
#NSManaged public var bookmarkSections: NSSet?
#NSManaged public var bookmarkReferences: NSSet?
}
extension BookmarkSections {
static func saveSectionsForBookmarks(entity: Article, privateContext: NSManagedObjectContext) -> NSSet {
let sections = entity.sections
let sectionED = NSEntityDescription.entity(forEntityName: "BookmarkSections", in: privateContext)
let sectionMO = NSManagedObject(entity: sectionED!, insertInto: privateContext)
let sectionsSet = NSMutableSet()
let allSectionItems = sections?.allObjects
for (_, sectionItem) in (allSectionItems?.enumerated())! {
let castedSection = sectionItem as? Sections
sectionMO.setValue(castedSection!.id, forKey: "id")
sectionMO.setValue(castedSection!.text, forKey: "text")
sectionMO.setValue(castedSection!.imageUrl, forKey: "imageUrl")
sectionMO.setValue(castedSection!.imageName, forKey: "imageName")
sectionMO.setValue(castedSection!.title, forKey: "title")
sectionsSet.add(sectionMO)
}
return sectionsSet
}
}
[`
extension Bookmarks {
public static func saveToNew(Entity: Article) {
let managedContext = CoreDataStorage.sharedInstance.privateQueueContext
let entityDesc =
NSEntityDescription.entity(forEntityName: "Bookmarks",
in: managedContext)!
let articleMO = NSManagedObject(entity: entityDesc,
insertInto: managedContext)
articleMO.setValue(Entity.id, forKey: "id")
articleMO.setValue(Entity.status, forKey: "status")
articleMO.setValue(Entity.createdBy, forKey: "createdBy")
articleMO.setValue(Entity.articleType, forKey: "articleType")
articleMO.setValue(Entity.modifiedBy, forKey: "modifiedBy")
let isSendNotification=NSNumber(value: false)
articleMO.setValue(isSendNotification, forKey: "sendNotification")
let modifiedDate = String(describing: Entity.modifiedDate)
articleMO.setValue(modifiedDate, forKey: "modifiedDate")
let createdDate = String(describing: Entity.createdDate)
articleMO.setValue(createdDate, forKey: "createdDate")
articleMO.setValue(Entity.category, forKey: "category")
articleMO.setValue(Entity.subCategory, forKey: "subCategory")
let general = NSMutableDictionary()
let dict = Entity.general
for (key,subJson):(Any, Any) in dict! {
general.setValue(subJson as! String, forKey: key as! String)
}
articleMO.setValue(general, forKey: "general")
articleMO.setValue(Entity.title, forKey: "title")
articleMO.setValue(BookmarkSections.saveSectionsForBookmarks(entity: Entity, privateContext: managedContext), forKey: "bookmarkSections")
articleMO.setValue(Entity.references, forKey: "references")
do {
try managedContext.save()
} catch let error as NSError {
print("Could not save. \(error), \(error.userInfo)")
}
}
`]
Error: