restkit - having trouble mapping relationships... or something - ios

Setup - using RestKit, along with it's abilities to store data in a CoreData store.
I'm trying to perform two separate GET operations:
issue/:issueId ==> this returns an Issue object, assuming one with that ID exists.
issue/:issueId/comment ==> this returns Comment objects, belonging to the issue matching issueId.
So, for the first call, that just gets an issue back. It will only return comments back if I pass in an extra parameter on the URL. Otherwise, it won't. Of course, if I do ask for it, then the objects get created just fine, and all the objects are connected correctly in my core-data store.
The objects that I'm mapping look like this:
#interface Issue : NSManagedObject
#property (nonatomic) int32_t issueId;
#property (nonatomic, retain) NSSet* comments;
// many other fields not shown.
#end
#interface Comment: NSManagedObject
#property (nonatomic) int32_t commentId;
// many other fields not shown.
#end
Issue has a collection of Comments. Comments don't know about their owning Issue.
So, all I'm trying to do is make it possible for both of these calls to exist.
For example, in our URLs, say "issueId" is 12345. So, if I make one call to http://example.com/issue/12345, I'd like the data to be written to my CoreData store. (This works great, btw). What I would like to happen next is to call "http://example.com/issue/12345/comments", and then have those comments write to the CoreData store, and also be connected to issue-12345, that's already there. That's the part that I'm having trouble with.
If anyone could offer guidance on this, I'd really appreciate it.

After reading this issue on the official repo, I would proceed like follows.
In you Core Data model add the inverse relationship Comment -> Issue, so that your Comment interface looks like
#interface Comment: NSManagedObject
#property (nonatomic, retain) Issue * issue;
#property (nonatomic) int32_t commentId;
// many other fields not shown.
#end
and make that relationship mandatory.
Now you have to setup your mapping adding that relationship, for instance
[issueMapping addRelationshipMappingWithSourceKeyPath:#"comments"
mapping:[self commentMapping]];
If my understanding is correct, RestKit should populate both relationships (the one-to-many Issue -> Comment and its inverse) for you.

Related

How to deserialize an object correctly, if there is not all data in JSON?

Imagine that we have an application that displays articles. Let's say that every article has such fields:
id
title
author
text
So we create object to store articles:
#interface Article : NSObject
#property int article_id;
#property NSString *title;
#property NSString *author;
#property NSString *text;
If we need to display one full article - we ask the server for information about the article by it's id. Server responces with JSON (for example), and we have all fields - id,caption,title,text on that JSON. We can deserialize it and our object will have all fields.
But what if we need to get a list of articles from the server (display them it TableView for example)? We don't need all data for this, so server sends us JSON array with only id and caption for every article. If we deserialize that dictionaries, our object will have nil fields (author and text). Is this normal to use that object? Or we need to create some special class without that fields, and use it for news list?
Something like this:
#interface ArticleForTableView : NSObject
#property int article_id;
#property NSString *title;
What is the correct way to send selected article object to detail controller? We need to create new Article object with all fields filled (if it it correct to use ArticleForTableView), or fill fields of exiting object (if it is correct to use Article with nil fields), or something else?
Someone else may disagree with me on this, but I don't think you really need to make another object class just to handle the different use case. As long as you are properly handling potential null values for the fields of your class then its really just up to you to make sure you have the data you need when you actually need to display it.
So if you only have id and caption in the table view, when you select a table view cell you could push your detail controller, pass the Article object you selected into detail controller and then retrieve the remaining information on demand for that article if it has not already been pulled from the server.

Relational database in Realm?

