RestKit KVC Validation won't call validation methods - validation

this is my first question here :)
OK so I have a projet with ReskKit 0.23.3 via cocoapods. I use RestKit/CoreData.
I fetch an URL, the result got mapped to my object and is correctly saved by core data. I want to use Key-Value Validation to check some values retrieved against the one already persisted. I read that i could use the methods validateKey:error: on my NSManagedObject. Somehow, it is never called. I'm frustrated...
Here are my files (for simplicity, i concatenated logic code into one flat file here):
JSON response /collections/{id}
{
"id": "00000000-0000-0000-0000-00000000000",
"image_url": "http://server/image.png",
"name": "Collection C",
"etag": 42,
"ctag": 42
}
Collection.h
#interface Collection : NSManagedObject
#property(nonatomic, strong) NSString *collectionId;
#property(nonatomic, strong) NSString *name;
#property(nonatomic, strong) NSURL *imageUrl;
#property(nonatomic, strong) NSNumber *etag;
#property(nonatomic, strong) NSNumber *ctag;
#end
Collection.m
#implementation Collection
#dynamic collectionId, name, imageUrl, etag, ctag;
- (BOOL)validateCollectionId:(id *)ioValue error:(NSError **)outError {
NSLog(#"Validating id");
NSLog(#"Coredata collection id: %#", self.collectionId);
NSLog(#"GET collection id: %#", (NSString *)*ioValue);
return YES;
}
- (BOOL)validateEtag:(id *)ioValue error:(NSError **)outError {
NSLog(#"Validating etag");
NSLog(#"Coredata collection etag: %#", self.etag);
NSLog(#"GET collection etag: %#", (NSString *)*ioValue);
return YES;
}
#end
Code
NSManagedObjectModel *managedObjectModel = [NSManagedObjectModel mergedModelFromBundles:nil];
RKManagedObjectStore *managedObjectStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:managedObjectModel];
[managedObjectStore createPersistentStoreCoordinator];
NSString *storePath = [RKApplicationDataDirectory() stringByAppendingString:#"/MyApp.sqlite"];
NSError *error;
NSPersistentStore *persistentStore = [managedObjectStore addSQLitePersistentStoreAtPath:storePath fromSeedDatabaseAtPath:nil withConfiguration:nil options:nil error:&error];
NSAssert(persistentStore, #"Failed to add persistent store with error: %#", error);
[managedObjectStore createManagedObjectContexts];
managedObjectStore.managedObjectCache = [[RKInMemoryManagedObjectCache alloc] initWithManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
[RKManagedObjectStore setDefaultStore:managedObjectStore];
NSURL *url = [NSURL URLWithString:#"http://server/api"];
RKObjectManager *objectManager = [self managerWithBaseURL:url];
objectManager.requestSerializationMIMEType = RKMIMETypeJSON;
objectManager.managedObjectStore = [RKManagedObjectStore defaultStore];
RKEntityMapping *collectionMapping = [RKEntityMapping mappingForEntityForName:#"Collection" inManagedObjectStore:[RKManagedObjectStore defaultStore]];
[collectionMapping addAttributeMappingsFromDictionary:#{#"id": #"collectionId",
#"image_url": #"imageUrl"}];
[collectionMapping addAttributeMappingsFromArray:#[#"name", #"etag", #"ctag"]];
[collectionMapping setIdentificationAttributes:#[#"collectionId"]];
RKResponseDescriptor *collectionResponseDescriptors = [RKResponseDescriptor responseDescriptorWithMapping:collectionMapping
method:RKRequestMethodGET pathPattern:#"collections/:collectionId"
keyPath:nil
statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
[objectManager addResponseDescriptor:collectionResponseDescriptor];
[objectManager getObjectsAtPath:#"collections/00000000-0000-0000-0000-00000000000" parameters:nil success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
Collection *collection = (Collection *)[mappingResult.array firstObject];
NSLog(#"Collection: %#", collection);
} failure:^(RKObjectRequestOperation *operation, NSError *error) {
NSLog(#"Oh noes :(");
}];
Output
2014-09-13 12:39:04.242 MyApp[41958:607] I restkit:RKLog.m:33 RestKit logging initialized...
2014-09-13 12:39:05.028 MyApp[41890:607] Collection: <NSManagedObject: 0x9108a60> (entity: Collection; id: 0x94166d0 <x-coredata://6645F428-7631-45F0-A8AF-E2352C50F35E/Collection/p1> ; data: {
collectionId = "00000000-0000-0000-0000-00000000000";
ctag = 42;
etag = 42;
imageUrl = "http://server/image.png";
name = "Collection C";
})
So, I get my Log with the Collection, but None of the NSLog in the validate<Key>:error: methods got triggered... Could not figure out why!
Edit
With some breaks, i figured this is RKMappingOperation who is responsible for calling those validation methods on my object. Precisely it's validateValue:atKeyPath:
RKMappingOperation.m
...
- (BOOL)validateValue:(id *)value atKeyPath:(NSString *)keyPath
{
BOOL success = YES;
if (self.objectMapping.performsKeyValueValidation && [self.destinationObject respondsToSelector:#selector(validateValue:forKeyPath:error:)]) {
NSError *validationError;
success = [self.destinationObject validateValue:value forKeyPath:keyPath error:&validationError];
...
}
...
But self.destinationObject is an NSManagedObject and not a Collection object...
Console
(lldb) po [self.destinationObject class]
NSManagedObject
I hope you could lead me to the right way :) Thank you!

It appears that you have not specified that the entity should use the Collection class in the Core Data model. If you don't specify anything then NSManagedObject will be used by default.

Related

Storing and retrieving a custom object from NSPasteBoard

I have an object that belongs to this class that contains the following declarations:
HEADER
#interface MyClassObject : NSObject <NSCopying, NSPasteboardWriting, NSPasteboardReading>
#property (nonatomic, strong) NSArray *children;
#property (nonatomic, assign) NSInteger type;
#property (nonatomic, strong) NSString *name;
#property (nonatomic, strong) id node;
children holds other objects of this class, node holds objects that can be NSView or NSImageView.
I would like to be able to copy and paste objects of this type to/from NSPasteboard.
I have google around but the explanations are vague.
What should I do to make this class copiable/readable to NSPasteboard or in other words, make it conform to NSPasteboardWriting and NSPasteboardReading protocols?
I don't have the slightest clue on how this is done and as usual, Apple documetation and nothing is the same thing.
This is the complete solution.
First thing is to make the class conforming to NSCoding too. In that case the class declaration will be
#interface MyClassObject : NSObject <NSCopying, NSPasteboardWriting, NSPasteboardReading, NSCoding>
To make it compatible with NSCoding you have to implement encodeWithCoder: and initWithCoder:... in my case:
- (void)encodeWithCoder:(NSCoder *)coder {
[coder encodeObject:#(self.type) forKey:#"type"];
[coder encodeObject:self.name forKey:#"name"];
// converting the node to NSData...
// the object contained in node must be compatible with NSCoding
NSData *nodeData = [NSKeyedArchiver archivedDataWithRootObject:self.node];
[coder encodeObject:nodeData forKey:#"node"];
NSData *childrenData = [NSKeyedArchiver archivedDataWithRootObject:self.children];
[coder encodeObject:childrenData forKey:#"children"];
}
- (id)initWithCoder:(NSCoder *)coder {
self = [super init];
if (self) {
_type = [[coder decodeObjectForKey:#"type"] integerValue];
_name = [coder decodeObjectForKey:#"name"];
NSData *nodeData = [coder decodeObjectForKey:#"node"];
_efeito = [NSKeyedUnarchiver unarchiveObjectWithData:nodeData];
NSData *childrenData = [coder decodeObjectForKey:#"children"];
_children = [NSKeyedUnarchiver unarchiveObjectWithData:childrenData];
_parent = nil; // I want this to be nil when the object is recreated
}
To make the class work with NSPasteboard you have to add these 4 methods:
-(id)initWithPasteboardPropertyList:(id)propertyList ofType:(NSString *)type {
return [NSKeyedUnarchiver unarchiveObjectWithData:propertyList];
}
+(NSArray *)readableTypesForPasteboard:(NSPasteboard *)pasteboard {
// I am using the bundleID as a type
return #[[[NSBundle mainBundle] bundleIdentifier]];
}
- (NSArray *)writableTypesForPasteboard:(NSPasteboard *)pasteboard {
// I am using the bundleID as a type
return #[[[NSBundle mainBundle] bundleIdentifier]];
}
- (id)pasteboardPropertyListForType:(NSString *)type {
// I am using the bundleID as a type
if(![type isEqualToString:[[NSBundle mainBundle] bundleIdentifier]]) {
return nil;
}
return [NSKeyedArchiver archivedDataWithRootObject:self];
}
Then, on the class you implement the copy and paste you add:
- (void)copy:(id)sender {
NSPasteboard *pasteBoard = [NSPasteboard generalPasteboard];
[pasteBoard clearContents];
NSArray *copiedObjects = #[theObjectYouWantToCopyToThePasteboard];
[pasteBoard writeObjects:copiedObjects];
}
- (void)paste:sender {
NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
NSArray *classArray = #[[LayerObject class]];
NSDictionary *options = [NSDictionary dictionary];
BOOL ok = [pasteboard canReadItemWithDataConformingToTypes:#[[[NSBundle mainBundle] bundleIdentifier]]];
if (ok) {
NSArray *objectsToPaste = [pasteboard readObjectsForClasses:classArray options:options];
MyObjectClass *object = [objectsToPaste objectAtIndex:0];
// object is the one you have copied to the pasteboard
// now you can do whatever you want with it.
// add the code here.
}
}
Ill only discuss writing. it is basically the same for reading (but in reverse of course ;))
write
declare the type you write
- (NSArray *)writableTypesForPasteboard:(NSPasteboard *)pasteboard {
return #[#"com.mycompany.mytype"];
}
write the data
- (id)pasteboardPropertyListForType:(NSString *)type {
//check the type we are asked to write
if(![type isEqualToString:#"com.mycompany.mytype"]) return nil;
//create a _plist_ object
// :: ONLY PLIST TYPES
NSMutableDictionary *plist = [[NSMutableDictionary alloc] init];
...
return plist;
}

AFNetworking 2.0 and simple JSON

I'm trying to use AFNetworking 2.0 consuming a simple json, the result from the json is:
{
"solicitudId": "61898",
"estado": "Atendida",
"tipoPago": null,
"monto": 23,
"mayorDerecho": 0,
"sistema": "SPRL"
}
I defined a class (solicitud.h solicitud.m) like this:
#interface SolicitudNSDictionary : NSDictionary
- (NSString *)solicitudId;
- (NSString *)estado;
- (NSString *)tipoPago;
- (NSNumber *)monto;
- (NSNumber *)mayorDerecho;
- (NSString *)sistema;
#end
the json is call here without error
- (IBAction)jsonButton:(id)sender {
// 1
NSString *string = [NSString stringWithFormat:#"%#solicitud?id=61898&from=MOVIL&ip=172.9.1.14", BaseURLString];
NSURL *url = [NSURL URLWithString:string];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
// 2
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
operation.responseSerializer = [AFJSONResponseSerializer serializer];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
// 3
self.solicitud = (NSDictionary *)responseObject;
self.title = #"JSON Retrieved";
[self.tableView reloadData];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
// 4
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Error Retrieving Weather"
message:[error localizedDescription]
delegate:nil
cancelButtonTitle:#"Ok"
otherButtonTitles:nil];
[alertView show];
}];
// 5
[operation start];
}
#end
My problem is I don't know how implement the tableview from the NSDictionary, on the cellForRowAtIndexPath i tried but i have not luck.
I declared in the #interface
#property(strong) NSDictionary *solicitud;
and this variable is set here
// 3
self.solicitud = (NSDictionary *)responseObject;
Where I get error is
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"cellName";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
NSDictionary *solicitudTmp = nil;
solicitudTmp = [self.solicitud];
// You will add code here later to customize the cell, but it's good for now.
cell.textLabel.text = [self.solicitudTmp solicitudId];
return cell;
}
At this line
solicitudTmp = [self.solicitud];
solicitudTmp = [self.solicitud]; is a syntax error.
Objective-C messages exist in the format [receiver message]. The code above is missing a message.
What you probably intended was:
cell.textLabel.text = [self.solicitud valueForKey:#"solicitudId"];
That said, there's a lot of other really questionable stuff going on in this question:
SolicitudNSDictionary, as a model, should be a subclass of NSObject that has an initializer that takes a dictionary (NSDictionary should not be subclassed)
You should let AFNetworking take care of turning URL parameters into a query string.
Your operation code could be improved by using an AFHTTPRequestOperationManager, instead of constructing requests yourself.
I would strongly encourage you to look at some additional resources before going forward. Apple's Developer Site has a number of programming guides and other reference material to help get you started.

Query data from Parse.com, iterate through, add certain parts to an NSObject, and add the object to an array of objects

I'm using iOS7 Xcode 5 with Parse.com's SDK. While querying data via parse, I'm trying to construct a Person (NSObject) for each returned object and create an NSArray of defaultPeople.
Here is the code for the Person:
Person.h
// Person.h
#import <Foundation/Foundation.h>
#interface Person : NSObject
#property (nonatomic, strong) NSString *name;
#property (nonatomic, strong) UIImage *image;
#property (nonatomic, assign) NSUInteger age;
#property (nonatomic, strong) NSString *gender;
#property (nonatomic, strong) NSString *location;
#property (nonatomic, strong) NSString *tagline;
#property (nonatomic, strong) NSString *objectId;
- (instancetype)initWithName:(NSString *)name
image:(UIImage *)image
age:(NSUInteger)age
gender:(NSString*)gender
location:(NSString*)location
tagline:(NSString*)tagline
objectId:(NSString*)objectId;
#end
Person.m:
// Person.m
#import "Person.h"
#implementation Person
#pragma mark - Object Lifecycle
- (instancetype)initWithName:(NSString *)name
image:(UIImage *)image
age:(NSUInteger)age
gender:(NSString*)gender
location:(NSString *)location
tagline:(NSString*)tagline
objectId:(NSString *)objectId {
self = [super init];
if (self) {
_name = name;
_image = image;
_age = age;
_gender = gender;
_location = location;
_tagline = tagline;
_objectId = objectId;
}
return self;
}
#end
Now here's the code I am using to try and create the array in my view controller .m file :
- (NSArray *)defaultPeople {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSLog(#"Current City for Querying: %#", [defaults objectForKey:#"CurrentCity"]);
if ([defaults objectForKey:#"CurrentCity"]) {
PFQuery *query = [PFQuery queryWithClassName:#"_User"];
[query whereKey:#"CurrentCity" equalTo:[defaults objectForKey:#"CurrentCity"]];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
// The find succeeded.
NSLog(#"Successfully retrieved %d scores.", objects.count);
// Do something with the found objects
for (PFObject *object in objects) {
NSString *userID = object.objectId;
NSString *first = [object objectForKey:#"FirstName"];
NSString *city = [object objectForKey:#"CurrentCity"];
NSUInteger age = (int)[object objectForKey:#"Age"];
NSString *gender = [object objectForKey:#"Gender"];
NSString *tagline = [object objectForKey:#"Tagline"];
Person *p = [[Person alloc]
initWithName:first
image:[UIImage imageWithData:
[NSData dataWithContentsOfURL:
[NSURL URLWithString:
[object objectForKey:#"PictureURL"]]]]
age:age
gender:gender
location:city
tagline:tagline
objectId:userID];
[self.people addObject:p]
}
} else {
NSLog(#"Error: %# %#", error, [error userInfo]);
}
}];
}
return self.people; //people was defined in the interface as:
//#property (nonatomic, strong) NSMutableArray *people;
}
I know that the querying is fine because I've NSLogged each NSString/NSUInteger in the for loop and it always returns the right value. My problem is creating a new Person object from those values and adding it to the defaultPeople array after each iteration. The result of this code is that my defaultPeople array always returns (null). PLEASE HELP!!! Thanks :)
Clayton
Ok guys FINALLY I figured out how do do this in a block that actually works:
- (void)queryForAllPostsNearLocation:(CLLocation *)currentLocation withNearbyDistance:(CLLocationAccuracy)nearbyDistance {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setBool:1 forKey:#"Users"];
PFQuery *query = [PFQuery queryWithClassName:#"_User"];
// If no objects are loaded in memory, we look to the cache first to fill the table
// and then subsequently do a query against the network.
if (query.countObjects == 0) {
query.cachePolicy = kPFCachePolicyCacheThenNetwork;
}
// Create a PFGeoPoint using the current location (to use in our query)
PFGeoPoint *userLocation =
[PFGeoPoint geoPointWithLatitude:[Global shared].LastLocation.latitude longitude:[Global shared].LastLocation.longitude];
// Create a PFQuery asking for all wall posts 1km of the user
[query whereKey:#"CurrentCityCoordinates" nearGeoPoint:userLocation withinKilometers:10];
// Include the associated PFUser objects in the returned data
[query includeKey:#"objectId"];
//Run the query in background with completion block
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (error) { // The query failed
NSLog(#"Error in geo query!");
} else { // The query is successful
defaultPeople = [[NSMutableArray alloc] init];
// 1. Find new posts (those that we did not already have)
// In this array we'll store the posts returned by the query
NSMutableArray *people = [[NSMutableArray alloc] initWithCapacity:100];
// Loop through all returned PFObjects
for (PFObject *object in objects) {
// Create an object of type Person with the PFObject
Person *p = [[Person alloc] init];
NSString *userID = object.objectId;
p.objectId = userID;
NSString *first = [object objectForKey:#"FirstName"];
p.name = first;
NSString *city = [object objectForKey:#"CurrentCity"];
p.location = city;
NSString *age = [object objectForKey:#"Age"];
p.age = age;
NSString *gender = [object objectForKey:#"Gender"];
p.gender = gender;
NSString *tagline = [object objectForKey:#"Tagline"];
p.tagline = tagline;
UIImage *img = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:[NSString stringWithFormat:#"%#",[object objectForKey:#"PictureURL"]]]]];
p.image = img;
if (![p.objectId isEqualToString:myID] && ![p.gender isEqualToString:myGender] && ![people containsObject:p]) {
[people addObject:p];
NSLog(#"Person: %#",p);
}
}
[defaultPeople addObjectsFromArray:people];
[[Global shared] setDefaultPeople:defaultPeople];
NSLog(#"Default People: %#",[Global shared].defaultPeople);
NSLog(#"Success. Retrieved %lu objects.", (unsigned long)[Global shared].defaultPeople.count);
if (defaultPeople.count == 0) {
[defaults setBool:0 forKey:#"Users"];
} else {
[defaults setBool:1 forKey:#"Users"];
}
}
}];
}
The BOOL returns on the bottom are to let the controller know whether or not to switch view controllers when prompted. If the switch controller toggle is hit, it only switches if the BOOL = 1, i.e. there are people in the area.
Thanks for all your help guys. Seriously.
[self.people addObject:p] is happening in the background thread so "return self.people" happens before self.people is updated. Thats why it is always returns nil.
instead of [query findObjectsInBackground] you can do
NSArray *objects = [query findObjects]
You need to return people inside the block, otherwise it will hit the return statement before it finishes finding the objects. It's finding them asynchronously with the block.
Another alternative is to get rid of the block and do:
NSArray *array = [query findObjects];
for (PFObject *object in array) {
NSString *userID = object.objectId;
NSString *first = [object objectForKey:#"FirstName"];
NSString *city = [object objectForKey:#"CurrentCity"];
NSUInteger age = (int)[object objectForKey:#"Age"];
NSString *gender = [object objectForKey:#"Gender"];
NSString *tagline = [object objectForKey:#"Tagline"];
Person *p = [[Person alloc]
initWithName:first
image:[UIImage imageWithData:
[NSData dataWithContentsOfURL:
[NSURL URLWithString:
[object objectForKey:#"PictureURL"]]]]
age:age
gender:gender
location:city
tagline:tagline
objectId:userID];
[self.people addObject:p];
}
return self.people;

Calling an API with UISearchBar

I have a UITableView that displays results from an API. The API is called whenever the user types into the UISearchBar via searchBar:textDidChange:. Effectively implementing an autocomplete search. My problem is the results loaded into the UITableView seem to be an iteration behind the last API call.
Example:
User types "union" into the UISearchBar, however no results are shown in the UITableView. User types any character after "union", "unions" for example, and the API results from "union" are displayed in the UITableView. When user scrolls down through results (of "unions", but really "union") "repopulated cells" display "unions" result.
SearchViewController.h
#import <UIKit/UIKit.h>
#interface SearchViewController : UIViewController <UITextFieldDelegate, UISearchBarDelegate, UITableViewDelegate, UITableViewDataSource, UISearchDisplayDelegate>{
UITableView *searchTableView;
UISearchBar *sBar;
UISearchDisplayController *searchDisplayController;
}
#property (strong, nonatomic) NSArray *loadedSearches;
#end
SearchViewController.m
#import "SearchViewController.h"
#import "AFJSONRequestOperation.h"
#interface SearchViewController ()
#end
#implementation SearchViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
self.title = #"Search";
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
searchTableView = [[UITableView alloc] initWithFrame:self.view.bounds];
searchTableView.delegate = self;
searchTableView.dataSource = self;
[self.view addSubview:searchTableView];
sBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0, 0, 160, 44)];
sBar.placeholder = #"Bus Route to...";
sBar.delegate = self;
searchDisplayController = [[UISearchDisplayController alloc] initWithSearchBar:sBar contentsController:self];
searchDisplayController.delegate = self;
searchDisplayController.searchResultsDataSource = searchTableView.dataSource;
searchDisplayController.searchResultsDelegate = searchTableView.delegate;
searchTableView.tableHeaderView = sBar;
}
-(void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
{
NSString *searchQuery = [NSString stringWithFormat:#"https://api.foursquare.com/v2/venues/search?ll=40.4263,-86.9177&client_id=xxx&client_secret=yyy&v=20121223&query='%#'",searchText];
searchQuery = [searchQuery stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSURL *url = [[NSURL alloc] initWithString:searchQuery];
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:url];
AFJSONRequestOperation *operation = [AFJSONRequestOperation
JSONRequestOperationWithRequest:request
success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON){
self.loadedSearches = JSON[#"response"][#"venues"];
} failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON){
NSLog(#"%#", error.localizedDescription);
}];
[operation start];
[searchTableView reloadData];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return self.loadedSearches.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell"];
if(cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"Cell"];
}
cell.textLabel.text = self.loadedSearches[indexPath.row][#"name"];
return cell;
}
#end
If my problem isn't clear, let me know.
Feel free to critique other aspects of the code, however I really would appreciate the solution to my problem:) Thanks in advance.
Example API response - http://pastebin.com/UZ1H2Zwy
The problem seems to be that you are refreshing the table before you get the data as you are making an asynchronous operation with AFJSONRequestOperation. So your model is probably getting updated correctly but your tableview is one refresh behind. Try moving [searchTableView reloadData] inside the block success callback:
AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request
success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON)
{
self.loadedSearches = JSON[#"response"][#"venues"];
// refreshing the TableView when the block gets the response
[searchTableView reloadData];
}
failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON)
{
NSLog(#"%#", error.localizedDescription);
}];
Hope this works.
Your requests work asynchronously, it is not probably related with scroll or something. Just result returns at that time. Try to cancel the previous requests. For example if you try to search "unions" then cancel the "union" request. Hope it helps.

RestKit iOS - Second Time fails

I'm using the RestKit for a simple request that returns JSON data.
I've implemented this code that works only the first time that the viewDidLoad is called, the second time the request is not performed. Why??
- (void)viewDidLoad
{
[super viewDidLoad];
[self planOtpTrip];
}
-(void) planOtpTrip{
NSLog(#"[INFO] planoOtpTrip");
client = [RKClient clientWithBaseURLString:#"http://myserver/opentripplanner-api-webapp/ws"];
[client setValue:#"application/json" forHTTPHeaderField:#"Accept"];
NSDictionary *queryParameters = [NSDictionary dictionaryWithObjectsAndKeys:#"45.028952,7.624598",#"fromPlace",#"45.06123,7.65981", #"toPlace",#"TRANSIT,WALK" ,#"mode", nil];
// Imposto il nome della API da richiamare
NSString *getResourcePath = RKPathAppendQueryParams(#"/plan", queryParameters);
[client get:getResourcePath delegate:self];
}
- (void)request:(RKRequest*)request didLoadResponse:(RKResponse*)response {
// where you handle response object
NSLog(#"Risposta ricevuta: %#", response.URL);
NSLog(#"Response Body: %#", response.bodyAsString);
id jsonParser = [[RKParserRegistry sharedRegistry] parserForMIMEType:RKMIMETypeJSON];
NSError *error = nil;
NSDictionary* parsedResponse = [jsonParser objectFromString:[response bodyAsString] error:&error];
if (error == nil)
{
NSLog(#"GET returned with HTTP Code %d and parsedContent: %#", [response statusCode], parsedResponse);
}
else
{
NSLog(#"Error: %#", error);
}
return [self renderResponse:parsedResponse];
}
I had this problem as well but i found a solution. Define the RKClient in your .h file like so:
#property (retain) RKClient *downloadClient;
Then in your .m file synthesize the downloadClient like so:
#synthesize downloadClient;
And then in your didLoadResponse method you release the downloadClient when your done with it, like so:
- (void)request:(RKRequest*)request didLoadResponse:(RKResponse*)response
{
//do something with your response
[downloadClient release];
}

Resources