using restkit do you have to remap EVERYTHING each time something changes? - ios

Right now I have a server that formats my data exactly how restkit wants it, and restkit just takes it and directly maps it to coredata.
This works fine, but when I start to accumulate a lot of data it becomes slow.
For example, I have one object called "stories" and each story contains an array of "posts". each time a new "post" gets added, I regenerate the "story" object to which the new post belongs to, and return the story object to the user for restkit to map. As a story starts to accumulate many posts, this process becomes very slow for restkit. I would prefer a way to just send back new posts, and then tell restkit "hey, add this post to the array of posts on this story", which is in contrast to what I do now which is more like "replace this story with this one I just returned, which includes all posts including any new or updated ones".
Is this possible within restkit? Am I better served just manipulating core data myself to support updates?

Yes, it's possible.
You can look at 'foreign key mapping' to connect your new posts to the existing story. The most important part is to set the relationship assignment type to Union because the default is replace.

Related

Realm over CoreData should I use NSFetchedResultController or a Dictionary?

I'm working on an app using Core Data and NSFetchedResultController but for many reasons I would like to switch it to use Realm.
I saw that there is a "Realm version" of the NSFetchedResultController on github but It wouldn't be compatible with my current code using Core Data.
The view controller is displaying a list of People from the same school, like an address book.
This list is a sublist of a people who studied in the same city.
So I was thinking to make 1 unique request to the database to retrieve the list of all people and then filter locally this list within 1 Dictionary per school [String: AnyObject] with String as Section name within the tableView and AnyObject an array of people.
first solution, make a NSFetchedResultController for each school with a predicate filtering the location. Then all delete actions etc.. are handled by the delegates -> this is not compatible with Realm
Create those dictionaries and update them for each actions... -> this works with Realm but it's very annoying to code.
Any better solution?
EDIT:
I need to clarify my request:
I'd like to write a class that inherit UITableViewController.
This class has a list of people sorted in the alphabetical order
The tableview has section with the first letter of their firstname
The tableview controller needs to handle updates of the data model (insert, update, delete)
As we might move from CoreData to Realm, I'd like to write code that is compatible with both so that I don't need to modify it again later. The only "smart" way I found to do it so far is to forget about the NSFetchedResultController and the RBQFetchedResultsController, because they are respectively linked to CoreData and Realm, and then use data structure like Dictionaries.
Just to clarify, you're creating a UITableView with multiple sections; 1 per school, and you want to sort a flat list of people in a Realm database into the table based on their school, correct?
If that Realm fetched results controller you mentioned (I'm guessing it's RBQFetchedResultsController) doesn't fit your app's architecture, then yeah, dictionaries would be the way to go, but it shouldn't hopefully be as 'manual' as you'd think.
The good thing about Realm Results objects are that they are 'live' in the sense that if a new item is added to Realm after the Results query was made, it'll be retroactively updated to include the new item. That being the case, as long as you're managing a dictionary of Results objects that each relate to fetching the people for each specific school, the only manual aspect would be managing the table sections themselves.
The only thing to be aware of is you'll need a mechanism to be notified when a new person has been added to a specific school (in order to know to refresh that section of the table view), but for now, it would be best if you did that in your own logic (Either through a callback block, or a delegate call).
On a sidenote, we're in the middle of adding a set of new APIs to make implementing native fetched results controller behaviour possible in Realm (i.e., automating the need to post a notification when a new object is added). We haven't got a proper release date confirmed yet, but it should be within the first quarter of 2016! :)

Persisting relationship with local user object during a Restkit update

