Core Data relationship to same entity - ios

I have been using archiving and I am switching to Core Data--however, I don't know how to implement a certain entity that I want to add to the database.
Here is how I would have written the header file not using Core Data...
#interface SELCase : NSObject <NSCoding>
#property (nonatomic) NSString *name;
#property (nonatomic) NSString *caseKey;
#property (nonatomic) BOOL individual;
#property (nonatomic) float indivAmount;
#property (nonatomic) SELCase *indivType;
#end
Data that would be loaded every time would be the name, caseKey, and individual. If individual was YES then that would be the end of it, but if it wasn't an individual then indivAmount and indivType would be loaded.
The problem is that I need a relationship to another SELCase and I have no idea how to do that. Any help?

Just create a relationship that points to the same entity.
Here's an example:
#property (nonatomic, retain) NSString * goalName;
#property (nonatomic, retain) NSData * goalPicture;
#property (nonatomic, retain) NSNumber * indexOrder;
#property (nonatomic, retain) NSNumber * wordPicture;
#property (nonatomic, retain) NSSet *item;
#property (nonatomic, retain) Goal *parent;
#property (nonatomic, retain) NSSet *subGoals;
#property (nonatomic, retain) User *user;
#property (nonatomic, retain) NSSet *video;
#property (nonatomic, retain) NSSet *stats;
#end
#interface Goal (CoreDataGeneratedAccessors)
- (void)addItemObject:(Item *)value;
- (void)removeItemObject:(Item *)value;
- (void)addItem:(NSSet *)values;
- (void)removeItem:(NSSet *)values;
- (void)addSubGoalsObject:(Goal *)value;
- (void)removeSubGoalsObject:(Goal *)value;
- (void)addSubGoals:(NSSet *)values;
- (void)removeSubGoals:(NSSet *)values;
- (void)addVideoObject:(Video *)value;
- (void)removeVideoObject:(Video *)value;
- (void)addVideo:(NSSet *)values;
- (void)removeVideo:(NSSet *)values;
- (void)addStatsObject:(Stats *)value;
- (void)removeStatsObject:(Stats *)value;
- (void)addStats:(NSSet *)values;
- (void)removeStats:(NSSet *)values;
In this case, the subGoals is a set consisting of the same type of entity.
In your model you'll just make a relationship (one/one, one/many, whatever) that points to the same entity.

Related

Objective-C accessing singleton's property causing EXC_BAD_ACCESS crash

