Core Data adding relationship by users - ios

I am stuck on a issue that I am having with core data, Once my app load the core data database gets create. I have a user entity that gets populated via the submit button on the login viewController if the credentials are correct. In the next View I take the return data from the login viewController and submit it to another URL. This gives me JSON return data which gets stored in the database as well.
I have attached my data model and everything work fine except the user entity, I cannot for the life of me to get the right user to go with the data inside the connections.
These are my users
Each user has two records the below is what's getting stored, instead of all all 1's in the user column there should be two 1's and two 2's.
Here is my code that I am using to store the data
User *username = [User MR_findFirstByAttribute:#"username" withValue:currentUser];
NSDictionary *parameters = #{#"DeviceKey": username.guid};
[_operationManager POST:#"GetPrivateRssFeeds" parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject) {
//I'm calling all the users with this piece
NSArray *elements = [User MR_findAll];
id attributes = [elements valueForKey:#"username"];
NSLog(#"user: %#",attributes);
username.sessionKey =[[operation.responseObject objectForKey:#"Data"]objectForKey:#"sessionId"];
id connections = [[operation.responseObject objectForKey:#"Data"]objectForKey:#"connections"];
id connection;
NSMutableArray *connectionArray = [NSMutableArray array];
for(connection in connections)
{
//Get the JSON values
NSString *contentSystemID = [connection objectForKey:#"contentSystemId"];
NSString *contentSystemName = [connection objectForKey:#"name"];
NSString *logoUrl = [connection objectForKey:#"logo"];
NSNumber *unreadCount = [connection objectForKey:#"unread_count"];
FCConnection *connectionEntity = [FCConnection MR_createEntity];
connectionEntity.contentSystemID = contentSystemID;
connectionEntity.contentSystemName = contentSystemName;
connectionEntity.logoUrl = logoUrl;
connectionEntity.unreadCount = unreadCount;
// [user addConnections:connection];
[connectionArray addObject:connection];
id categories = [connection objectForKey:#"categories"];
NSMutableArray *categoryArray = [NSMutableArray array];
for (id cat in categories)
{
NSString *title = [cat valueForKey:#"name"];
NSNumber *unreadCount = [cat valueForKey:#"unread_count"];
FCCategory *category = [FCCategory MR_createEntity];
category.title = title;
category.unreadCount = unreadCount;
id items = [cat objectForKey:#"items"];
NSMutableArray *itemArray = [NSMutableArray array];
for (id item in items)
{
NSNumber *isRead = [item valueForKey:#"unread"];
NSString *title = [item valueForKey:#"title"];
NSString *link = [item valueForKey:#"link"];
NSString *systemID = [item valueForKey:#"itemId"];
// BaseTopic *baseEntity = [A MR_createEntity];
// baseEntity.title = title;
Article *article = [Article MR_createEntity];
article.link = link;
article.systemID = systemID;
article.isRead = isRead;
[article setValue:title forKey:#"title"];
article.category = category;
[itemArray addObject:article];
NSLog(#"item %#",itemArray);
}
[category addArticles:[NSSet setWithArray:itemArray]]; // mapping the items to category
category.connection = connectionEntity;
[categoryArray addObject:category];
}
connectionEntity.user = username;
[connectionEntity addCategories:[NSSet setWithArray:categoryArray]]; // mapping the items to connection entity
How can I map the user to the connection?

You are setting the same user entity (i.e., username) for all the connections you are enumerating in that loop (for(connection in connections)). Hence why, It's all set to 1. Set the user entity properly based on your logic.

Related

How can I show data correctly in a UITableViewController with Firebase?

I am building something like an "instagram" app with posts with pictures. I am using Firebase to store/retrieve data.
My issue comes because the posts are shown kind of in a random way. For example, in cellForRowAtIndexPath, the first post is shown at indexPath[0], the next post is [1], but the next one which should be at [2], it shows at [0].
I will copy paste the most relevant code for the issue:
Here is the class where I retrieve the Scores:
////// Arrays to store the data
nameArray = [[NSMutableArray alloc]init];
scoreArray = [[NSMutableArray alloc]init];
photomealArray = [[NSMutableArray alloc]init];
keysArray = [[NSMutableArray alloc]init]; // Reference to know what post should be scored.
// Reference to the Database
self.storageRef = [[FIRStorage storage] reference];
FIRDatabaseReference *rootRef= [[FIRDatabase database] referenceFromURL:#"https://XXXXXXX.firebaseio.com/"];
// Query all posts
[[rootRef child:#"Posts"] observeSingleEventOfType:FIRDataEventTypeValue withBlock:^(FIRDataSnapshot * _Nonnull snapshot) {
if(snapshot.exists){
NSDictionary *dict = snapshot.value;
for (NSString *key in dict) {
// Query all users to show the Name of the person who posts
[[rootRef child:#"Users"] observeEventType:FIRDataEventTypeValue withBlock:^(FIRDataSnapshot * _Nonnull Usersnapshot) {
if(Usersnapshot.exists){
NSDictionary *Userdict = Usersnapshot.value;
for (NSString *Userkey in Userdict) {
NSString *Users_UserID = [Userdict[Userkey] valueForKey:#"UserID"];
NSString *UserID = [dict[key] valueForKey:#"UserID"];
// If the userID matches with the person who posts, then add it to the array
if([Users_UserID isEqualToString:UserID]) [nameArray addObject:[NSString stringWithFormat:#"%#",[Userdict[Userkey] valueForKey:#"Name"]]];
}
}
}];
UIImage *mealPhoto = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:[dict[key] valueForKey:#"PostPhoto"]]]];
NSString *Score = [dict[key] valueForKey:#"Score"];
NSString *PostKeys = [dict[key] valueForKey:#"KeyforPost"];
if([Score isEqual:#"null"]) [scoreArray addObject:#"0"]; else [scoreArray addObject:Score];
[photomealArray addObject:mealPhoto];
[keysArray addObject:PostKeys];
}
}
} withCancelBlock:^(NSError * _Nonnull error) {
NSLog(#"%#", error.localizedDescription);
}];
If I quit and relaunch the app, the new post will be stored in whatever index is "empty" starting from [0], which is what it is expected.
This is how the didSelectRowAtIndexPath looks like:
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath*)indexPath{
FeedTableViewCell *cell =[tableView cellForRowAtIndexPath:indexPath];
if(cell.selectionStyle != UITableViewCellSelectionStyleNone){
if([score.text intValue] >= -1 && [score.text intValue] <= 1 && ![score.text isEqual: #""]){
NSString *keyforcell = [keysArray objectAtIndex:indexPath.row];
NSLog(#"key a usar :%#", keyforcell);
ref = [[FIRDatabase database] referenceFromURL:#"https://XXXXX.firebaseio.com"];
[[[[self.ref child:#"Posts"] child:keyforcell] child:#"Score"] setValue:[NSNumber numberWithInt:[score.text intValue]]];
[[[[self.ref child:#"Posts"] child:keyforcell] child:#"Rated"] setValue:#"YES"];
[scoreArray insertObject:[NSNumber numberWithInt:[score.text intValue]] atIndex:indexPath.row];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
}
}
I have tried to play with observeSingleEventOfType and observeEventType because maybe this has to do with the fact that the single one is only called once. Also I've thought that viewDidLoad() is triggered after the cell is selected, but It shouldn't. Also, I think it has to do with the NSMutableArrays, but I am not completely sure
Can anyone help me out ?
UPDATE : I got to know that neither Firebase nor NSDictionary returns/stored sorted data, so my guess is that my issue has something to do with sorting the items. I got to know about orderbykey and orderbyvalue but they don't seem to work.
It is not clear to see what your issue is, but you should not forget there is no arrays in Firebase Database. The only collection type is dictionary, and dictionaries has no key order.
You should consider a sorting system on your objects, instead of directly adding them to arrays.

Add Key and Values from NSDictionary to NSMutableArray

I have an NSMutableArray with keys UserID and UserName.
I also have an NSDictionary with keys UserID and UserScore.
How do I add the key and values for UserScore from the NSDictionary to the NSMutableArray so that the UserName and UserScore represent the same UserID?
NSMutableArray:
UserID = 123,
UserName = JohnP
NSDictionary:
UserID = 123,
UserScore = 100
Desired output as NSMutableArray:
UserID = 123,
UserName = JohnP,
UserScore = 100
Below is how I retrieve data into the NSMutableArray using Parse and then grab data to a label:
[FBRequestConnection startWithGraphPath:#"/me/friends"
parameters:nil
HTTPMethod:#"GET"
completionHandler:^(
FBRequestConnection *connection,
id result,
NSError *error
) {
FacebookFriends = [result objectForKey:#"data"];
}];
NSString *friendname = [[NSString alloc] initWithFormat:#"%#",[[FacebookFriends objectAtIndex:indexPath.row] objectForKey:#"name"]];
I think you really just wanna use an NSMutableDictionary which is made to store value-key pairs.
You can give your mutable dictionary values from your other dictionary simply by pointing to its key-value (objectForKey).
It looks to me like you want to combine two dictionaries into one:
NSDictionary *dict1 = ...;
NSDictionary *dict2 = ...;
NSMutableDictionary *combined = [NSMutableDictionary new];
NSNumber *userid = dict1[#"UserID"];
NSAssert(userid, #"UserID missing from dict1");
NSAssert([userid isEqualToString:dict2[#"UserID"]], #"Mismatched UserIDs");
NSString *username = dict1[#"UserName"];
NSAssert(username, #"Username missing from dict1");
NSNumber *score = dict2[#"UserScore"];
NSAssert(score, #"Score missing from dict2");
combined[#"UserID"] = userid;
combined[#"UserName"] = username;
combined[#"UserScore"] = score;
However I also suspect you have an array of dictionaries. If so, let me know.

iOS: CHCSVParser & NSPredicate?

I'm currently attempting to use CHCSVParser to parse through a CSV file containing over 1500 entries, and 8 rows. I've successfully managed to parse through the file, and what I get is an NSArray of NSArrays of NSStrings.
For example here's what I get:
Loading CSV from: (
(
Last,
First,
Middle,
Nickname,
Gender,
City,
Age,
Email
),
(
Doe,
John,
Awesome,
"JD",
M,
"San Francisco",
"20",
"john#john.doe"
),
How could I sort this into a Person object and filter through it using NSPredicate, like Mattt Thompson does here.
Here's how I initialize the parser:
//Prepare Roster
NSString *pathToFile = [[NSBundle mainBundle] pathForResource:#"myFile" ofType: #"csv"];
NSArray *myFile = [NSArray arrayWithContentsOfCSVFile:pathToFile options:CHCSVParserOptionsSanitizesFields];
NSLog(#"Loading CSV from: %#", myFile);
Here's what Mattt does in the article I linked, which I'd like to do with my code:
NSArray *firstNames = #[ #"Alice", #"Bob", #"Charlie", #"Quentin" ];
NSArray *lastNames = #[ #"Smith", #"Jones", #"Smith", #"Alberts" ];
NSArray *ages = #[ #24, #27, #33, #31 ];
NSMutableArray *people = [NSMutableArray array];
[firstNames enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
Person *person = [[Person alloc] init];
person.firstName = firstNames[idx];
person.lastName = lastNames[idx];
person.age = ages[idx];
[people addObject:person];
}];
First, define a suitable Person class:
#interface Person : NSObject
#property(copy, nonatomic) NSString *firstName;
#property(copy, nonatomic) NSString *lastName;
// ...
#property(nonatomic) int age;
// ...
#end
Then you can read your data into an array of Person objects by enumerating the
myFile array. Inside the block, row is the "sub-array" for a single row:
NSMutableArray *people = [NSMutableArray array];
[myFile enumerateObjectsUsingBlock:^(NSArray *row, NSUInteger idx, BOOL *stop) {
if (row > 0) { // Skip row # 0 (the header)
Person *person = [[Person alloc] init];
person.lastName = row[0];
person.firstName = row[1];
// ...
person.age = [row[6] intValue];
// ...
[people addObject:person];
}
}];
Now you can filter that array as shown in the tutorial:
NSPredicate *smithPredicate = [NSPredicate predicateWithFormat:#"lastName = %#", #"Smith"];
NSArray *filtered = [people filteredArrayUsingPredicate:smithPredicate];

How to get array of values from NSDictionary array

When I try to print array of json values in log, I get addresses instead of values. Here's how I coded.
NSData *jsonData = [json dataUsingEncoding:NSASCIIStringEncoding];
NSArray *jsonArray = [NSJSONSerialization JSONObjectWithData:jsonData options:kNilOptions error:&error];
NSMutableArray *tempArray = [NSMutableArray arrayWithCapacity:jsonArray.count];
NSMutableArray *anotherTempArray = [NSMutableArray arrayWithCapacity:jsonArray.count];
NSDictionary *dict;
for(dict in jsonArray)
{
NSString *projectName = dict[#"Name"];
NSString *urlText = dict[#"Url"];
NSLog(#"Url text in array = %#", urlText);
NSString *attch = dict[#"attachmentes"];
NSLog(#"Attached url in array = %#", attch);
NSString *projID = dict[#"ProjectID"];
NSLog(#"Project ID in array = %#", projID);
SaveAttachment *saveAt = [[SaveAttachment alloc] initWithName:projectName withList:#"View" withAttachment:#"View"];
[tempArray addObject:saveAt];
SaveProjectId *saveProj = [[SaveProjectId alloc] initWithProjectId:projID];
saveProj.projectId = projID;
[anotherTempArray addObject:saveProj];
}
array = tempArray;
[self.tableViewProject reloadData];
NSLog(#"Array of project IDs === %#", anotherTempArray); //Get values (array of project ids here.
}
Replace
SaveProjectId *saveProj = [[SaveProjectId alloc] initWithProjectId:projID];
saveProj.projectId = projID;
[anotherTempArray addObject:saveProj];
with
[anotherTempArray addObject:projID];
This is because your anotherTempArray contains objects of SaveProjectId ie, everytime in for loop you are adding saveProj object not projID. Thats why your array showing SaveProjectId objects.
If you want to directly save them, then use the below modification
[anotherTempArray addObject:projID];
or you can use like(this is i would prefer)
NSLog(#"First project ID === %#", [anotherTempArray objectAtindex:0] projectId]);
You are storing SaveProjectId objects in the array, therefore when you print the content you see the address of those objects.
your "anotherTemoArray" is having objects of SaveProbectId so you have to pass object at index to SaveProjectId and then you can see the array information
When calling NSLog(#"Array of project IDs === %#", anotherTempArray); the -(NSString*)description method on each of the objects inside 'anotherTempArray' is being called.
In your case that means -(NSString*)description is being called on SaveProjectId objects. Override it to print out what you want... e.g.
-(NSString*)description {
return [NSString stringWithFormat:#"SaveProjectId: %#",self.projectId];
}

Core data filtering

I have a core data structure as follows:
Business <-------->> Employee <-------->> Address
Each business has multiple employees and each employee can have multiple addresses.
From the Business object, I want to be able to get an NSArray or NSSet of all the Address objects that specify a certain condition. E.g. All the street names have to be unique.
I know that I could override the isEqual: but I'm guessing this is going to have unintended results. Otherwise, I have been looking into using valueForKeyPath:#"#distinctUnionOfObjects", but I don't think I can pass a condition.
Here is some code that I have so far:
NSMutableArray *addressArray = [NSMutableArray array];
NSArray *employees = [Employee sortedArray];
//loop through employees
for (Employee *employee in employees) {
for (Address *address in employee.addresses) {
[addressArray addObject:address];
}
}
//filter out duplicates
addressArray = [addressArray valueForKeyPath:#"#distinctUnionOfObjects.city"];
This code gives me a list of unique cities, however, I want a collection containing Address objects that have unique city values (or some other condition).
I found a way to do this with the LinqToObjectiveC library:
NSArray* addressesWithUniqueCities = [input distinct:^id(id address) {
return [address city];
}];
Looking at the source, the underlying implementation is as follows:
typedef id (^Selector)(id item);
-(NSArray *)distinct:(Selector)keySelector
{
NSMutableSet* keyValues = [[NSMutableSet alloc] init];
NSMutableArray* distinctSet = [[NSMutableArray alloc] init];
for (id item in self) {
id keyForItem = keySelector(item);
if (![keyValues containsObject:keyForItem]) {
[distinctSet addObject:item];
[keyValues addObject:keyForItem];
}
}
return distinctSet;
}
My final code ended up being:
NSMutableArray *addressArray = [NSMutableArray array];
NSArray *employees = [Employee sortedEmployees];
//loop through employees
for (Employee *employee in employees) {
for (Address *address in employee.addresses) {
[addressArray addObject:address];
}
}
//filter out duplicates
NSArray *distinctAddressArray = [addressArray distinct:^id(id address) {
return [address addressLine];
}];
return distinctAddressArray;
I'm not sure this is what you meant but you could try something like: (untested)
+ (NSMutableArray*) addressesForBusiness:(Business*)business
sectionProperty:(NSString*)sectionProperty
{
NSFetchRequest* request = [[NSFetchRequest alloc] initWithEntityName:#"Address"];
request.predicate = [NSPredicate predicateWithFormat:#"employee.business == %#",business.objectID];
request.sortDescriptors = #[[NSSortDescriptor sortDescriptorWithKey:sectionProperty ascending:YES]];
NSArray* addresses = [business.managedObjectContext executeFetchRequest:request error:NULL];
NSMutableArray* sections = [NSMutableArray new];
NSMutableArray* currentSection = [NSMutableArray new];
NSManagedObject* prevAddress = nil;
for (NSManagedObject* address in addresses) {
if (prevAddress && ([[address valueForKey:sectionProperty] isEqual:[prevAddress valueForKey:sectionProperty]])) {
currentSection = [NSMutableArray new];
[sections addObject:currentSection];
}
prevAddress = address;
[currentSection addObject:address];
}
return sections;
}
This will return an array of arrays where each internal array hold objects with the same property value. In your example you could call:
[[self class] addressesForBusiness:someBusiness sectionProperty:addressLine];

Resources