network activity indicator not appearing in iOS app - ios

In my iOS app, I want to show the network activity indicator in the top status bar.
I've added the following:
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
But the activity indicator never appears.
Does anyone know what might be wrong?
Here is the full code:
-(void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:YES];
// load sets
[self loadSets];
}
-(void)loadSets{
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(#"in loadSets");
// show loading animation
UIView *loadingView = loadingIndicator;
loadingView.center = CGPointMake(screenWidth/2, screenHeight/2);
[self.view addSubview:loadingView];
[loadingIndicator startAnimating];
self.userSets = [[NSMutableArray alloc]init]; // re-initialize userSets
dispatch_async(bgQueue, ^{
NSString *userURLString = [userBaseUrl stringByAppendingFormat:#"/%#.json?auth_token=%#", username, auth_token];
NSLog(#"userURLString %#", userURLString);
NSURL *userURL = [NSURL URLWithString:userURLString];
NSData * userData = [NSData dataWithContentsOfURL:userURL];
dispatch_async(dispatch_get_main_queue(), ^{
if(userData){
[self fetchSets:userData];
// remove loading animation
[loadingView removeFromSuperview];
}else{
// error with authentication - should log out and require relogin
// [self logoutClick];
}
});
});
});
}
-(void)fetchSets:(NSData *)responseData{
NSError * error;
NSDictionary * json = [NSJSONSerialization
JSONObjectWithData:responseData
options:kNilOptions
error:&error];
if(json){
NSArray *sets = [json objectForKey:#"sets"];
for (NSDictionary *currentSet in sets){
Set *userSet = [[Set alloc] init];
userSet.name = [currentSet objectForKey:#"name"];
userSet.videoURL = [[currentSet objectForKey:#"media"] objectForKey:#"mp4"];
userSet.gifURL = [[currentSet objectForKey:#"media"] objectForKey:#"gif"];
userSet.imgURL = [[currentSet objectForKeyedSubscript:#"media"] objectForKey:#"img"];
userSet.setID = [currentSet objectForKey:#"id"];
[self.userSets addObject: userSet];
}
NSLog(#"trying to reload table data with userSets length %d", [self.userSets count]);
[self.collectionView reloadData];
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(#"done loading table data");
});
}
}

Related

Using of activity indicator in second viewController to load lot of data in Objective C

How can I use activity indicator in second view controller to let data to be complete load
first VC
- (IBAction)btnOpenHTML:(id)sender {
RecievVC *obj = [self.storyboard instantiateViewControllerWithIdentifier:#"RecievVC"];
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
[dict setValue:lblName.text forKey:#"lblname"];
//........Lots of Data........(then in the end of 1st VC)........//
obj.dicData = dict;
[self.navigationController pushViewController:obj animated:true];
}
#end
the 2nd VC Code after viewdidload
- (void)viewDidLoad {
[super viewDidLoad];
[self->webView.scrollView setScrollEnabled:true];
[self->webView loadHTMLString:[self getHTMLStirng] baseURL:[NSURL URLWithString:#""]];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
-(NSString *)getHTMLStirng {
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"resumeT" ofType:#"html"];
NSString *strHTML = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];
if (self.dicData) {
strHTML = [strHTML stringByReplacingOccurrencesOfString:#"#NAME" withString:[_dicData valueForKey:#"lblname"]];
//...............Lots of Data......Then at the end of 2nd VC............//
}return strHTML;
}
what I main by lots of data, it's around 150 of keys and value have to be send from 1st VC to 2nd VC.
any idea?
You could try GCD:
// instantiate the activity indicator
UIActivityIndicatorView *activity = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle: UIActivityIndicatorViewStyleGray];
activity.center = self.view.center;
// start your activity indicator
[activity startAnimating]
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// load data on background thread
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
[dict setValue:lblName.text forKey:#"lblname"];
//........Lots of Data........(then in the end of 1st VC)........//
// update UI on main thread
dispatch_async(dispatch_get_main_queue(), ^{
// stop your activity indicator
[activity stopAnimating]
RecievVC *obj = [self.storyboard instantiateViewControllerWithIdentifier:#"RecievVC"];
obj.dicData = dict;
[self.navigationController pushViewController:obj animated:true];
});
});

How to display UIActivityIndicatorView while fetching JSON data asynchronously to be populated in UITableView?

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

AMSlideMenu Cell click takes time to open another view?

I’m building an article reading app.I’m using AMSliderMenu(https://github.com/SocialObjects-Software/AMSlideMenu)
library for menu list.When i click on any cell in AMSlideMenu it load into another table view which contain list of articles.
I’m fetching article data in uitableview with JSON.
The issue is when i click on list of menu in AMSlideMenu it takes time to open another view.How can i resolve this problem.
Here is my code:
- (void)viewDidLoad
{
[super viewDidLoad];
BOOL myBool = [self isNetworkAvailable];
if (myBool)
{
#try {
_Title1 = [[NSMutableArray alloc] init];
_Author1 = [[NSMutableArray alloc] init];
_Images1 = [[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++)
{
[_Title1 addObject:ys_avatars[j][#"title"]];
[_Author1 addObject: ys_avatars[j][#"author"]];
[_Images1 addObject: ys_avatars[j][#"featured_img"]];
}
}
else
{
NSLog(#"asd");
}
}
#catch (NSException *exception) {
}
[self LoadMore];
[self LoadMore];
});
});
}
}
-(void)LoadMore
{ 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];
// for displaying the previous screen lable with back button in details view controller
UIBarButtonItem *backbutton1 = [[UIBarButtonItem alloc] initWithTitle:#"" style:UIBarButtonItemStyleBordered target:nil action:nil];
[[self navigationItem] setBackBarButtonItem:backbutton1];
NSString *urlString=[NSString stringWithFormat:#"www.example.com&page=%d",x]; NSURL *newUrl=[NSURL URLWithString:urlString];
NSData* data = [NSData dataWithContentsOfURL:newUrl];
NSArray *ys_avatars = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
x++;
if(ys_avatars){
for (int j=0;j<_Title1.count ;j++)
{
[_Title1 addObject:ys_avatars[j][#"title"]];
[_Author1 addObject: ys_avatars[j][#"author"]];
[_Images1 addObject: ys_avatars[j][#"featured_img"]];
} }
else
{ NSLog(#"asd"); }
[self.tableView reloadData];}
#catch (NSException *exception) {
}} }
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return _Title1.count;
}
Its because you have done many many works in viewDidLoad method that's executing synchronously, and that view controller will not be shown until the code in viewDidLoad will not executed completely.
So, load your data asynchronously or do it in viewDidAppear method, but I strongly suggest you to do it asynchronously.

iOS CollectionView: Loading Data Asynchronously

I'm using a collection view and trying to transition from loading the data synchronously to loading it asynchronously.
I know that the following currently works (it takes a while to load, but all the cells appear correctly when it's done):
// load projectData in main thread
NSData * projectData = [NSData dataWithContentsOfURL:userUrl];
[self performSelectorOnMainThread:#selector(fetchProjects:)withObject:projectData waitUntilDone:YES];
I rewrote it to do everything asynchronously:
// load project data asynchronously
dispatch_async(bgQueue, ^{
UIView *loadingAnimation = loadingCircle;
loadingAnimation.tag = 15;
[self.collectionView addSubview:loadingAnimation];
[loadingCircle startAnimating];
NSData * projectData = [NSData dataWithContentsOfURL:userUrl];
[self performSelector:#selector(fetchProjects:) withObject:projectData];
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(#"finished with loading projects");
UIView *viewToRemove = [self.view viewWithTag:15];
[viewToRemove removeFromSuperview];
[self.collectionView reloadData];
});
});
When I run the app after loading the data asynchronously, the view appears empty (the cells have no content), but when I scroll, some of the cells begin to appear.
Is there anything else I need to call besides reloadData to get my collection cells to appear properly?
Here is my fetchProjects:
// get JSON data of projects
- (void)fetchProjects:(NSData *)responseData {
NSError * error;
NSDictionary * json = [NSJSONSerialization
JSONObjectWithData:responseData
options:kNilOptions
error:&error]; // get dictionary from json data
NSDictionary * data = [json objectForKey:#"data"]; // get data in array
NSArray * projects = [data objectForKey:#"projects"];
NSDictionary * mostRecentProject = [projects objectAtIndex:0];
mostRecentProjectID = [mostRecentProject objectForKey:#"id"];
for (NSDictionary *currentProject in projects)
{
[projectIDs addObject: [currentProject objectForKey:#"id"]];
NSString *projectTitle = [currentProject objectForKey:#"title"];
NSString *trimmedProjectTitle = [projectTitle stringByTrimmingCharactersInSet:
[NSCharacterSet whitespaceAndNewlineCharacterSet]];
id delegate = [[UIApplication sharedApplication] delegate];
self.managedObjectContext = [delegate managedObjectContext];
Project *newProject = (Project *) [NSEntityDescription insertNewObjectForEntityForName:#"Project" inManagedObjectContext:[self managedObjectContext]];
CustomLabel *cellLabel=[[CustomLabel alloc]init];
cellLabel.text = trimmedProjectTitle;
NSLog(#"fetchprojects:%#",projectTitle);
[titles addObject:projectTitle];
CGSize maxLabelSize = CGSizeMake(screenWidth/2 - 30,100);
CustomLabel *titleLabel = [[CustomLabel alloc]init];
[titleLabel setNumberOfLines:0];
titleLabel.text = projectTitle;
CGSize expectedLabelSize = [titleLabel.text sizeWithFont:titleLabel.font constrainedToSize:maxLabelSize lineBreakMode:NSLineBreakByWordWrapping];
CGRect labelFrame = (CGRectMake(0, 0, screenWidth/2 - 30, 0));
labelFrame.origin.x = 0;
labelFrame.origin.y = screenWidth/2 - 70 - expectedLabelSize.height;
labelFrame.size.height = expectedLabelSize.height;
titleLabel.frame = labelFrame;
titleLabel.backgroundColor = [[UIColor blackColor]colorWithAlphaComponent:0.5f];
titleLabel.textColor =[UIColor whiteColor];
[titleLabel setFont: [UIFont fontWithName: #"HelveticaNeue" size:12]];
//NSLog(#"%#", titleLabel.text);
UIImageView *imagePreview = [[UIImageView alloc] initWithFrame:CGRectMake(7.5, 10, screenWidth/2 -30, screenWidth/2 -70)];
imagePreview.contentMode= UIViewContentModeScaleAspectFill;
imagePreview.clipsToBounds = YES;
[imagePreview setImage:[UIImage imageNamed:#"blank.png"]];
[imagePreview addSubview:titleLabel];
[imagePreview.subviews[0] setClipsToBounds:YES];
[projectContainers addObject: imagePreview];
}
}
You're doing a lot of UI work on a background thread which you really shouldn't do. From what I can see, the only line that really needs to be run on a background thread is this one:
NSData * projectData = [NSData dataWithContentsOfURL:userUrl];
The rest looks like it deals with setting up and displaying your UI and some CoreData stuff; all of that needs to be run on the main thread. The easiest way to do that and keep everything running in the right order would be something like this:
// NOTE: If you're sure you're already on the main thread here, you don't need the dispatch, but it's not going to hurt to leave it in.
dispatch_async(dispatch_get_main_queue(), ^{
UIView *loadingAnimation = loadingCircle;
loadingAnimation.tag = 15;
[self.collectionView addSubview:loadingAnimation];
[loadingCircle startAnimating];
});
dispatch_async(bgQueue, ^{
NSData * projectData = [NSData dataWithContentsOfURL:userUrl];
dispatch_async(dispatch_get_main_queue(), ^{
[self fetchProjects:projectData];
NSLog(#"finished with loading projects");
UIView *viewToRemove = [self.view viewWithTag:15];
[viewToRemove removeFromSuperview];
[self.collectionView reloadData];
});
});
Note: I also changed [self performSelector:#selector(fetchProjects:) withObject:projectData] to [self fetchProjects:projectData]; you don't really need to go through performSelector: there.

dispatch_queue and return data

I'm trying to write this method that returns an NSArray. My NSMutableArray (friendUsers) adds the objects right, but outside the dispatch_async the array is empty.
I try to add the users in the main queue ( as ashowed) but the array is empty to. Any ideas ? Thanks for all your help.
- (NSArray *)checkUsersInGroup {
NSMutableArray *friendUsers = [[NSMutableArray alloc] init];
dispatch_queue_t checkUSers = dispatch_queue_create("CheckUsers", NULL);
dispatch_async(checkUSers, ^{
NSArray *totalUsers = [VVDataRead lecturaDades];
NSArray *usersToSearch = [_grup objectForKey:#"groupFriends"];
for (NSString *tempUserId in usersToSearch){
for (NSDictionary *user in totalUsers){
NSString *id = [user objectForKey:#"id"];
if ([tempUserId isEqualToString:id])
dispatch_async(dispatch_get_main_queue(), ^{
[friendUsers addObject:user];
});
}
}
});
NSLog(#"people:%#",friendUsers);
return [friendUsers copy];
}
you can use blocks, it can make your life easier in this case.
- (void)checkUsersInGroupWithCompleteBlock:(void(^)(NSMutableArray * resultArray))completeBlock {
NSMutableArray *friendUsers = [[NSMutableArray alloc] init];
dispatch_queue_t checkUSers = dispatch_queue_create("CheckUsers", NULL);
dispatch_async(checkUSers, ^{
NSArray *totalUsers = [VVDataRead lecturaDades];
NSArray *usersToSearch = [_grup objectForKey:#"groupFriends"];
for (NSString *tempUserId in usersToSearch){
for (NSDictionary *user in totalUsers){
NSString *id = [user objectForKey:#"id"];
if ([tempUserId isEqualToString:id])
dispatch_async(dispatch_get_main_queue(), ^{
[friendUsers addObject:user];
});
}
}
// call the complete block with the result when you finished
if (completeBlock) completeBlock(friendUsers);
});
}
...and here is how you can call the method:
- (void)anyMethod {
// ... do whetever you want here before
[self checkUsersInGroupWithCompleteBlock:^(NSMutableArray *resultArray) {
NSLog(#"%#", resultArray);
}];
// ... or after
}
EDITED:
NOTE: here is another possible solution, but in your case it just suspends the main thread (which is definitely bad), so you won't gain anything with this solution but pain on the main thread, but if you are on two background threads, this solution can give a very nice example of synchronisation between the threads.
- (NSArray *)checkUsersInGroup {
NSMutableArray *friendUsers = [[NSMutableArray alloc] init];
// our semaphore is here
dispatch_semaphore_t _semaphore = dispatch_semaphore_create(0);
dispatch_queue_t checkUSers = dispatch_queue_create("CheckUsers", NULL);
dispatch_async(checkUSers, ^{
NSArray *totalUsers = [VVDataRead lecturaDades];
NSArray *usersToSearch = [_grup objectForKey:#"groupFriends"];
for (NSString *tempUserId in usersToSearch){
for (NSDictionary *user in totalUsers){
NSString *id = [user objectForKey:#"id"];
if ([tempUserId isEqualToString:id])
dispatch_async(dispatch_get_main_queue(), ^{
[friendUsers addObject:user];
});
}
}
// the process finished
dispatch_semaphore_signal(_semaphore);
});
// ... we are wainitng for the semaphore's signal
dispatch_semaphore_wait(_semaphore, DISPATCH_TIME_FOREVER);
dispatch_release(_semaphore);
NSLog(#"people:%#",friendUsers);
return [friendUsers copy];
}
There are number of strategies to solve this, but as your operation happens on a background thread, returning the array isn't one of them. You could use NSNotificationCenter to signal that the task as finished and read the array. i.e.
- (void)checkUsersInGroup {
NSMutableArray *friendUsers = [[NSMutableArray alloc] init];
dispatch_queue_t checkUSers = dispatch_queue_create("CheckUsers", NULL);
dispatch_async(checkUSers, ^{
NSArray *totalUsers = [VVDataRead lecturaDades];
NSArray *usersToSearch = [_grup objectForKey:#"groupFriends"];
for (NSString *tempUserId in usersToSearch){
for (NSDictionary *user in totalUsers){
NSString *id = [user objectForKey:#"id"];
if ([tempUserId isEqualToString:id])
dispatch_async(dispatch_get_main_queue(), ^{
[friendUsers addObject:user];
});
}
}
// Signal background task is finished
// Make sure to add an observer to this notification
[[NSNotificationCenter defaultCenter] postNotificationName:#"friendsAddLiteral"
object:nil];
});
}
//this method will respond to the notification
- (void) onFriendsAdded:(NSNotification*)notif {
//do something on the main thread
}

Resources