To Many Relationship in Core Data - objective-c

This is my GeneratedCode for Core data, automatically generated by xCode Editor.
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#class Building, Category, City, District, Image, LatitudeLongitude, OpeningHour, Promotion, Rating, Review, URL;
#interface Business : NSManagedObject {
#private
}
#property (nonatomic, retain) NSString * Website;
#property (nonatomic, retain) NSString * Email;
#property (nonatomic, retain) NSString * Street;
#property (nonatomic, retain) NSString * InBuildingAddress;
#property (nonatomic, retain) NSString * Phone;
#property (nonatomic, retain) NSString * Title;
#property (nonatomic, retain) NSString * Zip;
#property (nonatomic, retain) NSNumber * Price;
#property (nonatomic, retain) NSSet* Promotions;
#property (nonatomic, retain) Building * Building;
#property (nonatomic, retain) NSSet* Categories;
#property (nonatomic, retain) LatitudeLongitude * LatitudeLongitude;
#property (nonatomic, retain) NSSet* Images;
#property (nonatomic, retain) OpeningHour * OpeningHour;
#property (nonatomic, retain) NSSet* Reviews;
#property (nonatomic, retain) NSSet* URLs;
#property (nonatomic, retain) Rating * Rating;
#property (nonatomic, retain) NSSet* Districts;
#property (nonatomic, retain) City * City;
//I added these 3 lines, why not part of automatically generated Code?
- (void)addDistrictsObject:(District *)value;
- (void)addCategoriesObject:(Category *)value;
- (void)addReviewsObject:(Review *)value;
#end
Say I want to "clear" all Reviews and image.
Will doing self.Reviews=nil do the trick?
I know that doing self.LatitudeLongitude=nil will delete the relationship between self and it's LatitutudeLongitude.

EDIT - Not sure if you mean "clear" as in delete. My answer below is assuming that you want the objects gone from the context; not just severing the relationship between the NSManagedObjects
I don't believe
self.Reviews = nil;
will actually delete your Review objects for the Managed Object Context. That would send the release message to each one, but to delete them from the context you have to call
[aContext deleteObject:reviewObj];
on each one in the set. If you were to delete one of your Business objects (using "deleteObject" as shown above) and you had your relationship delete rules set to "Cascade", then I believe that would cause all of the Review, City, etc... objects owned by that Business object to be deleted automatically, but that is the only other way that I know of that will cause NSManagedObjects to be deleted.

Related

Property requires method to be defined in NSManagedObject subclass

I have some protocol like this:
#protocol UserProtocol <NSObject>
#property (nonatomic, strong) NSNumber *uid;
#property (nonatomic, strong) NSString *name;
#property (nonatomic, strong) NSNumber *rating;
#end
Then I created some actual class that implements that:
#interface User : NSObject <UserProtocol>
#end
Now I need another implementation that uses CoreData so I created CDUser entity (Xcode also generates category for that):
// CDUser.h
#interface CDUser : NSManagedObject <UserProtocol>
#end
// CDUser+CoreDataProperties.h
#interface CDUser (CoreDataProperties)
#property (nullable, nonatomic, retain) NSNumber *uid;
#property (nullable, nonatomic, retain) NSString *name;
#property (nullable, nonatomic, retain) NSNumber *rating;
#end
// CDUser+CoreDataProperties.m
#implementation CDUser (CoreDataProperties)
#dynamic uid;
#dynamic name;
#dynamic rating;
#end
CDUser actually implements UserProtocol but I have warnings like so for all properties:
Property 'uid' requires method 'uid' to be defined - use #synthesize, #dynamic or provide a method implementation in this class implementation
If I add #dynamic uid; again in CDBook.m then I get the following error:
Property declared in category 'CoreDataProperties' cannot be implemented in class implementation
How can I solve these warnings in a proper way?
Cause CDUser doesn't implement this protocol. Use protocol on category instead.
#interface CDUser : NSManagedObject
#end
// CDUser+CoreDataProperties.h
#interface CDUser (CoreDataProperties) <UserProtocol>
#property (nullable, nonatomic, retain) NSNumber *uid;
#property (nullable, nonatomic, retain) NSString *name;
#property (nullable, nonatomic, retain) NSNumber *rating;
#end

CoreData How to connect with relationship entity to another entity with already preloaded data?

