Control access to webservice method from multiple UIViewControllers

618 Views Asked by At

I have an iOS app with a tabbar and 3 different UIViewControllers, one for each tab. The app uses SudzC to interface with a C# .NET webservice to pull data from a database.

There is one webservice method that is called from all three view controllers, but I want to enforce that only one view controller can call the method at any point in time and no other view controller can call it until the data has been returned.

I tried to solve this by defining a NSLock in the AppDelegate, and then implementing the following code in each viewController:

if([SharedAppDelegate.loginLock lockBeforeDate:[[[NSDate alloc] init] dateByAddingTimeInterval:30.0]])
{
     // got the lock so call the webservice method
     SDZiOSWebService* webService = [SDZiOSWebService service];
     [webService Login:self action:@selector(handleRelogin:) username:userName password:password];
}
else
{
     // can't get lock so logout
     self->reloginInProgress = false;
     [SharedAppDelegate doLogout];
}

The handler for the webservice return is defined as (truncated for clarity)

-(void)handleRelogin: (id) result {
     SDZLoginResult *loginResult = (SDZLoginResult*)result;
     if(loginResult.Status)
     {
          SharedAppPersist.key = loginResult.key;
     }
     else
     {
          SharedAppPersist.key = @"";
     }
     [SharedAppDelegate.loginLock unlock];
}

My understanding is that the first UIViewController would get a lock and the others would block for up to 30 seconds waiting to get hold of the lock. However in the rare instances where more than one viewController tries to access the lock at the same time I get the following error instantly:

*** -[NSLock lockBeforeDate:]: deadlock (<NSLock: 0x2085df90> '(null)')

Can anyone tell me what I am doing wrong? I have a good understanding of locks in C/C++ but these Objective-C locks have be stumped.

2

There are 2 best solutions below

0
On

In my opinion you shouldn't use locks (which are "evil") for this simple case.

What you can try to use is a NSOperationQueue, set to manage 1 concurrent operation at a time, and then let the view controllers to enqueue their web service calls: the operation queue will guarantee that only one operation at a time will be performed. The other advantage of the operation queue is that a view controller can check if the queue is empty or not and then decide to enqueue its call or not, based on the current status. Finally you can use KVO to observer the queue status so each view controller can simply check this before submitting a new request.

Another possibility, similar to using NSOperationQueue, is to create a private GCD serial queue and again enqueue all web service requests (wrapped inside a block). While GCD serial queues are more straightforward to implement than NSOperationQueues (IMHO) they don't offer the same advantages of observability and the possibility to cancel operations.

2
On

If its just that you want 1 view to access the web-service at a time. you can make use of Singleton Classes. Here's a link to one of the examples out the many on the net. http://www.galloway.me.uk/tutorials/singleton-classes/

also you can use NSUserDefaults to store a bool value to inform you if a view is using the web-service or not. A simple example will be:

To Store Value

[[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"active_connection"];
[NSUserDefaults synchronize];

To Retrieve

if(![[NSUserDefaults standardUserDefaults] boolForKey:@"active_connection"]) {
   [[NSUserDefaults standardUserDefaults] setBool:NO forKey:@"active_connection"];
   [NSUserDefaults synchronize];
   // Send request to web-service
}

I hope this helps you. Happy Coding.!!