Not able to use one-to-many relationship in sharkORM - ios

I'm using KeyValueObjectMapping to convert JSON string to Model class.
Here is JSON string:
{
"id_str": "123456",
"name": "Some Name",
"protected": false,
"created_at": "Tue Mar 31 18:01:12 +0000 2009",
"tweets" : [
{
"created_at" : "Sat Apr 14 00:20:07 +0000 2012",
"id_str" : 190957570511478784,
"text" : "Tweet text",
"comments": {
"id_str":"2343",
"text":"This is comment1"
}
},
{
"created_at" : "Sat Apr 14 00:20:07 +0000 2012",
"id_str" : 190957570511478784,
"text" : "Tweet text",
"comments": {
"id_str":"2343",
"text":"This is comment2"
}
}
]
}
For which I have created model classes, like this:
For User,
#interface User : SRKObject
#property(nonatomic, strong) NSString *idStr;
#property(nonatomic, strong) NSString *name;
#property(nonatomic, strong) BOOL protected;
#property(nonatomic, strong) NSDate *createdAt;
#property(nonatomic, strong) NSArray *tweets;
#end
For tweets,
#interface Tweet : SRKObject
#property(nonatomic, strong) NSString *idStr;
#property(nonatomic, strong) NSString *text;
#property(nonatomic, strong) NSDate *createdAt;
#property(nonatomic, strong) Comments *comments;
#end
For Comments,
#interface Comments : SRKObject
#property(nonatomic, strong) NSString *idStr;
#property(nonatomic, strong) NSString *text;
#end
It is working absolutely fine and I'm getting tweets in array. And I get tweets as user.tweets.
To save this in database, I'm using SharkORM for ORM. But while saving either it crash or don't save tweets at all.
Here is the issue open in repo (but using different example) - https://github.com/sharksync/sharkorm/issues/78

The reason for the crash was the persistence of an array of Tweets stored in a User object as opposed to storing them as a separate class. See: This
Ultimately what you want to end up with is ....
#interface Tweet : NSObject
#property(nonatomic, strong) User* user;
#property(nonatomic, strong) NSString *idStr;
#property(nonatomic, strong) NSString *text;
#property(nonatomic, strong) NSDate *createdAt;
#end
As storing all tweets in an array object will just tie you in knots quickly (no searching, ever increasing save times).
So, when you get the JSON down, you can map the first class no problem, then iterate the tweets creating objects which are related to the User class.
Relating objects is just a case of setting the .user property with the relevant User entity which you have either created or queried for.

Thanks to #Adrian_H for the efforts. I figured out the solution and posting here.
I have added below code in Tweet & Comments model to save tweets as NSArray and comments as model object and it worked.
- (void)encodeWithCoder:(NSCoder *)aCoder;
-(id)initWithCoder:(NSCoder *)aDecoder;

Related

[__NSSingleEntryDictionaryI elements]: unrecognized selector sent to instance 0x2836c2820