This is my first application in CoreData so I definitely missing something here, but I spend over a week trying to find solution and apparently nobody on Internet had the same problem ;)
I'm creating something like deckbuilder app.:
My model looks like this: coredatamodel
The relationship is to-one and inverse to-many ( every card can be in one set, and in every set can be many cards) from CARD to SET.
For sake of simplicity I want to focus on CARD and SET entities.
I preloaded data into SET from CSV file into 5 atributes - didnt fill relationship "karty" because I didn't know how to do this. There are about 25 records in this entity.
And now, problem is I am trying to fill CARDS Entity which will have over 500 records.
Trying few options I found on SO I end with connection that created 500 records in SET too:/ so it looked like sql "JOIN" command.
What I want is load data to CARDS and connect them somehow to SET not changing number of records in SET.
If i have property (nonatomic, retain) NSSet *karty; what exactly NSSet means? It is s et but set of what? Set of single rows from Card entity? Set of Card objects? Set of NSStrings? Set of NSManagedObjects?
In normal SQL primary key to connect them would be "nazwaszort" so Card.nazwaszort=Set.nazwaszort.
My Set.h
#class Card;
#interface Set : NSManagedObject
#property (nonatomic, retain) NSNumber * cykl;
#property (nonatomic, retain) NSNumber * czymajor;
#property (nonatomic, retain) NSString * nazwa;
#property (nonatomic, retain) NSString * nazwashort;
#property (nonatomic, retain) NSNumber * nrwcyklu;
#property (nonatomic, retain) NSSet *karty;
#end
#interface Set (CoreDataGeneratedAccessors)
- (void)addKartyObject:(Card *)value;
- (void)removeKartyObject:(Card *)value;
- (void)addKarty:(NSSet *)values;
- (void)removeKarty:(NSSet *)values;
#end
My Card.h
#class Frakcja, Kolekcja, Set;
#interface Card : NSManagedObject
#property (nonatomic, retain) NSNumber * czylimit;
#property (nonatomic, retain) NSString * frakcja;
#property (nonatomic, retain) NSString * icesila;
#property (nonatomic, retain) NSNumber * iloscwsecie;
#property (nonatomic, retain) NSNumber * influence;
#property (nonatomic, retain) NSString * kodkarty;
#property (nonatomic, retain) NSNumber * koszt;
#property (nonatomic, retain) NSNumber * minimumdecksize;
#property (nonatomic, retain) NSString * nazwa;
#property (nonatomic, retain) NSString * nazwasetu;
#property (nonatomic, retain) NSString * nazwaszort;
#property (nonatomic, retain) NSNumber * nrcyklu;
#property (nonatomic, retain) NSString * podtyp;
#property (nonatomic, retain) NSString * strona;
#property (nonatomic, retain) NSNumber * trashkoszt;
#property (nonatomic, retain) NSString * typ;
#property (nonatomic, retain) Kolekcja *ilewkolekcja;
#property (nonatomic, retain) Frakcja *nazwafrakcji;
#property (nonatomic, retain) Set *wjakimsecie;
#end
I preloaded cards to SET using method
-(void) preloadDataPackInfoToDatabase {
NSError *error=nil;
NSString *sciezka = [[NSBundle mainBundle]pathForResource:#"nrsets" ofType:#"csv"];
NSArray *rows = [NSArray arrayWithContentsOfCSVFile:sciezka];
for (int i=1; i <=([rows count]-1); i++) {
Set *nowyDataPack = [NSEntityDescription insertNewObjectForEntityForName:[entityset name] inManagedObjectContext:_contextdp];
NSLog(#"tablica wierszy %#",rows[i][2]);
NSString *koddodatku = rows[i][2];
NSLog(#"kod dodatku:%#",koddodatku);
NSString *nrwcyklu = rows[i][4];
NSString *nrcyklu = rows[i][3];
NSString *nazwadatapack =rows[i][3];
NSString *czymajor =rows[i][0];
[nowyDataPack setValue:nazwadatapack forKey:#"nazwa"];
NSNumberFormatter *f = [[NSNumberFormatter alloc]init];
[f setNumberStyle:NSNumberFormatterDecimalStyle];
NSNumber *nrwcykluint = [f numberFromString:nrwcyklu];
NSNumber *nrcykluint = [f numberFromString:nrcyklu];
NSNumber *czymajorbool = [f numberFromString:czymajor];
[nowyDataPack setValue:nrwcykluint forKey:#"nrwcyklu"];
[nowyDataPack setValue:nrcykluint forKey:#"cykl"];
[nowyDataPack setValue:koddodatku forKey:#"nazwashort"];
[nowyDataPack setValue:czymajorbool forKey:#"czymajor"];
}
if (![ self.contextdp save:&error]) {
NSLog(#"Nieznany błąd %#,%#",error,[error userInfo]);
}
}
And another method (on pastebin to not flood your screens --> preloadAllCardsToDatabase
Thanks in advance for any help.
OK I finally did it. Had to move some CoreData stuff outside loop.
Still dont understand this whole magic but at least it works.
for those interested this is my preloading function --> here

RestKit 0.20 mapping string array

So after reading this answer, I do not see how my case is any different, but the proposed solution just doesn't seem to work in RestKit 0.20.3
For the following json:
{
"photos": [
"872ac577-3f31-47a0-966e-6f2ed2fbabcd"
],
"imageUrl": [
"http://domain/WebAPI/FileUpload/10232013629_21382571406.439790.jpeg"
]
}
Mapping into the following object:
#class Picture, Site, Survey;
#interface Job : NSManagedObject
#property (nonatomic, retain) NSString * jobId;
#property (nonatomic, retain) NSString * jobName;
#property (nonatomic, retain) NSString * jobStatusId;
#property (nonatomic, retain) NSString * jobStatus;
#property (nonatomic, retain) NSString * projectId;
#property (nonatomic, retain) NSString * projectType;
#property (nonatomic, retain) NSString * siteId;
#property (nonatomic, retain) NSSet *surveys;
#property (nonatomic, retain) NSSet *photos;
#property (nonatomic, retain) NSDate *createdDate;
#property (nonatomic, retain) Site *site;
#end
Where photos is a Picture object that looks like this:
#interface Picture : NSManagedObject
#property (nonatomic, retain) NSData * data;
#property (nonatomic, retain) NSString * photoId;
#property (nonatomic, retain) NSString * pictureLoc;
#property (nonatomic, retain) NSString * imageUrl;
#property (nonatomic, retain) Job *job;
#property (nonatomic, retain) QuestionAnswer *question;
#end
I want to map the photos and imageUrl into the Picture's photoId and imageUrl, respectively. I thought I could use the following in my Job's mapping:
RKEntityMapping *ret = [RKEntityMapping mappingForEntityForName:#"Job" inManagedObjectStore:[RKObjectManager sharedManager].managedObjectStore ];
[ret addAttributeMappingsFromDictionary: #{
#"photos": #"photos.photoId",
#"imageUrl": #"photos.imageUrl",
}];
However, the mapped Job always ends up with nothing in the photos property.
I know that I am doing all the in-between steps correctly (like actually using ret for the mapping), as the JSON is a snippet that contains many other keys I am able to map with no problem. What am I doing wrong? please tell me
EDIT
Changed the mapping code as per Wain's answer:
RKEntityMapping *ret = [RKEntityMapping mappingForEntityForName:#"Job" inManagedObjectStore:[RKObjectManager sharedManager].managedObjectStore ];
RKEntityMapping *subMapping = [RKEntityMapping mappingForEntityForName:#"Picture" inManagedObjectStore:[RKObjectManager sharedManager].managedObjectStore ];
[subMapping addAttributeMappingsFromDictionary: #{
#"photos":#"photoId",
#"imageUrl":#"imageUrl"
}];
[ret addPropertyMapping:[RKRelationshipMapping
relationshipMappingFromKeyPath:nil
toKeyPath:#"photos"
withMapping:subMapping]];
And now I get a Picture object added, but with nil for photoId and imageUrl (and yes, I checked to make sure I have the spelling and capitalization correct for the JSON keys)
The reason photos never has anything in it is that your mapping doesn't tell it to create a new object.
The problem is how you could tell it to create a single object and set both of the parameters into it. The problem is because you have 2 different arrays. The JSON I would expect you to have is 1 array which contains dictionaries with all of the details for that particular entry. RestKit can process both of the arrays but it would result in 2 distinct photo objects being created. To do this you need a second mapping with a nil key path and a relationship from ret to that new mapping.
An alternative could be to change the Job object so that it has 2 transformable (or transient) properties which are NSArrays. If you set ret to just map #"photos" : #"photos" and #"imageUrl" : #"imageUrl" then RestKit will map all of the data, just not into a photo object. You can then mutate this data after the mapping has completed if you want to...

Unrecognized selector sent to instance: key-value pair

I get
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSDictionaryM metadata]: unrecognized selector sent to instance 0xd0b6250'
when i reach and execute this part of my code:
IDTherapyMetadata* meta = [self.dataCon getMetaDataFromId:content.metadata];
the method in dataCon looks like this:
-(IDTherapyMetadata*)getMetaDataFromId:(NSString*)metaDataId
{
for (IDTherapyMetadata* metadata in self.parser.metadata)
{
if([metaDataId compare:metadata.objId] == NSOrderedSame)
{
return metadata;
}
}
return nil;
}
The strange thing is, if I write
IDTherapyMetadata* meta = [self.dataCon getMetaDataFromId:[content valueForKey:#"metadata"]];
instead it works just fine.
However I want to be able to use dotnotation and looking through apple's documentation
https://developer.apple.com/library/ios/documentation/cocoa/conceptual/KeyValueCoding/Articles/BasicPrinciples.html
haven't helped me.
Why i'm I getting this behaviour and how do I fix it?
Any help is greatly appreciated
EDIT:
The code for IDTherapyContents.h:
#import <Foundation/Foundation.h>
#interface IDTherapyContents : NSObject
#property (nonatomic, copy)NSString* urlId;
#property (nonatomic, copy)NSString* url;
#property (nonatomic, copy)NSString* type;
#property (nonatomic, copy)NSString* timestamp;
#property (nonatomic, copy)NSString* metadata;
#property (nonatomic, copy)NSString* lang;
#property (nonatomic, copy)NSString* objId;
//methods
-(id)initWithJsonDic:(NSDictionary*)jsonDic;
#end
Code for IDTherpayMetaData.h
#import <Foundation/Foundation.h>
#import "IDTherapyImages.h"
#interface IDTherapyMetadata : NSObject
//Array contains 'String' objects
#property (nonatomic, strong)NSMutableArray* thumbnails;
#property (nonatomic, copy)NSString* title;
#property (nonatomic, copy)NSString* timestamp;
#property (nonatomic, copy)NSString* subCategory;
#property (nonatomic, copy)NSString* lang;
#property (nonatomic, copy)NSString* objId;
#property (nonatomic, copy)NSString* description5;
#property (nonatomic, copy)NSString* description4;
#property (nonatomic, copy)NSString* description3;
#property (nonatomic, copy)NSString* description2;
#property (nonatomic, copy)NSString* description1;
#property (nonatomic, copy)NSString* category;
//Array contains 'IDTherapyImages' objects
#property (nonatomic, strong)NSMutableArray* images;
//methods
-(id)initWithJsonDic:(NSDictionary*)jsonDic;
#end
Code for IDTherapyProducts.h:
#interface IDTherapyProducts : NSObject
//Array contains 'String' objects
#property (nonatomic, strong)NSMutableArray* contents;
#property (nonatomic, copy)NSString* uniqueId;
#property (nonatomic, copy)NSString* timestamp;
#property (nonatomic, copy)NSString* price;
#property (nonatomic, copy)NSString* posted;
#property (nonatomic, copy)NSString* normalPrice;
#property (nonatomic, copy)NSString* metadata;
#property (nonatomic, copy)NSString* lang;
#property (nonatomic, copy)NSString* objId;
#property (nonatomic, copy)NSString* badge;
//methods
-(id)initWithJsonDic:(NSDictionary*)jsonDic;
#end
This is a simple case of confusing properties with KVC. They are different things.
content.metadata is nothing more than syntactic sugar for [content metadata]. So you need a method on content called -metadata and as content is an NSDictionary that method doesn't exist. So that's why the exception is thrown.
On any normal object [content valueForKey: #"metadata"] would also throw an exception because KVC looks for a method with that name. However, on NSDictionary -valueForKey: is overridden to call -objectForKey: unless the key starts with an # in which case the # is stripped and [super valueForKey: ...] is invoked.
However I want to be able to use dotnotation
Then you need a method on NSDictionary called -metadata. You could use a category.
#interface NSDictionary(MyMetadataExtension)
-(id) metadata;
#end
#implementation NSDictionary(MyMetadataExtension)
-(id) metadata
{
return [self objectForKey: #"metadata"];
}
#end

Expected specifier-qualifier-list before '*' token Objective C

Would anyone be kind enough to explain to me what I'm doing wrong. I'm giving it :
#import <Foundation/Foundation.h>
#interface Product : NSObject {
NSString *imageAddress;
NSString *name;
NSString *title;
}
#property (nonatomic, retain) *imageAddress;
#property (nonatomic, retain) *name;
#property (nonatomic, retain) *product;
#end
and its giving me:
Expected specifier-qualifier-list
before '*' token
for the property calls.
Thanks
This is gcc's cryptic way of telling you that you need a type for your properties.
#property (nonatomic, retain) NSString *imageAddress;
#property (nonatomic, retain) NSString *name;
#property (nonatomic, retain) NSString *product;