I've got a singleton class KTTTeacherService that has a single property currentTeacher. This property is nil initially and is updated with the static method KTTTeacherService#updateCurrentTeacher. After currentTeacher is updated, when I try to access it, my app crashes with a EXC_BAD_ACCESS error.
KTTTeacherService.h
#import <Foundation/Foundation.h>
#import "KTTeacher.h"
#interface KTTTeacherService : NSObject
#property (nonatomic, assign) KTTeacher* currentTeacher;
+ (KTTTeacherService*)shared;
+ (KTTeacher*)currentTeacher;
+ (NSString*)apiKey;
+ (void)updateCurrentTeacher:(KTTeacher*) teacher;
- (KTTeacher*)retrieveCurrentTeacher;
#end
KTTTeacherService.m
#import "KTTTeacherService.h"
#import "KTTeacher.h"
static KTTTeacherService* _singleton = nil;
#implementation KTTTeacherService;
+ (KTTTeacherService*) shared {
if(_singleton == nil) {
_singleton = [[KTTTeacherService alloc] init];
}
return _singleton;
}
+ (KTTeacher*) currentTeacher {
KTTTeacherService* shared = [KTTTeacherService shared];
if(shared == nil) {
return nil;
}
return [shared retrieveCurrentTeacher];
}
+ (NSString*) apiKey {
KTTeacher* teacher = [KTTTeacherService currentTeacher];
return teacher == nil ? nil : teacher.apiKey;
}
+ (void) updateCurrentTeacher:(KTTeacher*) teacher {
[KTTTeacherService shared].currentTeacher = teacher;
}
- (KTTeacher*) retrieveCurrentTeacher {
return _currentTeacher;
}
#end
KTTeacher.h
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#class KTClass, KTGroup, KTTeacherNote;
#interface KTTeacher : NSManagedObject
#property (nonatomic, strong) NSString * apiKey;
#property (nonatomic, strong) NSNumber * serverIdentifier;
#property (nonatomic, strong) NSString * firstName;
#property (nonatomic, strong) NSString * lastName;
#property (nonatomic, strong) NSString * photoPathOnServer;
#property (nonatomic, strong) NSString * username;
#property (nonatomic, strong) NSString * hashedPassword;
#property (nonatomic, strong) NSString * salt;
#property (nonatomic, strong) NSSet *classes;
#property (nonatomic, strong) NSSet *groups;
#property (nonatomic, strong) NSSet *notes;
#end
#interface KTTeacher (CoreDataGeneratedAccessors)
- (void)addClassesObject:(KTClass *)value;
- (void)removeClassesObject:(KTClass *)value;
- (void)addClasses:(NSSet *)values;
- (void)removeClasses:(NSSet *)values;
- (void)addNotesObject:(KTTeacherNote *)value;
- (void)removeNotesObject:(KTTeacherNote *)value;
- (void)addNotes:(NSSet *)values;
- (void)removeNotes:(NSSet *)values;
- (void)addGroupsObject:(KTGroup *)value;
- (void)removeGroupsObject:(KTGroup *)value;
- (void)addGroups:(NSSet *)values;
- (void)removeGroups:(NSSet *)values;
#end
KTTeacher.m
#import "KTTeacher.h"
#import "KTClass.h"
#import "KTTeacherNote.h"
#implementation KTTeacher
#dynamic apiKey;
#dynamic serverIdentifier;
#dynamic firstName;
#dynamic lastName;
#dynamic photoPathOnServer;
#dynamic username;
#dynamic hashedPassword;
#dynamic salt;
#dynamic classes;
#dynamic groups;
#dynamic notes;
#end
Where the crash happens:
KTTTeacherService* teacherService = [KTTTeacherService shared];
KTTeacher* currentTeacher = teacherService.currentTeacher;
Specifically here: teacherService.currentTeacher
Why is this happening? After doing some googling, I found some info about this error being caused by accessing references that have been garbage collected, but I don't understand why this variable would be garbage collected because the singleton keeps a reference.
Property written incorrectly. Use strong, weak, retain instead assign
property (nonatomic, assign) KTTeacher* currentTeacher;
I tried to reproduce the error and I could not. Create a project and put the classes KTTTeacherService, KTTeacher (empty class with the apiKey property) Everything works fine. Therefore you must have the error in the KTTeacher class.
In the viewDidLoad I have the following:
KTTTeacherService* teacherService = [KTTTeacherService shared];
// updateCurrentTeacher, for default is nil
KTTeacher *teacher = [[KTTeacher alloc] init];
[KTTTeacherService updateCurrentTeacher:teacher];
KTTeacher* currentTeacher = teacherService.currentTeacher
When you call currentTeacher method then, _currentTeacher (property) has a value now.

Best practices for JSONModel and polymorphism