Well, I should remember you I am still a beginner in iOS development with objective c. And maybe because of this, the solution is simple but I canĀ“t see it. I have a json who is armed in the following way :
{
"origin_addresses": [
"Test"
],
"rows": [
{
"elements": [
{
"distance": {
"text": "0.3 km",
"value": 339
},
"duration": {
"text": "1 min",
"value": 82
},
"status": "OK"
}
]
}
],
"status": "OK"
}
The app can consume the service without problems. My goal is to reach the distance attribute. Which I do as follows. I use a foreach to get through the array rows and then another foreach to get through the array elements.
for (BPCGARows *item in googleAddress.rows) {
NSLog(#"rows : %#", item);
for (BPCGAElements *element in item.elements) {
}
}
When I'm debugging the app, my first for each works without problems, but when I access the second foreach the (BPCGAElements * element in item.elements) the app crashes. Finally I can't print the item.elements in a log either. The error message is as follows:
'NSInvalidArgumentException', reason: '-[__NSSingleEntryDictionaryI
elements]: unrecognized selector sent to instance 0x281c75da0'
header BPCGADistance :
#interface BPCGADistance : MBOInspectableModel
#property (nonatomic, strong, readonly) NSString *text;
#property (nonatomic, strong, readonly) NSNumber *value;
#end
main BPCGADistance:
#interface BPCGADistance()
#property (nonatomic, strong, setter=text:) NSString *text;
#property (nonatomic, strong, setter=value:) NSNumber *value;
#end
header BPCGAElements :
#interface BPCGADistance : MBOInspectableModel
#property (nonatomic, strong, readonly) NSString *text;
#property (nonatomic, strong, readonly) NSNumber *value;
#end
main BPCGAElements:
#interface BPCGADistance()
#property (nonatomic, strong, setter=text:) NSString *text;
#property (nonatomic, strong, setter=value:) NSNumber *value;
#end
This is my NSLog(#"rows : %#", item),
I emphasize that by using the item.elements to print the crashea app on the console. ;

Realm subquery with count

People and groups. How many people are in each group? A simple scenario for an SQL database. Please help fetching this in Realm.
I have People:
#interface PersonObject : RLMObject
#property int idx;
#property NSString *firstName;
#property NSString *lastName;
#property RLMArray <PersonGroupId *> <PersonGroupId> *connectedGroupIds;
And Groups:
#interface GroupObject : RLMObject
#property int idx;
#property NSString *name;
I need to get a list of all groups with a total number of people in each of them. Currently it's done by looping through groups and it's really slow:
for (GroupObject *group in groups)
{
RLMResults *result = [PersonObject objectsWhere:#"ANY connectedGroupIds.idx == %d",group.idx];
group.numberOfMembers = result.count;
}
Is it possible to fetch that with just one query/subquery without looping through groups?
It sounds like you're trying to model a scenario where a person can belong to multiple groups, and a group can contain multiple people. To do that I'd suggest a model that looks like:
#interface PersonObject : RLMObject
#property NSString *firstName;
#property NSString *lastName;
#property RLMArray<GroupObject> *connectedGroups;
#end
#interface GroupObject : RLMObject
#property NSString *name;
#property RLMLinkingObjects<PersonObject> *members;
#end
#implementation GroupObject
+ (NSDictionary *)linkingObjectsProperties
{
return #{ #"members": [RLMPropertyDescriptor descriptorWithClass:PersonObject.class propertyName:#"connectedGroups"] };
}
#end
This takes advantages of Realm's built in support for relationships rather than trying to mimic them using per-object identifiers. This model would allow you to determine the number of members in a group using group.members.count.

How to do enumerateObjectsUsingBlock-like stuff with JSONModelArray?

I use JSONModel to hold my app datasource, and use -(id)initWithArray:(NSArray *)array modelClass:(Class)cls generated an JSONModelArray, now I want to do some search stuff like enumerateObjectsUsingBlock: method does. But I found that JSONModelArray is not inherited from NSArray.
So, how can I do this?
Try use BWJSONMatcher to convert json string to a NSArray.
For example, your json string seems like :
[{"name":"Arron","age":20,"grade":2},{"name":"Burrows","age":21,"grade":2}]
All you have to do is declare your own data model:
#interface Student : NSObject
#property (nonatomic, strong) NSString *name;
#property (nonatomic, assign) NSInteger age;
#property (nonatomic, assign) NSInteger grade;
#end
BWJSONMatcher will help you convert it to a NSArray in a very neat way:
NSArray *students = [BWJSONMatcher matchJSON:jsonString withClass:[Student class]];

RestKit 0.20 Posting object's attributes on conditional basis

I have complex JSON handling large amount of data, I need to optimise network traffic by sending only required attributes of mapped object to server.
For simplicity lets say I have following User class :
#property (nonatomic, retain) NSString *email;
#property (nonatomic, retain) NSString *fname;
#property (nonatomic, retain) NSString *password;
#property (nonatomic, retain) NSString *profilePic;
#property (nonatomic, retain) NSString *sname;
#property (nonatomic, retain) NSString *status;
#property (nonatomic, retain) NSString *token;
#property (nonatomic, retain) NSString *username;
#property (nonatomic, retain) NSNumber *isLoggedIn;
#property (nonatomic, retain) NSDate *dateCreated;
and my attributes mapping dictionary is following :
[dic addEntriesFromDictionary:#{
#"fname": #"fname",
#"sname": #"sname",
#"profilePic": #"profilePic",
#"email": #"email",
#"username": #"username",
#"password": #"password",
#"status": #"status",
#"token": #"token",
#"isLoggedIn": #"isLoggedIn",
#"dateCreated": #"dateCreated"
}];
For Signin call I needs to post just username & password as following JSON :
{
"user": {
"password": "password",
"username": "demouser"
}
}
While for Signup call I needs to POST entire User object so I cant downsize mapping dictionary. I needs to apply same procedure to lot more complex JSON.
How can I send required attributes of an object in POST call on conditional basis in an optimal fashion?
Thanks.
You are free to create multiple mappings for the same class / entity type - there is no restriction. Each mapping is associated with other mappings / request descriptor / response descriptor and this is where you need to concentrate on identification and uniqueness.
It may be simplest for you to have one request mapping which covers all of the attributes, and whose class is NSDictionary. Then, to use this mapping for a request you use KVC (dictionaryWithValuesForKeys:) to extract only the keys of interest from your true source object into a dictionary that you can then supply to the object manager for mapping and transmission.

Nested to-many relationships with RestKit

I'm trying to map relationships for the following JSON to a CoreData backed model.
Here's the JSON:
{
"photos": [
{
"id": 1,
"thumb": "http://localhost/test_1_thumb.png",
"image": "http://localhost/test_1.png",
"date": "2011-07-02T06:06:16Z",
"likes": 0,
"user": {
"id": 1,
"username": "User1",
"avatar": "http://cdn.domain.com/avatar.jpg"
},
"comments": [
{
"date": "2011-07-02T06:06:16Z",
"text": "This is the only comment",
"id": 1,
"author": {
"username": "User1",
"id": 1,
"avatar": "http://cdn.domain.com/avatar.jpg"
}
}
]
}
]
}
My CoreData models which I've mapped with RestKit's OM2 classes.
The Photo class.
#interface Photo : NSManagedObject
#property (nonatomic, retain) NSString * thumbnailUrl;
#property (nonatomic, retain) NSString * imageUrl;
#property (nonatomic, retain) NSDate * date;
#property (nonatomic, retain) NSNumber * likes;
#property (nonatomic, retain) NSNumber * photoID;
#property (nonatomic, retain) User *owner;
#property (nonatomic, retain) NSSet *comments;
#end
#interface Photo (CoreDataGeneratedAccessors)
- (void)addCommentsObject:(Comment *)value;
- (void)removeCommentsObject:(Comment *)value;
- (void)addComments:(NSSet *)values;
- (void)removeComments:(NSSet *)values;
#end
#implementation Photo
#dynamic thumbnailUrl;
#dynamic imageUrl;
#dynamic date;
#dynamic likes;
#dynamic photoID;
#dynamic owner;
#dynamic comments;
#end
Now the part I'm not completely sure on is the relationships:
// Map relationships between entities.
[commentMapping mapKeyPath:#"author" toRelationship:#"author" withMapping:userMapping];
[photoMapping mapKeyPath:#"user" toRelationship:#"owner" withMapping:userMapping];
[photoMapping mapKeyPath:#"comments" toRelationship:#"comments" withMapping:commentMapping];
With this I'm able to access all but the comments attribute of a photo, with this error: Comments = Relationship 'comments' fault on managed object. I have a feeling this is to do with the Photo model having comments defined as an NSSet or I'm doing something wrong with the relationship mapping but I'm not sure.
So turns out that the problem was nothing to do with RestKit or the mapping but my understanding of how CoreData functions.
I was accessing the mapped JSON objects in my delgate as follows:
- (void)objectLoader:(RKObjectLoader*)objectLoader didLoadObjects:(NSArray*)objects {
NSLog(#"Loaded photos: %#", objects);
for (id object in objects) {
Photo *photo = object;
// Print other photo attributes...
NSLog(#"Comments = %#", photo.comments );
}
}
The problem was in trying to print the collection object directly. CoreData would not fire a fault on the data store. Simply replacing with a fast iteration as follows and accessing the properties allowed me to access the comments object.
for (Photo *photo in objects) {
for (Comment *comment in photo.comments) NSLog(#"Comment = %#", comment.text);
}
You need something like:
[objectManager.mappingProvider setMapping:commentMapping forKeyPath:#"comments"];
Also checkout
https://github.com/RestKit/RestKit/wiki/Object-mapping

Resources