I am working on an events app that syncs "Events" with a JSON API using Restkit. The mapping looks roughly like this.
var eventsMapping = RKEntityMapping(forEntityForName: "Event", inManagedObjectStore: managedObjectStore)
eventsMapping.identificationAttributes = ["eventID", "name", "eventDescription"]
eventsMapping.addAttributeMappingsFromDictionary([
"id":"eventID",
"title":"name",
"description":"eventDescription",
(more mapping attributes here, etc...)
])
The events are displayed using a NSFetchedResultsController. Locally, there is a 'User' NSManagedObject that is created...however this object is just local to the app and is not synced to the server via Restkit. The "User" has a one-to-many relationship with Events, with the purpose that a user can "save" events that they are attending. (Note: Right now, both sides of the Core Data relationship delete rules is set to "no action.") The save is roughly done this way.
var managedObjectContext = RKManagedObjectStore.defaultStore().mainQueueManagedObjectContext
currentUser.mutableSetValueForKey("events").addObject(event)
managedObjectContext?.saveToPersistentStore(&error)
So far everything works great, selected events are stored and saved successfully with the user and persists through app relaunches as expected. However, there is one scenario that causes an event to be removed from the user and that is when an update is made to that particular event on the server. When Restkit detects this and updates the event, according to breakpoints I placed in the NSFetchedResultsController didChangeObject, apparently Restkit and/or Core Data is actually deleting the event and then inserting it back with the updates. This is transparent and just fine in most cases, but in this case I'm thinking the initial delete is what is breaking off the event from the user.
Of course, the eventsMapping above doesn't reference any relationship to a user in any way, so that could be another reason why the relationship is broken off. I have been reading more about Restkit relationships and I have used relationship / property mappings in Restkit before successfully to relate objects, but in that scenario both objects existed on the API. In this case, the User here isn't a part of the API at all, only local as I explained. So should I still be using a Restkit relationship mapping? Or perhaps I should be trying to accomplish all of the above via another way?
I figured out the answer, I made a goof in the code above. On the identification attributes for the mapping, I had three different properties in there, when I only should have used the one with the primary key that would never change (eventID).
eventsMapping.identificationAttributes = ["eventID"]
What apparently was happening was because I specified the title/name as an identification attribute, whenever that title changed on the server Restkit would identify it as a new/different object and delete the "old" object and insert the "new" one. When I changed it only to specify the primary key, it triggered an update instead and my relationship with the user persisted.
One more note for others that threw me at first: some older info I came across that helped me solve this said to use a primaryKeyAttribute on the mapping. This is apparently dated information: use the identificationAttributes instead.

Core Data--pass name of entity or reference to entire entity?

I'm making a simple bank account tracker, for self-instructional purposes. I'm using Core Data to store three entities, related as in the screenshot:
WMMGTransaction objects are simply stored as they are recorded, and extracted as needed to feed tableviews and detail views. This will be done via NSFetchedResultsController and a predicate. I'm using MagicalRecord to access Core Data, if that matters.
My question is this:
When I pass WMMGAccount data from one VC to another, such as when creating a new account, or when selecting one from a list (via delegation as a rule), does it matter if I pass a reference to the entire entity, or can I just use an NSString bearing the .name of the account and identify the account when required with a predicate and an NSFetchedResultsController? I guess this is a strategy question, and may generate discussion, rather than having a cut and dried answer, but I'm wrestling with it, so I thought I'd ask.
It sounds like you're asking if you should pass an object to the code that needs it, or if you should pass information that could be used to look up the same object again.
Unless you need to use the managed object on a different thread or queue, you should always pass the actual object. No sense re-fetching an object you already have. It's extra work and code complexity that (unless there are some unusual extenuating details you didn't mention) won't help in any way.
If you are needing to use the object on a different queue or thread, passing information that can be used to look it up is the correct approach. But in that case-- don't pass the value of one of the properties. Use the managed object ID.
Core Data won't force name values to be unique, while the object's managedObjectID is unique. It's also faster when retrieving the object, because you can use objectForID: or existingObjectForID: instead of performing a fetch.

Replicating RKConnectionDescription Without RestKit

I'm in the process of removing RestKit from our iOS app. I'm able to get things that I want into Core Data, but they're not really connected.
For example, we have one network call that returns a list of "Category"s (which have a "categoryID" and a "categoryName"; "Category"s also map to-many "StoreLocation"s). We then have another network call that returns a list of "StoreLocation"s (which, among other things, have a "storeName", "storeID", "storeCategoryIDs"; "StoreLocation"s also map to-many "Category"s).
With RestKit, I could use a RKConnectionDescription to describe that "storeCategoryIDs" drove the relationship to-many "Category"s. With that, if I had a given Category object, I could easily determine which StoreLocations belonged to that category.
I'm struggling to see how to accomplishing this without any RestKit dependencies. I suppose I could, whenever I'm about to insert a new Category or new StoreLocation, fetch all of the opposite managed objects and do this manually, but I seem to be missing some component of Core Data that can do.
The main part you're missing is the predicate applied to the fetch and which uses the identification attributes to find the appropriate existing objects. You do need to run your own fetch as core data will not magically update one object if you create a different new object and insert it.

Breeze projection query from already-loaded entity

