restkit / restkit Goto Github PK
View Code? Open in Web Editor NEWRestKit is a framework for consuming and modeling RESTful web resources on iOS and OS X
License: Apache License 2.0
RestKit is a framework for consuming and modeling RESTful web resources on iOS and OS X
License: Apache License 2.0
When using deleteObject the object manager sends paramaters with the request. I believe this causes rails to raise an InvalidAuthenticityToken error - at least on my development server running Rails 3.0.3.
Are there situations where you would want to pass paramaters with a DELETE request?
Merge work to simplify driving Three20 tables using RestKit.
From the mailing list:
My object properties were also all (null) when I provided an
objectClass to loadObjectsAtResourcePath, but worked fine without it.
This is when I registered the class with the RKObjectMapper. If I
didn't register the class with the ObjectMapper and passed in the
class for objectClass, it still gave me nil values.
On Apr 23, 3:28 pm, Blake Watters <[email protected]> wrote:
> Hi Jon -
>
> Glad that you got things sorted out. The mapping really shouldn't fail out
> if you supply the target class for a registered object. I've opened an issue
> in Github to cover that edge.
>
> On Sat, Apr 23, 2011 at 10:05 AM, Jon Phillips
> <[email protected]>wrote:
>
> > OK, final email ... sorry for the bother, but if it's helpful to
> > anyone else.
>
> > I thought I was doing good things in my loadObjectsAtResourcePath call
> > by including the class to map it to. But I didn't need it because my
> > json includes the key that is the class. Because I included the class,
> > the elements dictionary included the class name key, which messed
> > stuff up.
>
> > Upshot = if you're outputting standard JSON from Rails, RestKit is
> > smart enough to figure out the class you're mapping and you don't have
> > to tell it. In fact, apparently, you shouldn't tell it?
>
> *Blake Watters*
> Two Toasters | CTO
> [email protected]
> (919) 926-8537
ignore it
The issue is that the checks at the thread barrier consult the type of the object being loaded and serializes to a managed object ID if that object is Core Data backed. This check fails if the object has child objects that are persistent, but the parent object is not.
Avoid having to wait for a challenge on each request for Basic AUTH
The headers apparently are not copying into the right path on Xcode 4 preview. Need to take a look at some point.
When some objects in JSON have a null property and other objects have a string for that property, they complexify the mapping and our comparison here breaks trying to compare an NSNumber to an NSString ([NSNumber isEqualToString:] unrecognized selector). This addition will evaluate the class and force a property setting if its' different.
diff ./RKObjectMapper.m ../0.9/RKObjectMapper.m
316,323d315
< if (![currentValue isKindOfClass:[propertyValue class]])
< {
< if (![currentValue isEqual:propertyValue] || ![[currentValue description] isEqualToString:[propertyValue description]]) {
< [model setValue:propertyValue forKey:propertyName];
< return;
< }
<
< }
389c381
< if (!modelClass || [modelClass isKindOfClass: [NSNull class]]) {
---
> if ([modelClass isKindOfClass: [NSNull class]]) {
They currently only serialize for Rails.
When testing using the RKSpecResponseLoader, if you are using an object loader control is returned to the test case when didLoadResponse: or didFailLoadWithError: is invoked by the framework.
We need to inspect the type of the request and defer returning control if this is an object loader.
RKParamsAttachment does not currently initialize a filename for attached parameters. This causes issues as remote services expect one to be set. Initialize it to an empty string instead of nil.
I had to check the libRestKitCoreData.a file to get things to compile successfully. That is, it is apparently not optional. The error is:
"_OBJC_CLASS_$_RKManagedObject", referenced from:
objc-class-ref-to-RKManagedObject in libRestKitObjectMapping.a(RKObjectLoader.o)
objc-class-ref-to-RKManagedObject in libRestKitObjectMapping.a(RKObjectMapper.o)
ld: symbol(s) not found
Add support for generating API documentation from the source code using appledoc.
Same deal as cancellation. Make it a one-line operation.
From the mailing list:
On Sat, Apr 23, 2011 at 10:05 AM, Jon Phillips [email protected] wrote:
OK, final email ... sorry for the bother, but if it's helpful to anyone else.
I thought I was doing good things in my loadObjectsAtResourcePath call
by including the class to map it to. But I didn't need it because my
json includes the key that is the class. Because I included the class,
the elements dictionary included the class name key, which messed
stuff up.
Upshot = if you're outputting standard JSON from Rails, RestKit is
smart enough to figure out the class you're mapping and you don't have
to tell it. In fact, apparently, you shouldn't tell it?
A lot of people would like to be able to automatically track object status (i.e. does it exist local only or on the server). We should architect this into the framework:
Hi there,
Thus far I've got my application working more or less correctly using
Restkit. I've followed some of the examples provided.
In the example given by the discussion app the following occurs. An
instance of a post object is created, assigned some data and then
eventually posted. I.e. Something to the effect of:
DBPost *post = [DBPost object];
post.topicID = topic.topicID;
post.body = bodyTextEditor.text;
[[RKObjectManager sharedManager] postObject:post delegate:self];
Now should an error occur during transmission we end up with a post
that is stored locally in sqlite but not on the server.
What is the best approach to handle this?
Should all models that get transmitted require some sort of flag to
indicate whether or not they have been sent? (This could then be
combined with some routine to pick up "unsent" data and resubmit it.)
I've also read some mention of using temporary managedObjectContexts.
Is this a better alternative?
What do you suggest to solve these kinds of problems?
Any help on this would be fantastic.
Thanks in advance,
Matt
Update RestKit to build on OS X again.
Create an integrated example application pulling together the code samples from the Advanced MobileTuts article:
I have a UI that lets a user create a new object. They can hit Save to
upload the object or Cancel to abandon it. I'm used to the pattern
where you create a temporary managed object context for this purpose.
If the user cancels, you simply get rid of the temporary context and
the object is forgotten. However, I can't seem to do this in RestKit.
If I create a temporary context, RestKit is unaware of that context
and doesn't save my object. The offending code is in
RKManagedObjectLoader handleTargetObject. It performs a save, but on
the default context, not my temporary one. My object actually gets
uploaded, but processLoadModelsInBackground doesn't catch the changes
because my object still has a temporary object ID (since its context
didn't get saved).
I use the same logic for editing an existing object. However, that
path seems to work OK. I guess because even though my temporary
context doesn't get saved, the existing object has a permanent object
ID and hence processLoadModelsInBackground is able to find the object
and update it.
Is there a better pattern for doing this?
Mark -
In most of my UI's we've just had the state set on controls in the UI rather than heading back to the model, save causes the state to be read from the controls, set on the object, and then the put/postObject call is dispatched. Cancel button just pops the view so no write-through occurs. I suspect that what you are doing could be easily supported if we provide a mechanism for supplying the object loader with a managed object context from the main application. It may be enough just to expose a read/write property for the object context and initialize it to a thread-local one if its nil.
If a programmer is using core data then it should be the authoritative data store for the application.
RKRequestTTModel modelsDidLoad:(NSArray*)models sets the underlying model to the array of objects that has just been received. Instead it should check to see if there is a core data backing and use it if it is available.
This allows the programmer to include their own rules for updating local objects (ie locally created objects or other synchronization strategies)
Something like the following should work:
- (void)modelsDidLoad:(NSArray*)models {
RKManagedObjectStore* store = [RKObjectManager sharedManager].objectStore;
NSArray* fetchRequests = nil;
NSArray* allKnownObjects = nil;
if (store.managedObjectCache) {
fetchRequests = [store.managedObjectCache fetchRequestsForResourcePath:self.resourcePath];
allKnownObjects = [RKManagedObject objectsWithFetchRequests:fetchRequests];
} else {
allKnownObjects = models;
}
[_objects release];
_objects = nil;
_objects = [allKnownObjects retain];
_isLoaded = YES;
[self didFinishLoad];
}
Example JSON:
{
"field1" : "value1",
"object_field1" : {
"field2": "value2"
},
"object_array" : [
{"field3":"value3"}
]
}
In this case the type of "object_field1" and the type of the objects in "object_array" need to be explicitly registered with registerClass:forElementNamed:, otherwise these types won't be resolved and the instance variables won't be set.
RestKit version: 0.9.1.1
Xcode version: 4.0.1/4A1006
The sample apps are attempting to send a RKRequest before Reachability has detected that there is a network available. This causes an error to be raised.
In the 0.9 branch around line 390, RKObjectMapper
attempts to update or create the modelClass
from a dictionary of childElements
in relationshipElements
.
In the case that relationshipElements
does not exist, i.e that relationship property wasn't passed in the JSON yet it still exists, relationshipElements
will be nil, children
will be an empty NSSet
or NSArray
, and [object setValue:children forKey:propertyName]
on line 406 will destroy any currently associated objects.
I believe this is fixed on the core-data-cleanup branch but has not been merged in, but please let me know if my assumptions are incorrect.
The call to [super didFinishLoad:response] in the (void)didFinishLoad:(RKResponse*)response implementation on RKObjectLoader results in the RKRequestDidLoadResponseNotification being sent twice for a single RKObjectLoader. This has a side effect of reducing _totalLoading in RKRequestQueue twice for a single request, and thus messing up the enforcement of the RKRequestQueue's max loading count.
It seems that removing to call to super in RKObjectLoader's void)didFinishLoad:(RKResponse*)response method addresses the issue, although I've only tested this fix in a limited number of cases.
Hi,
In deletePersistantStore you are doing this:
NSMutableDictionary* threadDictionary = [[NSThread currentThread] threadDictionary];
[threadDictionary setObject:nil forKey:kRKManagedObjectContextKey];
However dictionaries don't allow you to set nil for a key. You should change this to:
NSMutableDictionary* threadDictionary = [[NSThread currentThread] threadDictionary];
[threadDictionary removeObjectForKey:kRKManagedObjectContextKey];
From the NSMutableDictionary documentation:
Raises an NSInvalidArgumentException if aKey or anObject is nil. If you need to represent a nil value in the dictionary, use NSNull.
If the model object has a NSURL
property that is mapped with RestKit, the RKObjectMapper
will not attempt to turn the JSON string into an NSURL
object.
To work around this problem, we had to create a KVC compliant NSString
property that does the NSURL
conversion for us. RestKit will set the string property, and our custom setter creates and sets the actual URL property. This is less than ideal
RKObjectMapper
into a method that subclasses can easily override. We could have made a custom subclass of the object mapper and included our URL logic there.Hey guys,
Today I was working with "/places.json?category=Automóviles" as
resourcePath and got EXEC_BAD_ACCESS at RKURL.m
- (id)initWithBaseURLString:(NSString*)baseURLString resourcePath:
(NSString*)resourcePath
and found out that NSURL cannot be initialized with non-ASCII
characters, so I fixed this using NSUTF8StringEncoding to get a URL
like this "/places.json?category=Autom%C3%B3viles"
Here is my code:
- (id)initWithBaseURLString:(NSString*)baseURLString resourcePath:
(NSString*)resourcePath {
NSString *completeURL = [NSString stringWithFormat:@"%@%@",
baseURLString, resourcePath];
// If some condition
completeURL = [completeURL
stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
if ((self = [self initWithString:completeURL])) {
_baseURLString = [baseURLString copy];
_resourcePath = [resourcePath copy];
}
return self;
}
Do you think we should merge this?
I'd like to propose an addition to RKManagedObjectStore that would allow for a deletion and also reseeding of the store. Right now when you delete, it automatically assumes you don't have a seed. Basically the new function has only one change beyond the normal store deletion.
if (seedFile)
[self createStoreIfNecessaryUsingSeedDatabase:seedFile];
[self createPersistentStoreCoordinator];
When a service responds with bad data (or a bad response code) RestKit properly handles the error until it invokes "didFailLoadWithError:" on the RKTTRequestModel in -[RKRequestModel objectLoaderDidLoadUnexpectedResponse:]. RestKit passes a nil error which ultimately is ignored by the Three20 Framework (improperly or not) which means the application is kept in a indeterminate "loading state"
The fix for this would be to provide an error to didFailLoadWirhError:.
RestKit currently has special handling (even though the date is ISO 8601) for converting serialized UTC dates from rails. A rails serialized date is interpolated as a date in the local timezone. E.g., if "2010-11-16T21:11:33Z" is sent it will be parsed as "2010-11-16 21:11:33 -0500" for those in the eastern timezone. RestKit copes with this by explicitly subtracting the offset (-18000 seconds in eastern) to get the correct time.
This does not work when a date is serialized as a string in which the nsdateformatter can recognize the timezone offset. "2010-11-16 21:11:33 -0000" (y-M-d HH:mm:ss Z), for instance, would be properly handled by NSDateFormatter. However, RestKit still converts this time even though it properly interpolated which will automatically offset your time incorrectly.
The best solution may be to explicitly check for that permutation of the ISO 8601 format and switch on it. Otherwise it should only support the rails serialization format so that people do not falsely assume it can handle other date formats.
The service unavailable notification was being dispatched after the response received notification. This caused a crash if the RKRequest was not otherwise retained.
According to Roy Fielding:
"REST is defined by four interface constraints: identification of resources; manipulation of resources through representations; self-descriptive messages; and, hypermedia as the engine of application state." [1]
The first three are incorporated by RestKit (although it seems to couple REST to HTTP), but the latter one is missing. Namely, there is no built-in support for links representing possible state transitions or resource relationships, but rather the related entities need to be embedded or to be asked for with isolated queries based on id's.
"...if the engine of application state (and hence the API) is not being driven by hypertext, then it cannot be RESTful and cannot be a REST API. Period. " [2]
I think most people entirely miss the HATEOAS constraint, because most frameworks neither enforce, nor support the usage of them. Changing this for RestKit could improve the quality of and finally enable REST for wannabe-rest-systems.
The readme says that it should be possible to choose either YAJL or SBJSON (JSON Framework). However, RKJSONSerialization explicitly includes "NSObject+SBJSON.h" and calls a method "JSONRepresentation" declared there, so it's not possible to compile this class against YAJL only.
People keep asking for this. Enable it.
Ok. So I have narrowed it down to the following:
I have subclassed RKDynamicRouter so that I can send my POST requests
as JSON and not URL parameters. This is the method I have overridden
in the router subclass:
[object propertiesForSerializiation] had the dictionary with all the
correct properties, but the JSONSerializationWithObject: method
returns an empty HTTPBody object. So I did a bit more digging and
discovered the culprit is in this bit of code:
It appears JSONKit is just returning a nil string when we call
JSONString on it. I've used JSONKit a bit for a project before, and I
know it freaks out if it receives objects like NSDate. So I nilled out
the NSDate objects on my object, and bingo, the request body had data
in it.
So my question now is, what is the best way around the date problem.
My app is a scheduling app, so dates are pretty critical. I could
possibly put methods on the managed object that return a formatted
string for a date and map those rather than the dates themselves. The
other solution would be to use a different JSON library to JSONKit
that supports date objects, if this is the best solution, which should
I use? I am leaning towards the former solution rather than the latter
as it gives me the added benefit of having control over how my dates
are formatted in the request and being able to deal with different
date formats in responses.
Just setup RestKit in my XCode 4 project. Have a few gotchas that I think you need to add to the documentation;
Other than that - thanks for a great lib. Excited to try it out.
Martin
When building an app with RestKit and the optional RestKit Three20 library, the build fails when building from a clean RestKit Build/ directory. Build failures in the app cite the absence of <RestKit/Three20/Three20.h>, presumably because the Copy Headers build task(s) on the RestKit target execute(s) prior to the Three20 component being built via inclusion in the app directly. This can likely be solved by adding the Copy Headers build task(s) as part of the RestKit Three20 library.
My project can't have a store that originates with a "mergedModelFromBundles" ... I've actually got two different active models that have to remain separate. So, to accommodate this, I wind up using something like this to retain a passed-in model instead of whipping up one inside the framework.
Use case is that you want to support multiple target resource paths with the same HTTP verb:
In my application, I have a User model that contains both information
about the user and his or her login information. I'm using Authlogic
(Rails backend) for user signup and login (done pretty much the same
way as the DiscussionBoard example). I want to allow the user to
update his or her information on the service as well (e.g. contact
information, credentials), which is tied to the User model. Therefore,
it needs to handle two different PUT requests to two different
resource paths on a Rails server (one for /login and one for /users/
<id>). The RKRailsRouter allows for only one resource path for each
method (makes sense), so I'm just wondering, is there a way around
this in RestKit? Is there a way I can have the RKObjectManager
"putObject" to a specific resource path?
Basically, imagine my scenario is the DiscussionBoard example, but you
want to give the user the ability to update their username, email,
password, or any other information.
Epic story ticket for the object mapper 2.0 work. This update will simplify and expand the capabilities of the object mapping layer.
Running both example projects I receive the following error:
2010-12-28 10:37:05.404 RKTwitter[20942:207] Hit error: Error
Domain=com.twotoasters.RestKit.ErrorDomain Code=2 "The client is
unable to contact the resource at http://twitter.com/status/user_timeline/twotoasters.json"
UserInfo=0x9218d90 {NSLocalizedDescription=The client is unable to
contact the resource at http://twitter.com/status/user_timeline/twotoasters.json}
This crashes the app. The json is available via web browser so it isn't a connection issue. Any ideas?
Also the examples use globalManager rather than sharedManager.
Simplify this:
NSString* JSON = @"whatever";
RKRequest* request = [[RKRequest alloc] initWithURL:URL];
request.method = RKRequestMethodPOST;
[request.URLRequest setHTTPBody:[JSON dataUsingEncoding: NSASCIIStringEncoding]];
[request send];
Readme says that Core Data framework is optional and only required if you use the Core Data related part of RestKit, but RKObjectLoader class in ObjectMapping module imports RKManagedObjectStore from the CoreData module and Core Data framework itself.
OK here is the setup:
Item model object (IIContentObject.h is a RKManagedObject subclass
that deals with the update dates etc)
==============
#import "IIContentObject.h"
@interface Item : IIContentObject {
@private
}
@property (nonatomic, retain) NSNumber * parentID;
@property (nonatomic, retain) NSNumber * categoryID;
@property (nonatomic, retain) NSNumber * itemID;
@property (nonatomic, retain) NSString * itemName;
@property (nonatomic, retain) NSString * imageBaseDir;
@property (nonatomic, retain) NSString * imageName;
@end
#import "Item.h"
@implementation Item
@dynamic parentID;
@dynamic categoryID;
@dynamic itemID;
@dynamic itemName;
@dynamic imageBaseDir;
@dynamic imageName;
+ (NSDictionary*)elementToPropertyMappings {
return [NSDictionary dictionaryWithKeysAndObjects:
@"id", @"itemID",
@"name", @"itemName",
@"parent_id", @"parentID",
@"category_id", @"categoryID",
@"imageBaseDir", @"imageBaseDir",
@"imageName", @"imageName",
@"created_at", @"createdAt",
@"updated_at", @"updatedAt",
nil];
}
+ (NSString*)primaryKeyProperty {
return @"itemID";
}
@end
App delegate
==========
// Initialize RestKit
RKObjectManager* objectManager = [RKObjectManager
objectManagerWithBaseURL:@"http://localhost:3000"];
RKObjectMapper* mapper = objectManager.mapper;
// Add our element to object mappings
[mapper registerClass:[Item class] forElementNamed:@"item"];
RKRailsRouter* router = [[[RKRailsRouter alloc] init] autorelease];
[router setModelName:@"item" forClass:[Item class]];
[router routeClass:[Item class] toResourcePath:@"/items"
forMethod:RKRequestMethodPOST];
[router routeClass:[Item class] toResourcePath:@"/items/(itemID)"
forMethod:RKRequestMethodPUT];
[router routeClass:[Item class] toResourcePath:@"/items/(itemID)"
forMethod:RKRequestMethodDELETE];
objectManager.router = router;
In my view I call
============
- (void)loadItemData {
RKObjectManager* objectManager = [RKObjectManager sharedManager];
[[objectManager loadObjectsAtResourcePath:@"/items.json" objectClass:
[Item class] delegate:self] retain];
}
=======================
I am probably missing something here and not using the routing
correctly!
Every time I call the load item data I end up with blank rows inserted
into core data. JSON is as follows:
[{"item":{"category_id":null,"created_at":"2011-04-11T09:32:25Z","id":
1,"imageBaseDir":"","imageName":"","name":"Inspiration","parent_id":null,"updated_at":"2011-04-11T09:32:25Z"}}]
Can someone push me in the right direction please.
Thanks
David
On the background request branch, requests with the RKRequestBackgroundPolicyCancel or RKRequestBackgroundPolicyRequeue background request policies are not sent due to a bug in RKRequest's sendAsynchronously method.
Although everything builds fine, code completion doesn't work in Xcode 4. This is a severe issue for a RestKit-newbie like me.
Picking up Jeff Arena's work from #41. This implements a cache that is capable of responding with previously loaded payloads and headers.
They all fail with path errors, certainly due to Xcode4's new build system.
Edit: I have a pull request in now that resolves this.
Just wondering how the has many relationship works. Looking through the source I see this
@"[email protected]_many", @"hasMany",
Lets say my managed object models are:
Photo
User
Photo can be "liked" by many users. So liked is the JSON element which contains many users.
Would be trivial to implement this.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.