I want to create simple relational database in the iOS Realm DB. How can i link user id and there Wishlist product similar to the relational database in SQL. Example like bellow image :
I know i can create 2 separate Realm models (tables) to store user id with time_stamp and in second table for Wishlist where there are user id with each Wishlist products or user id and a Wishlist, where the user has an array of Wishlist. Now i want to store all users with there multiple wishlists. This means that every time the user enters in APP, I have to query every wishlists in existence to see whether its intended for this specific wishlists. The problem is in Wishlist table, it doesn't have any primary key and realm not allowing to create model without primary key.
Does Realm support foreign key concept ? and use of composite key is quit complicated. Is there a more efficient way to do this?
You can use RLMArray , where RLMArray is the container type in Realm used to define to-many relationships.
As mentioned in the official Realm documentation for objective c check example here.
Also go through this link it might help RLMArray doc
RLMArray is used for the relational database concept in realm. You can try like this:
#import <Realm/Realm.h>
#class User;
// User model
#interface User : RLMObject
#property NSString *name;
#property NSString *user_id;
#property RLMArray<Watchlist *><Watchlist> *watchlist;
#end
RLM_ARRAY_TYPE(Dog) // define RLMArray<Dog>
// Watchlist model
#interface Watchlist : RLMObject
#property NSString *id;
#property NSInteger *activity;
#property NSInteger *cost;
#end
RLM_ARRAY_TYPE(Watchlist) // define RLMArray<Person>
// Implementations
#implementation User
#end // none needed
#implementation Watchlist
#end // none needed
Read data from realm :
RLMResults *watchlistDB = [Watchlist allObjects];
WatchlistDB = [realm_data objectAtIndex:index];
RLMArray *realm_array = WatchlistDB.watchlist;

iOS Core Data Saving MOC issue due to Validation rules context

I'm using Magical Record with the Core Data framework, and I've run into issues saving deleted objects from my MOC. I have a Patient NSManagedObject that has a set of Notes NSManagedObjects, so MOs look like like so:
Patient.h
#interface Patient : NSManagedObject
#property (nonatomic, retain) NSSet *notes;
#end
#interface Patient (CoreDataGeneratedAccessors)
- (void)addNotes:(NSSet *)values;
- (void)removeNotes:(NSSet *)values;
#end
Notes.h
#interface Note : NSManagedObject
#property (nonatomic, retain) NSDate * creationDate;
#property (nonatomic, retain) NSString * noteText;
#property (nonatomic, retain) Patient *patient;
#end
I also have validation rules to make sure the noteText property is not null or empty. Now in my view controller in the viewDidLoad method I'm creating a new note managed object using:
Note* lNote = [Note MR_createInContext:localContext];
So, the note is created instantly once the view loads, ready for the noteText property to be modified via a UITextView. If the user does not enter any text and presses Save the validation triggers and prevents the save, which is all good.
The problem occurs when I click on my notes archive folder button which is in this same view controller, once pressed, it presents a modal view controller and lets the user either load or delete notes, since I'm trying to delete a note from this archive screen, I have to rollback the previous note I created in the viewDidLoad method, so that I can delete the notes and save the default context, otherwise when I'm trying to save the deleted objects the validation rule for noteText property kicks in from the MOC.
I notice that this is more of a logical or work flow type of problem, but I want to prevent the rollback of the note created in the defaultContext and still be able to save the defaultContext with the deleted notes.
I've tried using different MOC but that presented more issues, one MOC for retrieving the patients and another one for creating notes.
Creating a different managed object context is the correct solution for your problem. A MOC is a "scratchpad" and you need two scratchpads in the scenario you describe. You are essentially interrupting the note creation process with another note editing process.
That being said, you could just delete the empty note and recreate it when the other controller is dismissed. You could also set the note text to #"". There are all kinds of hacky ways to accomplish this, but using two MOCs is the cleanest method.