If I use breeze to load a partial entity:
var query = EntityQuery.from('material')
.select('Id, MaterialName, MaterialType, MaterialSubType')
.orderBy(orderBy.material);
return manager.executeQuery(query)
.then(querySucceeded)
.fail(queryFailed);
function querySucceeded(data) {
var list = partialMapper.mapDtosToEntities(
manager, data.results, entityNames.material, 'id');
if (materialsObservable) {
materialsObservable(list);
}
log('Retrieved Materials from remote data source',
data, true);
}
...and I also want to have another slightly different partial query from the same entity (maybe a few other fields for example) then I'm assuming that I need to do another separate query as those fields weren't retrieved in the first query?
OK, so what if I want to use the same fields retrieved in the first query (Id, Materialname, MaterialType, MaterialSubType) but I want to call those fields different names in the second query (Materialname becomes just "name", MaterialType becomes "masterType" and so on) then is it possible to clone the partial entity I already have in memory (assuming it is in memory?) and rename the fields or do I still need to do a completely separate query?
I think I would "union" the two cases into one projection if I could afford to do so. That would simplify things dramatically. But it's really important to understand the following point:
You do not need to turn query projection results into entities!
Backgound: the CCJS example
You probably learned about the projection-into-entities technique from the CCJS example in John Papa's superb PluralSight course "Single Page Apps JumpStart". CCJS uses this technique for a very specific reason: to simplify list update without making a trip to the server.
Consider the CCJS "Sessions List" which is populated by a projection query. John didn't have to turn the query results into entities. He could have bound directly to the projected results. Remember that Knockout happily binds to raw data values. The user never edits the sessions on that list directly. If displayed session values can't change, turning them into observable properties is a waste of CPU.
When you tap on a Session, you go to a Session view/edit screen with access to almost every property of the complete session entity. CCJS needs the full entity there so it looks for the full (not partial) session in cache and, if not found, loads the entity from the server. Even to this point there is no particular value in having previously converted the original projection results into (partial) session entities.
Now edit the Session - change the title - and save it. Return to the "Sessions List"
Question
How do you make sure that the updated title appears in the Sessions List?
If we bound the Sessions List HTML to the projection data objects, those objects are not entities. They're just objects. The entity you edited in the session view is not an object in the collection displayed in the Sessions List. Yes, there is a corresponding object in the list - one that has the same session id. But it is not the same object.
Choices
#1: Refresh the list from the server by reissuing the projection query. Bind directly to the projection data. Note that the data consist of raw JavaScript objects, not entities; they are not in the Breeze cache.
#2: Publish an event after saving the real session entity; the subscribing "Sessions List" ViewModel hears the event, extracts the changes, and updates its copy of the session in the list.
#3: Use the projection-into-entity technique so that you can use a session entity everywhere.
Pros and Cons
#1 is easy to implement. But it requires a server trip every time you enter the Sessions List view.
One of the CCJS design goals was that, once loaded, it should be able to operate entirely offline with zero access to the server. It should work crisply when connectivity is intermittent and poor.
CCJS is your always-ready guide to the conference. It tells you instantly what sessions are available, when and where so you can find the session you want, as you're walking the halls, and get there. If you've been to a tech conference or hotel you know that the wifi is generally awful and the app is almost useless if it only works when it has direct access to the server.
#1 is not well suited to the intended operating environment for CCJS.
The CCJS Jumpstart is part way down that "server independent" path; you'll see something much closer to a full offline implementation soon.
You'll also lose the ability to navigate to related entities. The Sessions List displays each session's track, timeslot and room. That's repetitive information found in the "lookup" reference entities. You'll either have to expand the projection to include this information in a "flattened" view of the session (fatter payload) or get clever on the client-side and patch in the track, timeslot and room data by hand (complexity).
#2 helps with offline/intermittent connectivity scenarios. Of course you'll have to set up the messaging system, establish a protocol about saved entities and teach the Sessions List to find and update the affected session projection object. That's not super difficult - the Breeze EntityManager publishes an event that may be sufficient - but it would take even more code.
#3 is good for "server independence", has a small projection payload, is super-easy, and is a cool demonstration of breeze. You have to manage the isPartial flag so you always know whether the session in cache is complete. That's not hard.
It could get more complicated if you needed multiple flavors of "partial entity" ... which seems to be where you are going. That was not an issue in CCJS.
John chose #3 for CCJS because it fit the application objectives.
That doesn't make it the right choice for every application. It may not be the right choice for you.
For example, if you always have a fast, low latency connection, then #1 may be your best choice. I really don't know.
I like the cast-to-entity approach myself because it is easy and works so well most of the time. I do think carefully about that choice before I make it.
Summary
You do not have to turn projection query results into entities
You can bind to projected data directly, without Knockout observable properties, if they are read-only
Make sure you have a good reason to convert projected data into (partial) entities.
CCJS has a good reason to convert projected query data into entities. Do you?

Resources