Restkit request load handler - iphone

As i see in all RestKit documentations, didWSRequestLoadObjects delegate function is used to handle service response.
The problem is, if I have a different requests (postObject) in my view controller i have to check response type in didWSRequestLoadObjects for each request.
Is there a way to register a function before each postObject and get each response in different function?

Which version of RestKit are you using?
On the last release it is highly encouraged to use blocks instead of a loadObjects delegate function. For example, the RKObjectManager postObject method has a success and error parameters which receives a block.
Here is an example of use:
RKObjectManager *manager = [RKObjectManager managerWithBaseURL:[NSURL URLWithString:#"http://some.url"];
//Configure here your manager with response descriptors and stuff..
[manager postObject:someObject path:#"/some/path" parameters:nil success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
//Success Response code here
} failure:^(RKObjectRequestOperation *operation, NSError *error) {
//Error Response code here
}];

Related

iPhone how to fetch data asynchronously from a web service with API call limit?

I'm pulling data from a web service with an API call limit of 125 per hour. My initial sync of the user's data will use a method similar to the one below. I'm having trouble understanding the correctness of concurrency implications of the code below.
I'm adding a series of AFHTTPRequestOperation over to a serial NSOPerationsQueue (max concurrent count = 1). The resulting calls return asynchronously and cause the method to process the data dictionary. Because of the API call limit, I know that at some point my code will fail and start to return error dictionaries instead.
Can I expect the following code to return full data dictionaries sequentially, or due to asynchronous nature of callbacks, can some of them complete before earlier requests?
Because I'm trying to do initial sync, I want to make sure that once the code fails due to API call limit, I have no "holes" in my data up until the failure point.
-(void)addRequestWithString:(NSString*)requestString
{
// 1: Create a NSURL and a NSURLRequest to points to the web service providing data. Add Oauth1 information to the request, including any extra parameters that are not in scope of Oauth1 protocol
NSURL *url = [NSURL URLWithString:requestString];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[ self.auth authorizeRequest:request withExtraParams:self.extraAuthParameters];
// 2: Use AFHTTPRequestOperation class, alloc and init it with the request.
AFHTTPRequestOperation *datasource_download_operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
// 3: Give the user feedback, while downloading the data source by enabling network activity indicator.
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
// 4: By using setCompletionBlockWithSuccess:failure:, you can add two blocks: one for the case where the operation finishes successfully, and one for the case where it fails.
[datasource_download_operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSDictionary* dictonary = [NSJSONSerialization JSONObjectWithData:(NSData *)responseObject
options:NSJSONReadingMutableContainers error:&error];
[self processResponseDictionary:dictonary];
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
} failure:^(AFHTTPRequestOperation *operation, NSError *error){
// 8: In case you are not successful, you display a message to notify the user.
// Connection error message
DLog(#"API fetch error: %#", error);
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
}];
// 9: Finally, add ìdatasource_download_operationî to ìdownloadQueueî of PendingOperations.
[[self syncQueue] addOperation:datasource_download_operation];
}
Your approach will continue the operations even after they start failing.
If you need the operations to go one at a time, but stop once the failure block is hit, enqueue a new request in the completion block of the prior request.
(This code is from an answer to AFNetworking Synchronous Operation in NSOperationQueue on iPhone; I didn't write it.)
NSEnumerator *enumerator = [operations reverseObjectEnumerator];
AFHTTPRequestOperation *currentOperation = nil;
AFHTTPRequestOperation *nextOperation = [enumerator nextObject];
while (nextOperation != nil && (currentOperation = [enumerator nextObject])) {
currentOperation.completionBlock = ^{
[client enqueueHTTPRequestOperation:nextOperation];
}
nextOperation = currentOperation;
}
[client enqueueHTTPRequestOperation:currentOperation];
If the failure block is hit, the following operations will never be enqueued.

getting JSON data without object mapping using RestKit 0.2

I'm trying to call a RESTful web service with RESTKit 0.2 that returns only a string, but the RKResponseDescriptor class forces me to use a mapping with its method responseDescriptorWithMapping in order to be able to get the string value, I created the mapping and got the string value without any problems, but how can i get this string value without having to create the object mapping (i.e. creating an NSObject subclass, creating a property for the string to be received, and creating a mapping dictionary between the returned JSON key and this property) ?
You can still use RestKit to make the request. Sometimes it makes sense to do so in case server is returning an error for example, in which case it is necessary to access the raw data returned from server.
[[RKObjectManager sharedManager] getObjectsAtPath:kLoginURL
parameters:params
success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
NSError *error;
NSDictionary* json = [NSJSONSerialization JSONObjectWithData:operation.HTTPRequestOperation.responseData options:NSJSONReadingMutableLeaves error:&error];
NSLog(#"msg: %#", [json objectForKey:#"msg"]);
}
failure:^(RKObjectRequestOperation *operation, NSError *error) {
}];
In this case, don't use RestKit to make the request. RestKit uses AFNetworking for the underlying network communications so if you import the classes you can use it too. Try directly using AFHTTPRequestOperation to make the request.

Check if the current request is already in request queue using restkit iphone

I am using restkit for sending request, response mapping and all. but sometimes, i am sending same request multiple time which comes from different screens. So it get added to request queue. But i don't want to add request into request queue if the request is already there. How can i check that.
I am calling function like this
//Here I need to check if the following url is in RKRequestQueue. If it not there then call the below method,
[self getDataFromServer];
- (void)getDataFromServer{
RKObjectManager *manager = [[RestKit sharedDataManager] objectManager];
[manager loadObjectsAtResourcePath:#"/getData" usingBlock:^(RKObjectLoader *loader) {
[RKClient sharedClient].requestQueue.showsNetworkActivityIndicatorWhenBusy = YES;
loader.method = RKRequestMethodPOST;
loader.params = inputData;
loader.onDidFailWithError = ^(NSError *error) {
};
loader.onDidLoadObjects = ^(NSArray *objects) {
};
}
Any help is appreciated.
Interesting question, I was pretty sure that you can actually check the URLs of your requests in the RKRequestQueue, but I wasn't able to find anything useful for that in the RKRequestQueue reference. Instead, I've found the containsRequest: method, but this will only compare RKRequest objects, not the actual URLs.
I guess the simplest thing to do is to create some kind of proxy for managing your network activity, implement the requestQueue:didSendRequest: delegate method and monitor which URLs are currently processed.

How to pass info with NSURLConnection instance so I can get it from connectionDidFinishLoading

I am using NSURLConnection to load data from a response. It works as it should, the delegate method connectionDidFinishLoading has the connection instance with the data I need. The problem is that I want to pass some information along with the request so that I can get it when the connection finishes loading:
User wants to share the content of a URL via (Facebook, Twitter,
C, D).
NSURLConnection is used to get the content of the URL
Once I have the content, I use the SL framework
SLComposeViewController:composeViewControllerForServiceType and need
to give it the service type
At this point I don't know what service the user selected in step 1. I'd like to send that with the NSURLConnection.
Can I extend NSURLConnection with a property for this? That seems very heavy-handed. There must be a "right way" to do this.
Many Thanks
Assuming you don't need the delegate-based version of the NSURLConnection process for some other reason, this is a good use case for the block-based version:
- (void)shareContentAtURL:(NSURL *)shareURL viaService:(NSString *)service
{
NSURLRequest *urlRequest = [NSURLRequest requestWithURL:shareURL];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[NSURLConnection sendAsynchronousRequest:urlRequest queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
{
if ([data length] == 0 && error == nil) {
// handle empty response
} else if (error != nil) {
// handle error
} else {
// back to the main thread for UI stuff
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
// do whatever you do to get something you want to post from the url content
NSString *postText = [self postTextFromData:data];
// present the compose view
SLComposeViewController *vc = [SLComposeViewController composeViewControllerForServiceType:service];
[vc setInitialText:postText];
[self presentViewController:vc animated:YES];
}];
}
}];
}
Since blocks can capture variables from their surrounding scope, you can just use whatever context you already had for the user's choice of service inside the NSURLConnection's completion block.
If you're still wed to the delegate-based NSURLConnection API for whatever reason, you can always use an ivar or some other piece of state attached to whatever object is handling this process: set self.serviceType or some such when the user chooses a service, then refer back to it once you get your content from the NSURLConnectionDelegate methods and are ready to show a compose view.
You could check the URL property of an NSURLConnection instance and determine the service by parsing the baseURL or absoluteString property of the URL with something like - (ServiceType)serviceTypeForURL:(NSURL *)theURL;
All the NSURLConnectionDelegate methods pass the calling NSURLConnection object-so you could get it from
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
or
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error

EXC_BAD_ACCESS -iPhone Objective c

I've been trying this Twitter api stuff and it's really confusing...
I keep getting EXC_BAD_ACCESS bad access with the following code... What is the problem here?
NSURL *followingURL = [NSURL URLWithString:#"https://api.twitter.com/1/users/lookup.json"];
// Pass in the parameters (basically '.ids.json?screen_name=[screen_name]')
id fromIntToNum = [NSNumber numberWithInteger: friID];
NSDictionary *parameters = [NSDictionary dictionaryWithObjectsAndKeys:#"159462573", #"user_id", nil];
// Setup the request
twitterRequest = [[TWRequest alloc] initWithURL:followingURL
parameters:parameters
requestMethod:TWRequestMethodGET];
// This is important! Set the account for the request so we can do an authenticated request. Without this you cannot get the followers for private accounts and Twitter may also return an error if you're doing too many requests
[twitterRequest setAccount:theAccount];
// Perform the request for Twitter friends
[twitterRequest performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error) {
if (error) {
/*
// deal with any errors - keep in mind, though you may receive a valid response that contains an error, so you may want to look at the response and ensure no 'error:' key is present in the dictionary
NSLog(#"%#",error);*/
} else {
/*NSError *jsonError = nil;
// Convert the response into a dictionary
NSDictionary *twitterGrabbedUserInfo = [NSJSONSerialization JSONObjectWithData:responseData options:NSJSONWritingPrettyPrinted error:&jsonError];
// Grab the Ids that Twitter returned and add them to the dictionary we created earlier
NSLog(#"%#", [twitterGrabbedUserInfo objectForKey:#"screen_name"]);*/
}
}];
I separated the line the my code fails on...
What could be causing this Twitter API problem?
The following line causes the crash:::
[twitterRequest performRequestWithHandler:^(NSData *responseData,
NSHTTPURLResponse *urlResponse, NSError *error) {
I will also sometimes get this error:
[__NSCFNumber credentialForAccount:]: unrecognized
UPDATE: I commented out the handler and I made TwitterRequest and ivar, but it still crashes...
In your block you look for an error, but if you get one you log it and continue on. You should put an "else" statement in and only proceed if no error.
Why not try to comment out all your code in the handler - don't do anything - and see if you get the crash. Then try with just the error code. Then try with the JSON serialization, and finally the last line. If you can find the part of the block that is causing the problem that would help.
Also, I suspect that performRequestWithHandler: does not block, but expects you to notify your class within the block that the request is done. If so it means "TWRequest *twitterRequest" should be an ivar or property, and you need to allows for some method to get called when the handler is done. Your crash may be due to ARC reallocating your object while the object is running.
EDIT:
Note that that the TWRequest class description says: "Use the initWithURL:parameters:requestMethod: method to initialize a newly created TWRequest object passing the required property values. " It says PLURAL properties, meaning more than 1. Could it be that it also expects a "credentialForAccount" property? You have to read the twitter docs to find all the required properties.
EDIT2:
Well, we don't even know if you get as far as your handler. Put a NSLog there but I suspect its never getting that far. If true this leaves three possibilities:
a) it does't like the URL (although this seems good)
b) you are missing some parameters it expects
c) id doesn't like "theAccount" object - is it a valid ACAccount object? Try NSLogging it.
It has to be one of these three things.
I had similar issues with iOS5 and TWRequest, and the problem turned out to be the ACAccountStore being released before the request handler block was called. The following code fixed this for me:
__weak ACAccountStore *wAccountStore = accountStore;
[request performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error) {
__strong ACAccountStore *sAccountStore = wAccountStore;
// handle the finished request here
// to silence Xcode warning 'unused variable'
// not necessary for releasing sAccountStore,
// it will go out of scope anyway when the block ends
sAccountStore = nil;
}];
This way, the accountStore gets retained until the block finishes.