How to get a call event using CTCallCenter:setCallEventHandler: that occurred while the app was suspended?

The documentation for CTCallCenter:setCallEventHandler: states that:

However, call events can also take place while your application is suspended. While it is suspended, your application does not receive call events. When your application resumes the active state, it receives a single call event for each call that changed state

The part relevant to this question is

When your application resumes the active state, it receives a single call event for each call that changed state

Implying the app will receive a call event for a call that took place in the past while the app was suspended. And this is possible according to the answer to this question: How does the Navita TEM app get call log information?

My question is: if my app is suspended and a call takes place, then when my app resumes the active state how can it retrieve the call event for the call that took place?

I have tried many, many code experiments but have been unable to retrieve any call information when my app resumes the active state.

This is the most simplest thing I have tried: 1) Create a new project using the Xcode single view application template. 2) Add the code shown below to didFinishLaunchingWithOptions 3) Launch the app 4) Task away from the app 5) Make a call from another device, answer the call, hang up the call from either device 6) Bring the app back to the foreground thus resuming the active state.

The code to register for call events is:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
        self.callCenter = [[CTCallCenter alloc] init];
        [self.callCenter setCallEventHandler:^(CTCall *call)
         {
             NSLog(@"Event handler called");
             if ([call.callState isEqualToString: CTCallStateConnected])
             {
                 NSLog(@"Connected");
             }
             else if ([call.callState isEqualToString: CTCallStateDialing])
             {
                 NSLog(@"Dialing");
             }
             else if ([call.callState isEqualToString: CTCallStateDisconnected])
             {
                 NSLog(@"Disconnected");

             } else if ([call.callState isEqualToString: CTCallStateIncoming])
             {
                 NSLog(@"Incomming");
             }
         }];  

    return YES;
}

With this code I am able to get call events if the app is in the foreground when the call occurs. But if I task away from the app before making the call then I am unable to get a call event when my app next resumes the active state - as it states it should in the Apple documentation.

Other things I have tried:

1) The documentation states that the block object is dispatched on the default priority global dispatch queue, so I have tried placing the registration of setCallEventHandler within dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{})

2) Calling setCallEventHandler: in appBecameActive instead of didFinishLaunchingWithOptions

3) Adding background abilities to the app - via beginBackgroundTaskWithExpirationHandler and/or location updates using startUpdatingLocation or startMonitoringForSignificantLocationChanges.

4) Various combinations of the above.

The bounty will be awarded once I get code running on my device which is able to get call events that took place while the app was suspended.

This is on iOS 7.


I've found a solution but I have no idea why it's working. Only thing I can think of is a bug in GCD and/or CoreTelephony.

Basically, I allocate two instances of CTCallCenter like this

void (^block)(CTCall*) = ^(CTCall* call) { NSLog(@"%@", call.callState); };

-(BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions
{
    callCenter1 = [[CTCallCenter alloc] init];
    callCenter1.callEventHandler = block;

    callCenter2 = [[CTCallCenter alloc] init];
    callCenter2.callEventHandler = block;

    return YES;
}

Similar Code in Swift:

func block (call:CTCall!) {
        println(call.callState)
    }

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
        //Declare callcenter in the class like 'var callcenter = CTCallCenter()'
        callcenter.callEventHandler = block

        return true
    }

To test this I made a call, answered it and then hanged up it while app was in background. When I launched it I received 3 call events: incoming, connected, disconnected.


In my case, I was working on an enterprise app which doesn't need to be approved by Apple's app market - so if you develop an enterprise app this solution is for you.

Also, the chosen answer didn't work while the app is the background.

The solution is simple, basically you just need to add 2 capabilities (VOIP & Background fetch) in the Capabilities tab:

  • Your project target -> Capabilities -> Background Modes -> mark Voice over IP & Background fetch

enter image description here

Now, your app is registered to the iOS framework calls "delegate" so the OP code snip solution:

[self.callCenter setCallEventHandler:^(CTCall *call)
     {
         NSLog(@"Event handler called");
         if ([call.callState isEqualToString: CTCallStateConnected])
         {
             NSLog(@"Connected");
         }
         else if ([call.callState isEqualToString: CTCallStateDialing])
         {
             NSLog(@"Dialing");
         }
         else if ([call.callState isEqualToString: CTCallStateDisconnected])
         {
             NSLog(@"Disconnected");

         } else if ([call.callState isEqualToString: CTCallStateIncoming])
         {
             NSLog(@"Incomming");
         }
     }];  

Would defiantly work and you will get notifications even if your app is in the background.