1

in my app I am implementing my internet network with global dispatch queue and gcd. I want to set network indicator visible while there is network activity.

here is my network block - >

{
[[UIApplication sharedApplication]setNetworkActivityIndicatorVisible:YES];

send sync http request

[[UIApplication sharedApplication]setNetworkActivityIndicatorVisible:NO];


}

my questions :

  1. I want to check if there is a block that doesn't executed yet! before hiding network indicator. How could I implement that !

  2. does calling setNetworkActivityIndicatorVisible from another thread, safe because i see that NetworkActivityIndicatorVisible is nonatomic.

david
  • 157
  • 1
  • 1
  • 12

2 Answers2

2

@DavidBemerguy's approach is a good start, but you'd typically want to implement it with dispatch_group_notify to hide your indicator. That said, IMO, you don't need GCD here. You just need a NetworkIndicatorController.

Create an object (the controller) that listens to notifications like DidStartNetworkActivity and DidStopNetworkActivity. Post notifications when you start or stop. Inside the controller, keep a count and when it hits 0, hide the indicator. Something like this (totally untested, just typing here, and I've been writing exclusively in Swift for the last few days, so forgive any missing semicolons):

.h:

extern NSString * const DidStartNetworkActivityNotification;
extern NSString * const DidStopNetworkActivityNotification;

@interface NetworkIndicatorController
- (void) start;
- (void) stop;
@end

.m

@interface NetworkIndicatorController ()
@property (nonatomic, readwrite, assign) NSInteger count;
@end

@implementation NetworkIndicatorController

- (void)start {
    NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
    [nc addObserver:self name:DidStartNetworkActivityNotification selector:@selector(didStart:) object:nil];
    [nc addObserver:self name:DidStopNetworkActivityNotification selector:@selector(didStop:) object:nil];
    self.count = 0;
}

- (void)stop {
    NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
    [nc removeObserver:self name:DidStartNetworkActivityNotification object:nil];
    [nc removeObserver:self name:DidStopNetworkActivityNotification object:nil];
}

- (void)didStart:(NSNotification *)note {
    dispatch_async(dispatch_get_main_queue(),  {
      self.count = self.count + 1;
      [[UIApplication sharedApplication]setNetworkActivityIndicatorVisible:YES];
    });
}

- (void)didStop:(NSNotification *)note {
    dispatch_async(dispatch_get_main_queue(),  {
      self.count = self.count - 1;
      if (self.count <= 0) {
         [[UIApplication sharedApplication]setNetworkActivityIndicatorVisible:NO];
      }
    });
}

You could get similar stuff with dispatch_group, but I think this is simpler. The problem with the dispatch group approach is keeping track of when you do and don't want to call dispatch_notify. I'm sure the final code isn't that hard, but it's trickier to think about all the possible race conditions.

You could also just directly call -startNetworkActivity and -stopNetworkActivity directly on an instance of this object that you pass around rather than using notifications.

Rob Napier
  • 286,113
  • 34
  • 456
  • 610
  • This is the right way to go about it. For the record, to my knowledge, there is (at least currently) no public API for determining whether an arbitrary GCD queue is empty. – ipmcc Sep 03 '14 at 10:45
  • 1
    Even if you could ask if a queue were empty, it would be useless, because between the time you checked and the time you did something about it, it might not be empty anymore. Worse, other queues may target this queue and can change what queue they target. And there's the question of whether a queue is empty if some of the queues that target it are suspended but have blocks on them. You can address all of those questions with dispatch_groups, but you have to decide what you want the answers to be, which can be a lot of work :D – Rob Napier Sep 03 '14 at 12:45
  • Well said, Rob. Well said. :) – ipmcc Sep 03 '14 at 13:12
1

One possible approach is to create a group of tasks, then waiting for them to finish. Between the start and finish you can update your activity indicator. Obviously you'll need some object that will retain reference to the group. Check this code, based on apple documentation:

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); // Retain this queue somewhere accessible from the places you want to dispatch
dispatch_group_t group = dispatch_group_create(); // Retain this group somewhere accessible from the places you want to dispatch

// Add a task to the group
dispatch_group_async(group, queue, ^{
  // Some asynchronous work
});

// Do some other work while the tasks execute.
disptach_sync(dispatch_get_main_queue(), ^{
    [[UIApplication sharedApplication]setNetworkActivityIndicatorVisible:YES];
}


// When you cannot make any more forward progress,
// wait on the group to block the current thread.
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);

// Release the group when it is no longer needed.
disptach_sync(dispatch_get_main_queue(), ^{
    [[UIApplication sharedApplication]setNetworkActivityIndicatorVisible:NO];
}

dispatch_release(group);

Remember that you can have a singleton object that you dispatch your blocks and and keeps track of your wait.

Rob Napier
  • 286,113
  • 34
  • 456
  • 610
David Bemerguy
  • 1,334
  • 11
  • 15