NSFetchedResultsController with AFNetworking and MagicalRecord - ios

When should I init my NSFetchedResultsController with AFNetworking? Currently I am doing with this:
[[AFHttpClient sharedClient] GET:#"/admin/stockCategories" parameters:nil success:^(NSURLSessionDataTask *task, id responseObject) {
NSHTTPURLResponse *response = (NSHTTPURLResponse *)task.response;
if (response.statusCode == 200) {
NSArray *results = (NSArray *)responseObject;
if ([results count] > 0) {
for (NSDictionary *obj in results) {
StockCategory *category = [StockCategory MR_createEntity];
category.name = obj[#"name"];
category.categoryId = obj[#"_id"];
category.createdDate = [NSDate dateForRFC3339DateTimeString:obj[#"createdDate"]];
[context MR_saveWithOptions:MRSaveParentContexts completion:^(BOOL success, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
[SVProgressHUD dismiss];
[self.tableView reloadData];
});
}];
}
} else {
}
}
} failure:^(NSURLSessionDataTask *task, NSError *error) {
}];
I know normally I put execute fetch request in success block after I saved object to core data. Should I put do this:
[context MR_saveWithOptions:MRSaveParentContexts completion:^(BOOL success, NSError *error) {
// add NSFetchedResultsController here ?
self.fetchedResultsController = [StockCategory MR_fetchAllSortedBy:#"createdDate" ascending:NO withPredicate:nil groupBy:nil delegate:self];
dispatch_async(dispatch_get_main_queue(), ^{
[SVProgressHUD dismiss];
[self.tableView reloadData];
});
}];
if so, self.fetchedResultsController will be init multiple times. I don't think so. What I am currently do is this:
- (NSFetchedResultsController *)fetchedResultsController{
if (!_fetchedResultsController) {
_fetchedResultsController = [StockCategory MR_fetchAllSortedBy:#"createdDate" ascending:NO withPredicate:nil groupBy:nil delegate:self];
}
return _fetchedResultsController;
}
But in my UITableViewDatasource methods, only numberOfRows method works, but the cell.textLabel.text is empty. But if I relaunch the simulator, and comment the afnetworking get method. everything works, objects have been saved the core data, and NSFetchedResultsController works too.

Related

Code Review for filling tableview cells with response returned from server

Connection Class
-(NSDictionary *)getResponseFromSearchByRoutewithUrl:(NSString *)url :(HttpCompletionBlock)completionHandler {
NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url]];
[urlRequest setHTTPMethod:#"GET"];
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
NSDictionary *responseDictionary;
NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration];
NSURLSessionDataTask *task = [session dataTaskWithRequest: urlRequest completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
//check if we encountered an error
if(error != nil){
NSLog(#"%#", [error localizedDescription]);
}else{
//get and check the HTTP status code
NSInteger HTTPStatusCode = [(NSHTTPURLResponse *)response statusCode];
if (HTTPStatusCode != 200) {
NSLog(#"HTTP status code = %ld", (long)HTTPStatusCode);
}
[task resume];
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
if(data != nil){
NSError *parseError = nil;
NSDictionary *responseDictionary = [NSJSONSerialization JSONObjectWithData:data options:0 error:&parseError];
NSLog(#"The response is - %#",responseDictionary);
return completionHandler(responseDictionary,nil);
}
else {
return completionHandler(nil,error);
}
}];
}
}];
[task resume];
return responseDictionary;
}
ViewController having Table view cells Class
-(void)viewDidLoad
{
_appDelegate= (AppDelegate *)[[UIApplication sharedApplication] delegate];
[[ConnectionManager sharedConnectionManager]getResponseFromSearchByRoutewithUrl:_urlString :^(NSDictionary *responseDictionary, NSError *error) {
if(error == nil)
{
_appDelegate.flightsArray=[responseDictionary objectForKey:#"scheduledFlights"];
NSLog(#"the flights array is%# ", _appDelegate.flightsArray);
}
else
{
NSLog(#"Error == %# ",error);
}
}];
}
Table View delegate class methods
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return _appDelegate.flightsArray.count;
}
Kindly Review the code and tell why the number of rows is zero...even when the response dictionary is returning dictionary...
try below code
-(void)viewDidLoad
{
_appDelegate= (AppDelegate *)[[UIApplication sharedApplication] delegate];
[[ConnectionManager sharedConnectionManager]getResponseFromSearchByRoutewithUrl:_urlString :^(NSDictionary *responseDictionary, NSError *error) {
if(error == nil)
{
_appDelegate.flightsArray=[responseDictionary objectForKey:#"scheduledFlights"];
NSLog(#"the flights array is%# ", _appDelegate.flightsArray);
[self performSelector:#selector(reloadTableview) withObject:nil afterDelay:0.2];
}
else
{
NSLog(#"Error == %# ",error);
}
}];
}
- (void)reloadTableview{
[self.tableview reloadData];
}

How can I write completion block with nullable?

When I call this method with nil, the app crashes, but I want to know how to write it with nullable.
CRASH
[KPTaxnoteApiSaveHandler saveEntryWithUuid:uuid completion:nil];
OK
[KPTaxnoteApiSaveHandler saveEntryWithUuid:uuid completion:^(NSError *error) {}];
This is the code.
+ (void)saveEntryWithUuid:(NSString *)uuid completion:(void (^ __nullable)(NSError * _Nullable error))completion {
NSLog(#"saveEntryWithUuid");
Entry *entry = [Entry MR_findFirstByAttribute:#"uuid" withValue:uuid];
NSDictionary *params = #{#"entry[uuid]":entry.uuid};
[KPTaxnoteApiSaveHandler postWithUrl:kApiUrlStringForEntry params:params completion:^(NSError *error) {
if (!error) {
[MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext) {
Entry *entry = [Entry MR_findFirstByAttribute:#"uuid" withValue:uuid inContext:localContext];
entry.needSave = #NO;
}];
}
completion(error);
}];
+ (void)postWithUrl:(NSString *)urlStr params:(NSDictionary *)params completion:(nullable void (^)(NSError *_Nullable error))completion {
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
[manager POST:urlStr parameters:params success:^(AFHTTPRequestOperation *operation, id responseObject) {
completion(nil);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
completion(error);
}];
Where is the crash happening? My first guess is you need to do something like this:
if (completion) {
completion(nil); // Or completion(error);
}
This will handle the case where the completion is nil.

JSON Request using AFNetworking 2 giving me a NSURLErrorDomain error

I am making a simple GET request using AFNetworking 2, but I am getting a NSURLErrorDomain error.
I created a manager class which subclasses AFHTTPRequestOperationManager and creates a singleton instance so that I can use a shared manager.
+ (id)manager {
static dispatch_once_t pred = 0;
__strong static id _sharedObject = nil;
dispatch_once(&pred, ^{
_sharedObject = [[self alloc] init];
});
return _sharedObject;
}
- (id)init {
NSURL *baseURL = [ZSSAuthentication baseURL];
self = [super initWithBaseURL:baseURL];
if (self) {
[self setRequestSerializer:[AFJSONRequestSerializer serializer]];
[self setResponseSerializer:[AFJSONResponseSerializer serializer]];
[self.requestSerializer setAuthorizationHeaderFieldWithUsername:[ZSSAuthentication username] password:[ZSSAuthentication password]];
[[AFNetworkActivityIndicatorManager sharedManager] setEnabled:YES];
}
return self;
}
- (void)getData:(NSString *)pubID parameters:(NSDictionary *)parameters completion:(void (^)(NSDictionary *results))completion failure:(void (^)(NSError *error))failure {
NSString *url = [NSString stringWithFormat:#"data/all/%#", pubID];
[self GET:url parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject) {
// Check to see if there are errors
ZSSError *error = [self errorForAPICall:responseObject status:[operation.response statusCode]];
if (error) {
[self logMessage:error.localizedDescription];
failure(error);
return;
}
NSDictionary *data = [responseObject objectForKey:#"data"];
completion(data);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
failure(error);
}];
}
Then, in my viewController's viewDidLoad method I make a call to that method:
[[ZSSManager manager] getData:self.pubID parameters:nil completion:^(NSDictionary *results) {
self.items = results;
[self dataWillReload];
NSLog(#"%#", results);
[self.tableView reloadData];
} failure:^(NSError *error) {
NSLog(#"Error: %# %li", error, (long)error.code);
}];
Then I get this error:
Error Domain=NSURLErrorDomain Code=-999 "The operation couldn’t be completed. (NSURLErrorDomain error -999.)" UserInfo=0x7ff952306610 {NSErrorFailingURLKey=http://test.mysite.com/v1/data/all/5}
The strange thing is, on a previous viewController, I make a different call to the manager, and it completes and returns data correctly. But, when I make this second call, I get the error. AND, if I move that getData call out of the viewDidLoad method, and invoke it with a button press, it DOES WORK. What the heck?
What could be causing this?

Post request response is not saved in afnetworking 2.0

I am having problems in saving a response coming from a POST request.
Based on AFnetworking documentation and NSScreencast tutorial I created my own subclass of AFHTTPRequestOperationManager, but I am not sure why the response is not saved.
How do I know, the response is not saved?
Because there is an error:(null) message in console and the my method does not perform segue.
I know that I am getting the values, because of the breakpoint that I put NSURLSessionDataTask
But I do not know why the values are not saved and I am getting an error message. I appreciate any help.
The APIClient/Manager
AuthAPIManager.h
#import "AFHTTPSessionManager.h"
#interface AuthAPIManager : AFHTTPSessionManager
+(AuthAPIManager *)sharedManager;
-(NSURLSessionDataTask *)initializeLogin:(NSString *)username completion:(void(^)(NSDictionary *results, NSError *error))completion;
//for login
#property (nonatomic,readonly,retain)NSString *StoreIdentifierForVendor;
#property(nonatomic,copy)NSString *devicetype;
#end
AuthAPIManager.m
#import "AuthAPIManager.h"
#import "LoginInfo.h"
#import "CredentialStore.h"
static AuthAPIManager *sharedManager =nil;
static dispatch_once_t onceToken;
#implementation AuthAPIManager
+(AuthAPIManager *)sharedManager
{
dispatch_once(&onceToken, ^{
sharedManager = [[AuthAPIManager alloc] initWithBaseURL:[NSURL URLWithString:BASE_URL]];
sharedManager.responseSerializer=[AFJSONResponseSerializer serializer];
sharedManager.requestSerializer = [AFJSONRequestSerializer serializer];
});
return sharedManager;
}
-(id)initWithBaseURL:(NSURL *)url
{
self = [super initWithBaseURL:url];
if (self) {
}
return self;
}
-(NSURLSessionDataTask *)initializeLogin:(NSString *)username completion:(void (^)(NSDictionary *, NSError *))completion
{
_devicetype = #"ios";
_StoreIdentifierForVendor = [[[UIDevice currentDevice]identifierForVendor]UUIDString];
id loginParameters =#{#"AccountId":username,
#"DeviceType":_devicetype
};
NSURLSessionDataTask *task =[self POST:#"/Accn" parameters:loginParameters
success:^(NSURLSessionDataTask *task, id responseObject)
{
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)task.response;
if (httpResponse.statusCode == 200) {
LoginInfo *loginInfo =[[LoginInfo alloc]initWithDictionary:responseObject];
CredentialStore *credStore =[CredentialStore sharedStore];
credStore.loginInfo =loginInfo;
completion(responseObject,nil);
loginInfo = responseObject;
} else {
dispatch_async(dispatch_get_main_queue(), ^{
completion(nil, nil);
});
NSLog(#"Received: %#", responseObject);
NSLog(#"Received HTTP %d", httpResponse.statusCode);
}
} failure:^(NSURLSessionDataTask *task, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
completion(nil, error);
});
}];
return task;
}
#end
And this is how I am calling my method in my view controller
- (IBAction)login:(id)sender
{
[_usernameTextField resignFirstResponder];
[SVProgressHUD show];
NSURLSessionDataTask *task = [[AuthAPIManager sharedManager] initializeLogin:self.usernameTextField.text completion:^(NSDictionary *results, NSError *error)
{
if (results) {
LoginInfo *loginInfo = [[LoginInfo alloc]initWithDictionary:results];
CredentialStore *credStore =[ CredentialStore sharedStore];
credStore.loginInfo =loginInfo;
[self performSegueWithIdentifier:#"welcomeViewSegue" sender:self];
}
else
{
NSLog(#"there is an error:%#",error);
}
}];
[SVProgressHUD dismiss];
}
Your success block is being called, but your status code is not 200.
You are calling your completion block like this:
completion(nil, nil);
So this code:
if (results) {
[snip]
}
else
{
NSLog(#"there is an error:%#",error);
}
is passed a nil results object.
Set a breakpoint on if (httpResponse.statusCode == 200) and inspect httpResponse in your debugger to see why it's not what you expect. (You may get a different success code, such as 204.)
Call your completion block with results instead of nil.

remove table rows before reload

I want to remove all rows in my table view before reloading the data, but can't seem to get it to work.
In my viewcontroller I get my array from this AFNetworking request.
- (void)viewDidLoad
[[LocationApiClient sharedInstance] getPath:#"locations.json"
parameters:nil
success:
^(AFHTTPRequestOperation *operation, id response) {
NSLog(#"Response: %#", response);
NSMutableArray *location_results = [NSMutableArray array];
for (id locationDictionary in response) {
Location *location = [[Location alloc] initWithDictionary:locationDictionary];
[location_results addObject:location];
}
self.location_results = location_results;
[self.tableView reloadData];
}
failure:
^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error fetching locations!");
NSLog(#"%#", error);
}];
}
And I want to remove data then reload it with this button
- (IBAction)locationPressed:(id)sender {
[[Location sharedSingleton].locationManager startUpdatingLocation];
[self viewDidLoad];
NSMutableArray *location_results = [NSMutableArray array];
[location_results removeAllObjects];
[self.tableView reloadData];
}
But it's not removing the rows. I see the reload happening over the top of the rows that are already there. Any suggestions?
Please DON'T EVER call viewDidLoad manually.
Create a method like
- (void)reloadDataWithCompletion:(void(^)(NSArray *locations))completion
failure:(void(^)(NSError *error))failure {
[[LocationApiClient sharedInstance] getPath:#"locations.json"
parameters:nil
success:
^(AFHTTPRequestOperation *operation, id response) {
NSLog(#"Response: %#", response);
NSMutableArray *location_results = [NSMutableArray array];
for (id locationDictionary in response) {
Location *location = [[Location alloc] initWithDictionary:locationDictionary];
[location_results addObject:location];
}
if(completion)
completion(location_results);
}
failure:
^(AFHTTPRequestOperation *operation, NSError *error) {
if(failure)
failure(error);
}];
}
And call it whenever you need to reload the data
- (void)viewDidLoad {
[super viewDidLoad]; // Don't forget the call to super!
[self reloadDataWithCompletion:^(NSArray *locations) {
self.location_results = locations;
[self.tableView reloadData];
} failure:^(NSError *error) {
NSLog(#"Error fetching locations!");
NSLog(#"%#", error);
}];
}
- (IBAction)locationPressed:(id)sender {
[[Location sharedSingleton].locationManager startUpdatingLocation];
[self reloadDataWithCompletion:^(NSArray *locations) {
self.location_results = locations;
[self.tableView reloadData];
} failure:^(NSError *error) {
NSLog(#"Error fetching locations!");
NSLog(#"%#", error);
}];
}
In order to achieve a graphical effect for reloading the table you can do (assuming that you have only one section), substitute
[self.tableView reloadData];
with
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationFade];

Resources