How do you deal with subclassing collection attributes in JSONModel?
Let's say I have these two endpoints with different responses of the same "product object".
domain.com/api/1.0/getProductList
domain.com/api/1.0/getProductDetails/productId
I wrote some example code below to show you my issue:
// ProductListModel
#interface ProductListModel : JSONModel
#property (nonatomic, strong) NSNumber *productId;
#property (nonatomic, strong) NSNumber *userId;
#property (nonatomic, strong) NSArray<OrderListModel> *orders;
#end
// ProductDetailModel
#interface ProductDetailModel : ProductListModel
#property (nonatomic, strong) NSURL *productImageUrl;
#property (nonatomic, strong) NSArray<OrderDetailModel> *orders;
#end
// OrderListModel
#protocol OrderListModel <NSObject>
#end
#interface OrderListModel : JSONModel
#property (nonatomic, strong) NSNumber *orderId;
#property (nonatomic, strong) NSNumber *price;
#end
// OrderDetailModel
#protocol OrderDetailModel <NSObject>
#end
#interface OrderDetailModel : OrderListModel
#property (nonatomic, strong) NSURL *orderImageUrl;
#end
The ProductDetailModel wants the same inherited attributes as ProductListModel, but it wants the orders array in the subclassed type.
However this leads to a compiler warning:
Property type 'NSArray<OrderDetailModel> *' is incompatible with type
'NSArray<OrderListModel> *' inherited from 'ProductListModel'
I found this related SO post but I'd rather not monkey patch the JSONModel library.
Edit #1:
This has been discussed in the #574, and #229 github issues before but requires a "type" string to determine what class to instantiate. This requires a change on the backend API.
Is there a way to do this without changing the backend API?
You can't override the property in the subclass to have a different type as it will violate the Liskov substitution principle - #Paulw11
For future readers, here's how the updated example code would look like:
// ProductBaseModel
#interface ProductBaseModel : JSONModel
#property (nonatomic, strong) NSNumber *productId;
#property (nonatomic, strong) NSNumber *userId;
#end
// ProductListModel
#interface ProductListModel : ProductBaseModel
#property (nonatomic, strong) NSArray<OrderListModel> *orders;
#end
// ProductDetailModel
#interface ProductDetailModel : ProductBaseModel
#property (nonatomic, strong) NSURL *productImageUrl;
#property (nonatomic, strong) NSArray<OrderDetailModel> *orders;
#end
// OrderListModel
#protocol OrderListModel <NSObject>
#end
#interface OrderListModel : JSONModel
#property (nonatomic, strong) NSNumber *orderId;
#property (nonatomic, strong) NSNumber *price;
#end
// OrderDetailModel
#protocol OrderDetailModel <NSObject>
#end
#interface OrderDetailModel : OrderListModel
#property (nonatomic, strong) NSURL *orderImageUrl;
#end

What will happen if my class conforms to two protocols having same property?

Let's say I have two protocols
#protocol Playlist<NSObject>
#property(nonatomic, copy) NSString *title;
#property(nonatomic, assign) NSUInteger trackCount;
#end
and another as
#protocol Album<NSObject>
#property(nonatomic, copy) NSString *name;
#property(nonatomic, assign) NSUInteger trackCount;
#end
and there is a class which conforms to these protocols
.h file
#interface MusicLibrary <Playlist, Album>
#end
.m file
#implementation MusicLibrary
#synthesize title;
#synthesize name;
#synthesize trackCount;
#end
Which trackCount property will it refer to? Can I use trackCount twice?
It surely do not give any compile time error.
It looks to me that you are modeling your data wrong. The way you have it setup a musicLibrary is BOTH a playlist and an album. I think a more correct model would have a MusicLibrary containing many playlist and many albums. Something like:
#property (nonatomic, strong) NSArray<Album>* albums;
#property (nonatomic, strong) NSArray<Playlist>* playlists;

Having trouble with new field in entity (unrecognized selector error)

