iOS app terminates in Xcode simulator in connectionDidFinishLoading on the following line of code.
NSMutableDictionary *dict = [parser objectWithString:[[NSString alloc] initWithData:dataForConnection encoding:NSUTF8StringEncoding] error:nil];
Following the more code of this function
- (void)connectionDidFinishLoading:(NSURLConnection*)connection {
NSMutableData *dataForConnection = [self dataForConnection:(URLConnection*)connection];
NSInteger statusCode=[((URLConnection*)connection).response statusCode];
NSString *tag=((URLConnection*)connection).tagKey;
[self removeReceivedDataHandle:tag];
if (statusCode != 200 && statusCode!=204 && statusCode!=405){
[reportActivityIndicator stopAnimating];
[transactionsActivityIndicator stopAnimating];
[swipeHQCheckout showMessage:PHRASE_ServerCommunicationError];
return;
}
if ([reports count] == 0) {
[self removeReceivedDataHandle];
[transactionsActivityIndicator stopAnimating];
return;
}
SBJsonParser *parser = [[SBJsonParser alloc] init];
[reports removeObject:tag];
if ([tag isEqualToString:API_TransactionReport] ||
[tag isEqualToString:API_FetchTransactions]) {
NSMutableDictionary *dict = [parser objectWithString:[[NSString alloc] initWithData:dataForConnection encoding:NSUTF8StringEncoding] error:nil]; // here issue
NSString *response_code=[dict objectForKey:#"response_code"];
// more code down here
}
What could be the issue, advanced thanks for the suggestions.
It seems your NSMutableData dataForConnection is coming nil and you are initializing a NSMutableDictionary with nil value
To avoid crash :
if (dataForConnection != nil){
NSMutableDictionary *dict = [parser objectWithString:[[NSString alloc] initWithData:dataForConnection encoding:NSUTF8StringEncoding] error:nil];
}else{
NSLog(#"NO Data");
}
You should add the check at the start of the method to follow best practise, like
if (dataForConnection == nil){
NSLog(#"NO Data");
return;
}else{
//Do whatever you want to do
}
Related
I have create a demo for downloading a file from the server it is working fine with foreground and background, but when I'm going to resume it, It will giving below Error
I have stcuk here could help me so solved it, I search in internet but I couldn't find anything helpful.
Download file and Pause Downlaod is working fine I have issue to resume it.
Task <3AE5BF24-3A4E-4713-8FC2-A0032022C913>.<6> finished with error [-3003] Error Domain=NSURLErrorDomain Code=-3003 "(null)" UserInfo={_NSURLErrorRelatedURLSessionTaskErrorKey=(
"BackgroundDownloadTask <3AE5BF24-3A4E-4713-8FC2-A0032022C913>.<6>"
), _NSURLErrorFailingURLSessionTaskErrorKey=BackgroundDownloadTask <3AE5BF24-3A4E-4713-8FC2-A0032022C913>.<6>}
Here is the final Xcode project
Downlaod xcode project
#import "NSURLSession+ResumeData.h"
#import <UIKit/UIKit.h>
#define IS_IOS10ORLATER ([[[UIDevice currentDevice] systemVersion] floatValue] >= 10)
#pragma mark- private
static NSData * correctRequestData(NSData *data) {
if (!data) {
return nil;
}
// return the same data if it's correct
if ([NSKeyedUnarchiver unarchiveObjectWithData:data] != nil) {
return data;
}
NSMutableDictionary *archive = [[NSPropertyListSerialization propertyListWithData:data options:NSPropertyListMutableContainersAndLeaves format:nil error:nil] mutableCopy];
if (!archive) {
return nil;
}
NSInteger k = 0;
id objectss = archive[#"$objects"];
while ([objectss[1] objectForKey:[NSString stringWithFormat:#"$%ld",k]] != nil) {
k += 1;
}
NSInteger i = 0;
while ([archive[#"$objects"][1] objectForKey:[NSString stringWithFormat:#"__nsurlrequest_proto_prop_obj_%ld",i]] != nil) {
NSMutableArray *arr = archive[#"$objects"];
NSMutableDictionary *dic = arr[1];
id obj = [dic objectForKey:[NSString stringWithFormat:#"__nsurlrequest_proto_prop_obj_%ld",i]];
if (obj) {
[dic setValue:obj forKey:[NSString stringWithFormat:#"$%ld",i+k]];
[dic removeObjectForKey:[NSString stringWithFormat:#"__nsurlrequest_proto_prop_obj_%ld",i]];
[arr replaceObjectAtIndex:1 withObject:dic];
archive[#"$objects"] = arr;
}
i++;
}
if ([archive[#"$objects"][1] objectForKey:#"__nsurlrequest_proto_props"] != nil) {
NSMutableArray *arr = archive[#"$objects"];
NSMutableDictionary *dic = arr[1];
id obj = [dic objectForKey:#"__nsurlrequest_proto_props"];
if (obj) {
[dic setValue:obj forKey:[NSString stringWithFormat:#"$%ld",i+k]];
[dic removeObjectForKey:#"__nsurlrequest_proto_props"];
[arr replaceObjectAtIndex:1 withObject:dic];
archive[#"$objects"] = arr;
}
}
// Rectify weird "NSKeyedArchiveRootObjectKey" top key to NSKeyedArchiveRootObjectKey = "root"
if ([archive[#"$top"] objectForKey:#"NSKeyedArchiveRootObjectKey"] != nil) {
[archive[#"$top"] setObject:archive[#"$top"][#"NSKeyedArchiveRootObjectKey"] forKey: NSKeyedArchiveRootObjectKey];
[archive[#"$top"] removeObjectForKey:#"NSKeyedArchiveRootObjectKey"];
}
// Reencode archived object
NSData *result = [NSPropertyListSerialization dataWithPropertyList:archive format:NSPropertyListBinaryFormat_v1_0 options:0 error:nil];
return result;
}
static NSMutableDictionary *getResumeDictionary(NSData *data) {
NSMutableDictionary *iresumeDictionary = nil;
if (IS_IOS10ORLATER) {
id root = nil;
id keyedUnarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
#try {
root = [keyedUnarchiver decodeTopLevelObjectForKey:#"NSKeyedArchiveRootObjectKey" error:nil];
if (root == nil) {
root = [keyedUnarchiver decodeTopLevelObjectForKey:NSKeyedArchiveRootObjectKey error:nil];
}
} #catch(NSException *exception) {
}
[keyedUnarchiver finishDecoding];
iresumeDictionary = [root mutableCopy];
}
if (iresumeDictionary == nil) {
iresumeDictionary = [NSPropertyListSerialization propertyListWithData:data options:NSPropertyListMutableContainersAndLeaves format:nil error:nil];
}
return iresumeDictionary;
}
static NSData *correctResumeData(NSData *data) {
NSString *kResumeCurrentRequest = #"NSURLSessionResumeCurrentRequest";
NSString *kResumeOriginalRequest = #"NSURLSessionResumeOriginalRequest";
if (data == nil) {
return nil;
}
NSMutableDictionary *resumeDictionary = getResumeDictionary(data);
if (resumeDictionary == nil) {
return nil;
}
resumeDictionary[kResumeCurrentRequest] = correctRequestData(resumeDictionary[kResumeCurrentRequest]);
resumeDictionary[kResumeOriginalRequest] = correctRequestData(resumeDictionary[kResumeOriginalRequest]);
NSData *result = [NSPropertyListSerialization dataWithPropertyList:resumeDictionary format:NSPropertyListXMLFormat_v1_0 options:0 error:nil];
return result;
}
#implementation NSURLSession (ResumeData)
- (NSURLSessionDownloadTask *)downloadTaskWithCorrectResumeData:(NSData *)resumeData {
NSString *kResumeCurrentRequest = #"NSURLSessionResumeCurrentRequest";
NSString *kResumeOriginalRequest = #"NSURLSessionResumeOriginalRequest";
NSData *cData = correctResumeData(resumeData);
cData = cData ? cData:resumeData;
NSURLSessionDownloadTask *task = [self downloadTaskWithResumeData:cData];
NSMutableDictionary *resumeDic = getResumeDictionary(cData);
if (resumeDic) {
if (task.originalRequest == nil) {
NSData *originalReqData = resumeDic[kResumeOriginalRequest];
NSURLRequest *originalRequest = [NSKeyedUnarchiver unarchiveObjectWithData:originalReqData ];
if (originalRequest) {
[task setValue:originalRequest forKey:#"originalRequest"];
}
}
if (task.currentRequest == nil) {
NSData *currentReqData = resumeDic[kResumeCurrentRequest];
NSURLRequest *currentRequest = [NSKeyedUnarchiver unarchiveObjectWithData:currentReqData];
if (currentRequest) {
[task setValue:currentRequest forKey:#"currentRequest"];
}
}
}
return task;
}
#end
I have a piece of code that execute a coredata update of the database, and I would like to know when that block is finished. Is there a way to get it knowing when the coredata has finished to update the tables?
Main function:
NSMutableArray* responseArray = [NSJSONSerialization JSONObjectWithData:data options:0 error:NULL];
dispatch_async(dispatch_get_main_queue(), ^{
[self parseAndAddLovAll:responseArray toArray:self.objects];
});
Function used in dispatch:
- (void)parseAndAddLovAll:(NSMutableArray*)responseArray toArray:(NSMutableArray*)destinationArray
{
NSError *error;
DB_ListOfValue_manage *elements_to_store = [[DB_ListOfValue_manage alloc] init];
NSManagedObjectContext * context = [elements_to_store managedObjectContext];
for (int index=0; index < [responseArray count]; index++)
{
NSDictionary * responseArray2 = [[NSDictionary alloc] initWithDictionary:responseArray[index]];
NSString * table_to_store = [[NSString alloc] initWithString:[responseArray2 objectForKey:#"table"]];
NSArray * lignes = [[NSArray alloc] initWithObjects:[responseArray2 objectForKey:#"lignes"], nil];
id value;
// Check if LOV table or contact table
if ((([#"Table_contact" compare:table_to_store])!=NSOrderedSame)&&
(([#"Table_event" compare:table_to_store])!=NSOrderedSame))
{
for (NSDictionary * item in lignes[0])
{
value = [item objectForKey:#"codeevent"];
if ([value isEqualToNumber:[NSNumber numberWithInt:EVENT_ID]])
{//FIXME: bug to check when SYNC
elements_to_store = (DB_ListOfValue_manage*)[NSEntityDescription insertNewObjectForEntityForName:table_to_store inManagedObjectContext:context];
elements_to_store.code_event = [value isKindOfClass:[NSNull class]] ? #"" : value;
value = [item objectForKey:#"id"];
elements_to_store.id = [value isKindOfClass:[NSNull class]] ? #"" : value;
value = [item objectForKey:#"used"];
elements_to_store.used = [value isKindOfClass:[NSNull class]] ? #"" : value;
if (![context save:&error]) {
#ifdef DEBUG
NSLog(#"Whoops, couldn't save: %#", [error localizedDescription]);
#endif
}
else{
#ifdef DEBUG
NSLog(#"Data saved to DB, table %# %# %#", table_to_store, elements_to_store.label1, elements_to_store.label2);
#endif
}
}
}
}
}
}
I'm using JSQMessage and am having a little difficulty with showing the placeholder for media until I have it correctly downloading, and then replacing with the media. I have everything working correctly as far as adding the messages and media to server, I just can't get it to replace the placeholders.
Currently, I have a function that queries my database and pulls an array of objects for messages and then loops through and calls this function for each object to output and add it to my message thread. I'm struggling to figure out why the section with "messageToAdd.isMediaMessage" is not replacing the placeholders with the actual media following it's download from the server. Does anyone know how I should be handling this to make sure it adds the message with a placeholder, and then replaces once the media is downloaded correctly?
- (void)addMessage:(PFObject *)object
{
id<JSQMessageMediaData> messageMedia = nil;
PFObject *user = object[#"messageSender"];
[users addObject:user];
NSString *name = #"";
if(user[#"profileFName"] && user[#"profileLName"])
name= [NSString stringWithFormat:#"%# %#",user[#"profileFName"],user[#"profileLName"]];
else
name= [NSString stringWithFormat:#"%# %#",user[#"consultantFName"],user[#"consultantLName"]];
if([object[#"messageFileType"] isEqual: #"video"]){
JSQVideoMediaItem *messageMedia = [[JSQVideoMediaItem alloc] init];
messageMedia.fileURL = nil;
messageMedia.isReadyToPlay = NO;
messageToAdd = [JSQMessage messageWithSenderId:user.objectId displayName:name media:messageMedia];
} else if ([object[#"messageFileType"] isEqual: #"image"]){
JSQPhotoMediaItem *messageMedia = [[JSQPhotoMediaItem alloc] init];
messageMedia.image = nil;
messageToAdd = [JSQMessage messageWithSenderId:user.objectId displayName:name media:messageMedia];
} else{
messageToAdd= [[JSQMessage alloc] initWithSenderId:user.objectId senderDisplayName:name date:object[#"sendDate"] text:object[#"messageContent"]];
}
if(isLoadMore)
[messages insertObject:messageToAdd atIndex:0];
else
[messages addObject:messageToAdd];
// NOT TRIGGERING THESE AFTER MEDIA DOWNLOADED
if (messageToAdd.isMediaMessage) {
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){
if ([object[#"messageFileType"] isEqual: #"image"]){
[object[#"messageMedia"] getDataInBackgroundWithBlock:^(NSData *imageData, NSError *error) {
if (!error) {
JSQPhotoMediaItem *photoItem = [[JSQPhotoMediaItem alloc] initWithImage:[UIImage imageWithData:imageData]];
((JSQPhotoMediaItem *)messageMedia).image = [UIImage imageWithCGImage:photoItem.image.CGImage];
[self.collectionView reloadData];
}
}];
}
else if([object[#"messageFileType"] isEqual: #"video"]){
PFFile *videoFile = object[#"messageMedia"];
NSURL *videoURL = [NSURL URLWithString:videoFile.url];
((JSQVideoMediaItem *)messageMedia).fileURL = videoURL;
((JSQVideoMediaItem *)messageMedia).isReadyToPlay = YES;
[self.collectionView reloadData];
}
else {
NSLog(#"%s error: unrecognized media item", __PRETTY_FUNCTION__);
}
});
}
}
For others who come along with the same issue/question, I resolved how it was working by looking at the project NotificationChat here:https://github.com/relatedcode/NotificationChat/blob/master/NotificationChat/Classes/Chat/ChatView.m. It gives a really good overview of using the JSQMessage platform.
Here's my modified function so you can see the finished product.
- (void)addMessage:(PFObject *)object
{
PFObject *user = object[#"messageSender"];
[users addObject:user];
PFFile *mediaMessage = object[#"messageMedia"];
NSString *name = #"";
if(user[#"profileFName"] && user[#"profileLName"])
name= [NSString stringWithFormat:#"%# %#",user[#"profileFName"],user[#"profileLName"]];
else
name= [NSString stringWithFormat:#"%# %#",user[#"consultantFName"],user[#"consultantLName"]];
if([object[#"messageFileType"] isEqual: #"video"]){
JSQVideoMediaItem *mediaItem = [[JSQVideoMediaItem alloc] initWithFileURL:[NSURL URLWithString:mediaMessage.url] isReadyToPlay:YES];
mediaItem.appliesMediaViewMaskAsOutgoing = [user.objectId isEqualToString:self.senderId];
messageToAdd = [[JSQMessage alloc] initWithSenderId:user.objectId senderDisplayName:name date:object.createdAt media:mediaItem];
} else if ([object[#"messageFileType"] isEqual: #"image"]){
JSQPhotoMediaItem *mediaItem = [[JSQPhotoMediaItem alloc] initWithImage:nil];
mediaItem.appliesMediaViewMaskAsOutgoing = [user.objectId isEqualToString:self.senderId];
messageToAdd = [[JSQMessage alloc] initWithSenderId:user.objectId senderDisplayName:name date:object.createdAt media:mediaItem];
[mediaMessage getDataInBackgroundWithBlock:^(NSData *imageData, NSError *error)
{
if (error == nil)
{
mediaItem.image = [UIImage imageWithData:imageData];
[self.collectionView reloadData];
}
}];
} else{
messageToAdd= [[JSQMessage alloc] initWithSenderId:user.objectId senderDisplayName:name date:object[#"sendDate"] text:object[#"messageContent"]];
}
if(isLoadMore)
[messages insertObject:messageToAdd atIndex:0];
else
[messages addObject:messageToAdd];
}
Based on the code I think one possible reason is you need reloadData on main(UI) thread after download data successfully and asynchronously on background thread
This question already has answers here:
populating a tableview with data using JSON and AFNetworking NSDictionary
(1 answer)
JSON data is not loading in slow internet connection? [closed]
(1 answer)
Closed 8 years ago.
I'm building an article reading app.I'm parsing JSON data using NSData in UITableView.
I'm facing an issue that is data is not load in slow internet speed(2g or 3g)means UI is empty.I want to implement NSUrlConnection
but i'm new in iOS development unable to implement NSUrlConnection in my code.
this is my code:
- (void)viewDidLoad
{
[super viewDidLoad];
BOOL myBool = [self isNetworkAvailable];
if (myBool)
{
#try {
// for table cell seperator line color
self.tableView.separatorColor = [UIColor colorWithRed:190/255.0 green:190/255.0 blue:190/255.0 alpha:1.0];
UIBarButtonItem *backbutton1 = [[UIBarButtonItem alloc] initWithTitle:#"" style:UIBarButtonItemStyleBordered target:nil action:nil];
[[self navigationItem] setBackBarButtonItem:backbutton1];
_Title1 = [[NSMutableArray alloc] init];
_Author1 = [[NSMutableArray alloc] init];
_Images1 = [[NSMutableArray alloc] init];
_Details1 = [[NSMutableArray alloc] init];
_link1 = [[NSMutableArray alloc] init];
_Date1 = [[NSMutableArray alloc] init];
NSData* data = [NSData dataWithContentsOfURL:ysURL];
NSArray *ys_avatars = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
if(ys_avatars){
for (int j=0;j<ys_avatars.count;j++)
{
if( ys_avatars[j][#"title"]==[NSNull null] ){
[_Title1 addObject: #""];
}
else{
[_Title1 addObject:ys_avatars[j][#"title"]];
}
if( ys_avatars[j][#"author"]==[NSNull null] ){
[_Author1 addObject: #""];
}
[_Author1 addObject: ys_avatars[j][#"author"]];
if( ys_avatars[j][#"featured_img"]==[NSNull null] ){
[_Images1 addObject: #""];
}
else{
[_Images1 addObject: ys_avatars[j][#"featured_img"]];
}
if( ys_avatars[j][#"content"]==[NSNull null] ){
[_Details1 addObject: #""];
}else{
[_Details1 addObject:ys_avatars[j][#"content"]];
}
if( ys_avatars[j][#"permalink"]==[NSNull null] ){
[_link1 addObject: #""];
}
else{
[_link1 addObject:ys_avatars[j][#"permalink"]];
}
if( ys_avatars[j][#"date"]==[NSNull null] ){
[_Date1 addObject: #""];
}
else{
NSString *newStr=[ys_avatars[j][#"date"] substringToIndex:[ys_avatars[j][#"date"] length]-3];
[_Date1 addObject:newStr];
}
}
}
else
{
NSLog(#"asd");
}
}
#catch (NSException *exception) {
}
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *Cellidentifier1 = #"ysTableViewCell";
ysTableViewCell *cell1 = [tableView dequeueReusableCellWithIdentifier:Cellidentifier1 forIndexPath:indexPath];
long row = [indexPath row];
cell1.TitleLabel1.text = _Title1[row];
cell1.AuthorLabel1.text = _Author1[row];
NSString *StoryUrl = [_Images1[indexPath.row] stringByAddingPercentEscapesUsingEncoding:NSASCIIStringEncoding];
if(StoryUrl) {
NSArray *subStringsUrl = [yourStoryUrl componentsSeparatedByString:#"/"];
NSString *stripedName = [subStringsUrl lastObject];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString* filePath =[documentsDirectory stringByAppendingPathComponent: [NSString stringWithFormat:#"%#",stripedName]];
if(filePath) {
UIImage *image = [UIImage imageWithContentsOfFile:filePath];
if(image) {
ysTableViewCell *updateCell =(id)[tableView cellForRowAtIndexPath:indexPath];
if(updateCell)
updateCell.ThumbImage1.image=image;
cell1.ThumbImage1.image=image;
} else {
dispatch_queue_t taskQ = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(taskQ, ^{
NSURL *Imageurl = [NSURL URLWithString:yourStoryUrl];
NSData *data = [NSData dataWithContentsOfURL:Imageurl];
UIImage *images1 = [[UIImage alloc] initWithData:data];
NSData *imageData = UIImagePNGRepresentation(images1);
if (![imageData writeToFile:filePath atomically:NO])
{
NSLog((#"Failed to cache image data to disk"));
}
else
{
NSLog(#"the cachedImagedPath is %#",filePath);
}
dispatch_sync(dispatch_get_main_queue(), ^{
ysTableViewCell *updateCell =(id)[tableView cellForRowAtIndexPath:indexPath];
if(updateCell)
updateCell.ThumbImage1.image=images1;
cell1.ThumbImage1.image=images1;
});
});
}
return cell1;
}
Help is appreciated.
Thanks in advance.
This looks really messy, and i suggest you change your whole design.
A basic and cleaner way (but probably not the best/cleanest way) :
Create class to handle outside-of-view related work (JSON parsing here)
Call that class in viewDidLoad to start parsing
Call a method that refreshes your table view with the newly parsed data when the parsing is done (in the JSON class).
That way, the table view will load your placeholders first and then reload itself when it has the data.
In my opinion, a better way would be to populate it before loading it so there is no wait time.
Can you find what you need yourself, or code it alone? or do you need help? If so, with what?
EDIT : You could/should also use the AFNetworking framework that will make your life 10 times easier with JSON/Internet related code.
I usually create a class that handles the load of my data, whether from a URL or local store. You could use AFNetworking, but there is a ton of extra stuff you might not need. The basics of using NSUrlConnection is really easy.
Try this tutorial, it will help you to understand how Apple's implementation works before you add a third party library that masks it for you.
NSUrlConnection Tutorial
I am calling a GET API , that takes a string keyword and returns me JSON data , which i parse and display in my UITableView
While the API returns data , I am displaying UIActivityIndicatorView, this is working fine.
However , as soon as the data is recieved the UIActivityIndicatorView disappears as expected but data does not show in the UITableView, but if i touch anywhere on the screen the data gets visible in the UI TableView.
This is my code:
-(void)searchBarSearchButtonClicked:(UISearchBar *)searchBar
{
[indicator startAnimating];
indicator.hidesWhenStopped = YES;
dispatch_queue_t queue = dispatch_queue_create("ID", NULL);
dispatch_async(queue, ^{
NSString *searchText=searchBar.text;
NSString *trimmedString = [searchText stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceCharacterSet]];
if (trimmedString.length==0) {
isFilter=NO;
UIAlertView *noConn = [[UIAlertView alloc] initWithTitle:#"ERROR" message:#"Please enter something in search bar" delegate:self cancelButtonTitle:nil otherButtonTitles:#"ok", nil];
[noConn show];
} else {
NSString *searchNew = [trimmedString stringByReplacingOccurrencesOfString:#" " withString:#"%20"];
isFilter=YES;
#try {
[label removeFromSuperview];
_Title1 = [[NSMutableArray alloc] init];
_Author1 = [[NSMutableArray alloc] init];
_Images1 = [[NSMutableArray alloc] init];
_Details1 = [[NSMutableArray alloc] init];
_link1 = [[NSMutableArray alloc] init];
_Date1 = [[NSMutableArray alloc] init];
NSString* myURLString = [NSString stringWithFormat:#"www.example.com=%#", searchNew];
NSURL *url = [NSURL URLWithString:myURLString];
NSData* data = [NSData dataWithContentsOfURL:url];
if ((unsigned long)data.length > 3) {
NSArray *ys_avatars = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
if(ys_avatars) {
for (int j=0;j<ys_avatars.count;j++) {
if( ys_avatars[j][#"title"]==[NSNull null] ) {
[_Title1 addObject: #""];
} else {
[_Title1 addObject:ys_avatars[j][#"title"]];
}
if( ys_avatars[j][#"author"]==[NSNull null] ) {
[_Author1 addObject: #""];
}
[_Author1 addObject: ys_avatars[j][#"author"]];
if( ys_avatars[j][#"featured_img"]==[NSNull null] ) {
[_Images1 addObject: #""];
} else {
[_Images1 addObject: ys_avatars[j][#"featured_img"]];
}
if( ys_avatars[j][#"content"]==[NSNull null] ) {
[_Details1 addObject: #""];
} else {
[_Details1 addObject:ys_avatars[j][#"content"]];
}
if( ys_avatars[j][#"permalink"]==[NSNull null] ) {
[_link1 addObject: #""];
} else {
[_link1 addObject:ys_avatars[j][#"permalink"]];
}
if( ys_avatars[j][#"date"]==[NSNull null] ) {
[_Date1 addObject: #""];
} else {
NSString *newStr=[ys_avatars[j][#"date"] substringToIndex:[ys_avatars[j][#"date"] length]-3];
[_Date1 addObject:newStr];
}
}
} else {
NSLog(#"error");
}
[self.myTableView reloadData];
} else {
if(IDIOM == IPAD){
[self.myTableView reloadData];
self.tableView.separatorColor = [UIColor colorWithRed:255/255.0 green:255/255.0 blue:255/255.0 alpha:1.0];
label = [[UILabel alloc] initWithFrame:CGRectMake(150, 200, 200, 100)];
label.text=#"No Article Found";
label.backgroundColor = [UIColor clearColor];
[self.view addSubview:label];
} else {
[self.myTableView reloadData];
self.tableView.separatorColor = [UIColor colorWithRed:255/255.0 green:255/255.0 blue:255/255.0 alpha:1.0];
label = [[UILabel alloc] initWithFrame:CGRectMake(90, 100, 200, 100)];
label.text=#"No Article Found";
label.backgroundColor = [UIColor clearColor];
[self.view addSubview:label];
}
}
}
#catch (NSException *exception) { }
}
dispatch_async(dispatch_get_main_queue(), ^{
[indicator performSelectorOnMainThread:#selector(stopAnimating) withObject:nil waitUntilDone:YES];
});
});
[self.mySearchBar resignFirstResponder];
}
Your basic problem is that you are trying to update the UI from background threads. All UI updates must be done on the main thread / queue.
Usually the easiest way to do that is by using:
dispatch_async(dispatch_get_main_queue(), ^{
// code to run on the main queue
});
I actually see that you're using that when you stop the UIActiviteIndicatorView here:
dispatch_async(dispatch_get_main_queue(), ^{
[indicator performSelectorOnMainThread:#selector(stopAnimating) withObject:nil waitUntilDone:YES];
});
But, in that case, you're actually dispatching the stopAnimating method to the main queue twice. You only really need this:
dispatch_async(dispatch_get_main_queue(), ^{
[indicator stopAnimating];
});
As for your table not updating, that's because you need to dispatch all your reloadData calls to the main queue.
There are quite a few places in your code that need to be dispatched back to the main queue but, instead of wrapping all of those in a dispatch_async to the main queue, there's an easier way. The only place I see where you are actually doing something that should be done on a background thread is this line:
NSData* data = [NSData dataWithContentsOfURL:url];
Which means you can get rid of the dispatch_async(queue, ^{...}); at the beginning of your method and, instead, only do that just before you call [NSData dataWithContentsOfUrl:url]. Then, dispatch_async back to the main queue immediately after.
Like this:
-(void)searchBarSearchButtonClicked:(UISearchBar *)searchBar
{
[indicator startAnimating];
indicator.hidesWhenStopped = YES;
NSString *searchText=searchBar.text;
NSString *trimmedString = [searchText stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceCharacterSet]];
if (trimmedString.length==0) {
isFilter=NO;
UIAlertView *noConn = [[UIAlertView alloc] initWithTitle:#"ERROR" message:#"Please enter something in search bar" delegate:self cancelButtonTitle:nil otherButtonTitles:#"ok", nil];
[noConn show];
} else {
NSString *searchNew = [trimmedString stringByReplacingOccurrencesOfString:#" " withString:#"%20"];
isFilter=YES;
#try {
[label removeFromSuperview];
_Title1 = [[NSMutableArray alloc] init];
_Author1 = [[NSMutableArray alloc] init];
_Images1 = [[NSMutableArray alloc] init];
_Details1 = [[NSMutableArray alloc] init];
_link1 = [[NSMutableArray alloc] init];
_Date1 = [[NSMutableArray alloc] init];
NSString* myURLString = [NSString stringWithFormat:#"www.example.com=%#", searchNew];
NSURL *url = [NSURL URLWithString:myURLString];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSData* data = [NSData dataWithContentsOfURL:url];
dispatch_async(dispatch_get_main_queue(), ^{
if ((unsigned long)data.length > 3) {
NSArray *ys_avatars = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
if(ys_avatars) {
for (int j=0;j<ys_avatars.count;j++) {
if( ys_avatars[j][#"title"]==[NSNull null] ) {
[_Title1 addObject: #""];
} else {
[_Title1 addObject:ys_avatars[j][#"title"]];
}
if( ys_avatars[j][#"author"]==[NSNull null] ) {
[_Author1 addObject: #""];
}
[_Author1 addObject: ys_avatars[j][#"author"]];
if( ys_avatars[j][#"featured_img"]==[NSNull null] ) {
[_Images1 addObject: #""];
} else {
[_Images1 addObject: ys_avatars[j][#"featured_img"]];
}
if( ys_avatars[j][#"content"]==[NSNull null] ) {
[_Details1 addObject: #""];
} else {
[_Details1 addObject:ys_avatars[j][#"content"]];
}
if( ys_avatars[j][#"permalink"]==[NSNull null] ) {
[_link1 addObject: #""];
} else {
[_link1 addObject:ys_avatars[j][#"permalink"]];
}
if( ys_avatars[j][#"date"]==[NSNull null] ) {
[_Date1 addObject: #""];
} else {
NSString *newStr=[ys_avatars[j][#"date"] substringToIndex:[ys_avatars[j][#"date"] length]-3];
[_Date1 addObject:newStr];
}
}
} else {
NSLog(#"error");
}
[self.myTableView reloadData];
} else {
if(IDIOM == IPAD){
[self.myTableView reloadData];
self.tableView.separatorColor = [UIColor colorWithRed:255/255.0 green:255/255.0 blue:255/255.0 alpha:1.0];
label = [[UILabel alloc] initWithFrame:CGRectMake(150, 200, 200, 100)];
label.text=#"No Article Found";
label.backgroundColor = [UIColor clearColor];
[self.view addSubview:label];
} else {
[self.myTableView reloadData];
self.tableView.separatorColor = [UIColor colorWithRed:255/255.0 green:255/255.0 blue:255/255.0 alpha:1.0];
label = [[UILabel alloc] initWithFrame:CGRectMake(90, 100, 200, 100)];
label.text=#"No Article Found";
label.backgroundColor = [UIColor clearColor];
[self.view addSubview:label];
}
}
[indicator stopAnimating];
});
});
}
#catch (NSException *exception) { }
}
[self.mySearchBar resignFirstResponder];
}
Note: You are doing quite a bit in that one method. I'd suggest splitting that up in to multiple methods to make your code more readable and maintainable.
try to use NSURLConnection that would save a lot of headache and make your URL requests more manageable
#interface myTableView : UITableViewController<NSURLConnectionDelegate>{
NSMutableData *_responseData;
}
and then use the delegate methods to parse data received,stop your indicatorview,and reload your tableview
#pragma mark NSURLConnection Delegate Methods
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
// A response has been received, this is where we initialize the instance var you created
// so that we can append data to it in the didReceiveData method
// Furthermore, this method is called each time there is a redirect so reinitializing it
// also serves to clear it
_responseData = [[NSMutableData alloc] init];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
// Append the new data to the instance variable you declared
[_responseData appendData:data];
}
- (NSCachedURLResponse *)connection:(NSURLConnection *)connection
willCacheResponse:(NSCachedURLResponse*)cachedResponse {
// Return nil to indicate not necessary to store a cached response for this connection
return nil;
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
// The request is complete and data has been received
// You can parse the stuff in your instance variable now
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
// The request has failed for some reason!
// Check the error var
}
and make your URL request wherever you want
// Create the request.
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:#"http://google.com"]];
// Create url connection and fire request
NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:request delegate:self];
source