iOS Should I Use Core Data? [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 9 years ago.
Improve this question
I have an exercise application that allows you to create a Workout Program, and then create individual Workouts associated with the Program. You can also create Workouts independent of any Program. I'm new to Obj-C fundamentals and am trying to figure out how I should tie in Workouts with their Program (keeping in mind that eventually reporting data will use the relationship between the two).
I've concluded that this can/should be done in 1 of 2 ways and need help learning which is the most efficient:
1) When a Workout is created, see if it originates from a Program or is on it's own (this is how I've done it successfully now). If it's from a program, add a 'WorkoutInProgram' Core Data attribute to the Workout which will store the ProgramName. Then whenever I want to fetch all Workouts in a Program, I just look up the attribute where WorkoutInProgram == ProgramName.
2) Create some sort of Core Data relationship between Workouts and Programs. When a Workout is created within a Program, the relationship between the ProgramName attribute (of the Program entity) and the Workout entity is stored. I spent around an hour trying to figure out this relationship, got confused, and resorted to #1 which worked.
I'm not sure if a Core Data relationship is useful here, I got hung up on the fact that multiple Workouts would be associated with a single Program entity based on ProgramName.
As always, thank you.
Here is roughly what things should look like in XCode. Program.workouts is a To-Many relationship and Workouts.program is an optional To-One relationship.
Generated NSManagedObject subclasses should look like this
//
// Program.h
// CoreDataLibraryApp
//
//
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#class Workout;
#interface Program : NSManagedObject
#property (nonatomic, retain) NSDate * timeStamp;
#property (nonatomic, retain) NSString * name;
#property (nonatomic, retain) NSSet *workouts;
#end
#interface Program (CoreDataGeneratedAccessors)
- (void)addWorkoutsObject:(Workout *)value;
- (void)removeWorkoutsObject:(Workout *)value;
- (void)addWorkouts:(NSSet *)values;
- (void)removeWorkouts:(NSSet *)values;
#end
//
// Workout.h
// CoreDataLibraryApp
//
//
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#class Program;
#interface Workout : NSManagedObject
#property (nonatomic, retain) NSString * name;
#property (nonatomic, retain) Program *program;
#end
When you create a new Workout you can set the program attribute like this:
newWorkout.program = selectedProgram;
If you want to get all workouts for a program you do something like this
for (Workout *workout in selectedProgram.workouts) {
NSLog(#" workout is %#", workout.name);
}
What you've done so far is create your own relationship manually, you just aren't using Core Data to manage the relationship. I would recommend creating a one-to-many relationship between Program and Workout. The relationship can be specified as optional so it doesn't have to be populated.
Some benefits to having the relationship specified with Core Data include automatically keeping things in sync if the name of the Program ever changes, being able to specify delete behaviors, and code simplification when it comes to fetching the Program associated with a Workout.
One thing that might be causing confusion (I know it did for me at first) is that you don't specify the relationships based on specific attributes (like you would if you were creating a model in a relational DB). You don't, for example, create a specific attribute for the purpose of linking the two entities together. Instead, when you create your relationship, you might name it "program" in the Workout entity and "workouts" in the Program entity.

Delete/Insert versus Update for NSManagedObjects when data should be updated

I'm trying to solve problem which appears during this case:
User can initiate data loading from external source, when data is loaded, it is saved via CoreData. Then it is displayed in some views and some other classes got references to NSManagedObjects.
Data loading can be initiated by other condition (for example, when application resumes from background). New external data is received, dataController deletes previous and creates new data. And here is the problem.
I want to notify all data consumers-classes that they should load new instances (send them references to deleted objects, so they can compare references with ones they own and determine should they ask for new data version or not). But after deletion consumer-class has reference to fault without properties, its ObjectID is useless (because new instance was saved) and I don't know how load its new version.
I can implement some NSManagedObject wrapper:
#interface Model : NSObject
- (id)initWithUniqueId:(id)uniqueId dataObject:(NSManagedObject *)dataObject;
#property (nonatomic, strong, readonly) id uniqueId;
#property (nonatomic, strong, readonly) NSManagedObject *dataObject;
#end
This object can reload itself after dataObject becomes fault. But maybe this approach is wrong and this overhead is not needed? And NSManagedObject should be deleted only if it is really deleted, not updated? And then if object is updated, we can use KVO to handle properties changes, and if object is deleted, we can observe NSManagedObjectContext notifications for changes and look for deleted objects.
I just want to know which way would you prefer and why (maybe you like some other approach)? Thanks in advance.
If you are using an external data source, your own version of some kind of unique ID makes sense.
Then everything becomes simple. You load the data, update the persistent store when you save the context, and send of a notification via the NSNotificationCenter. All listeners can now simply update their data by re-fetching.
(Fetched results controllers that implement the delegate methods do not even have to be notified via the notification center.)

Resources