I'm trying to add a field to an entity and whenever I try to access the new field I get
unrecognized selector sent to instance...
Everything else about the entity works. I'm able to add objects and I'm able to assign values to other fields in the object, but not to the new objects.
Of note, I also deleted the DerivedData directory and I deleted the .mom/.momd files to make sure the tables are being built correctly.
Any thoughts?
Locations.h
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#class Games, Players;
#interface Locations : NSManagedObject
#property (nonatomic, retain) NSNumber * defaultLoc;
#property (nonatomic, retain) NSString * locationAddr1;
#property (nonatomic, retain) NSString * locationAddr2;
#property (nonatomic, retain) NSString * locationCity;
#property (nonatomic, retain) NSNumber * locationID;
#property (nonatomic, retain) NSString * locationName;
#property (nonatomic, retain) NSString * locationState;
#property (nonatomic, retain) NSString * locationZip;
#property (nonatomic, retain) NSNumber * numberOfCourts;
#property (nonatomic, retain) NSNumber * defLoc;
#property (nonatomic, retain) NSSet *haveGames;
#property (nonatomic, retain) NSSet *havePlayers;
#end
#interface Locations (CoreDataGeneratedAccessors)
- (void)addHaveGamesObject:(Games *)value;
- (void)removeHaveGamesObject:(Games *)value;
- (void)addHaveGames:(NSSet *)values;
- (void)removeHaveGames:(NSSet *)values;
- (void)addHavePlayersObject:(Players *)value;
- (void)removeHavePlayersObject:(Players *)value;
- (void)addHavePlayers:(NSSet *)values;
- (void)removeHavePlayers:(NSSet *)values;
#end
Locations.m
#import "Locations.h"
#import "Games.h"
#import "Players.h"
#implementation Locations
#dynamic defaultLoc;
#dynamic locationAddr1;
#dynamic locationAddr2;
#dynamic locationCity;
#dynamic locationID;
#dynamic locationName;
#dynamic locationState;
#dynamic locationZip;
#dynamic numberOfCourts;
#dynamic defLoc;
#dynamic haveGames;
#dynamic havePlayers;
#end
Note: defLoc and defaultLoc are two fields that I added. These are the ones giving me problems.
Executing code:
- (IBAction)updateLocation:(UIStoryboardSegue *)segue
{
// If it is not "Edit" it is an "Add" and we need to insert a newobject.
if ([segueType1 isEqualToString:#"Add"])
{
NSManagedObjectContext *context = [[self fetchedResultsController] managedObjectContext];
location = [NSEntityDescription insertNewObjectForEntityForName:#"Locations" inManagedObjectContext:context];
}
location.locationName = lName;
location.locationAddr1 = lAddr1;
location.locationAddr2 = lAddr2;
location.locationCity = lCity;
location.locationState = lState;
location.locationZip = lZip;
location.numberOfCourts = lNumCourts;
location.defLoc = lNumCourts;
// location.defaultLoc = [NSNumber numberWithInt:1];
// DLog(#"ldefaultLocation = %#",ldefaultLocation);
location.defaultLoc = ldefaultLocation;
segueType1 = #"Add"; // Always reset back to Add so that segues work right
}
location.defLoc above is the command that gives the error...All other location statements work fine.
That can happen when you don't declare the new attribute in your NSManagedObject child. As soon as you add a new field in the core data model, remember to add the right property to the NSManagedObject class and declare it #dynamic in the implementation.

RestKit CoreData one to many relationship mapping, the To Many part not working

I am using RestKit 0.20 to map 2 entities.There is a one to many relationship.
Teacher<->>SchoolClass
Here is the Teacher.h
#class SchoolClass;
#interface Teacher : NSManagedObject
#property (nonatomic, retain) NSString * firstName;
#property (nonatomic, retain) NSString * lastName;
#property (nonatomic, retain) NSNumber * teacherId;
#property (nonatomic, retain) NSSet *teachesClass;
#end
#interface Teacher (CoreDataGeneratedAccessors)
- (void)addTeachesClassObject:(SchoolClass *)value;
- (void)removeTeachesClassObject:(SchoolClass *)value;
- (void)addTeachesClass:(NSSet *)values;
- (void)removeTeachesClass:(NSSet *)values;
#end
Here is the SchoolClass.h
#interface SchoolClass : NSManagedObject
#property (nonatomic, retain) NSString * classCodeId;
#property (nonatomic, retain) NSString * classDesc;
#property (nonatomic, retain) NSString * classRoom;
#property (nonatomic, retain) Teacher *classTeacher;
#end
The code for the relationship mapping is:
[classMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"teacher" toKeyPath:#"classTeacher" withMapping:teacherMapping]];
The results are that in the SchoolClass objects, the classTeacher properties are correctly added. However in the Teacher objects, the teachesClass properties are all empty. Is this expected behavior or I missed something?
Thanks
Ray
Somehow the problem is gone now. All the relationships now working fine. Not sure exactly what happened. Perhaps because I did a reset for the simulator after made a json change. Previously the json results had a problem that caused both side of the relationship problems. After it was fixed, the SchoolClass objects were fine but Teacher objects had relation problems. Now both are fine.

Resources