Leon Leon - 6 months ago 15
iOS Question

How can a completion handler return to the wrong ViewController?

We have a WebService singleton which handles all our API calls. All works fine unless we have multiple API calls at once from different ViewControllers.


  1. VCA sends request A

  2. VCB sends request B

  3. Response B is returned to VCB

  4. Response A is returned to VCA - this causes an exception



WebService:

typealias ServiceResponse = (NSDictionary!, NSError!) -> ()

class WebService: NSObject {

var serviceResponse : ServiceResponse!

class var sharedInstance:WebService {
struct Singleton {
static let instance = WebService()
}
return Singleton.instance
}

func callWebserviceWith(paramsDict: NSDictionary, sendAccessToken: Bool, onCompletion: ServiceResponse) -> Void {
serviceResponse = onCompletion


How can this happen?

Answer

The issue is not with completion block, it's how you are dealing with it :)

Mistake

Your singleton ServiceResponse class has a class variable named serviceResponse that means there will be only one variable named serviceResponse in the memory (This is a pointer variable holding the reference to function pointer)

var serviceResponse : ServiceResponse!

Now when VCA makes a call it passes its pointer to block of code lets call VCAF1 now your serviceResponse will have value of VCAF1, assume that your web service is not yet complete and VCB calls the webservice and passes its pointer VCBf1 which is absolutely different from VCAF1 (differrent functions or blocks of code all together) but your serviceResponse is not bothered is it it simply forgets its early value and starts holding VCBf1.

Now your first service call response arrives and your code says execute the code for which I am holding reference in serviceResponse. Ta da!!!!! error :) You are executing wrong code :)

Solution

I dont find any problem in design to be honest but then I might be wrong :) Creating a singleton class for webservice is very common approach used, closures are nothing differrent then the blocks in objectiveC and I never had any issue with singleton design pattern through out my career as iOS developer :)

All you have to do

  1. delete variable serviceResponse

  2. modify the function to use function arguement rather than instance property

func callWebserviceWith(paramsDict: NSDictionary, sendAccessToken: Bool, onCompletion: ServiceResponse) -> Void {
    //when done use funtion arguement passed :)
    onCompletion()
}