Как получить событие вызова с помощью CTCallCenter: setCallEventHandler: это произошло во время приостановки приложения?

Документация для CTCallCenter: setCallEventHandler: указывает, что:

Однако события вызова также могут иметь место, пока ваше приложение приостановлено. Пока оно приостановлено, ваше приложение не получает вызывать события. Когда ваше приложение возобновляет активное состояние, оно получает одно событие вызова для каждого вызова, который изменил состояние

Часть, относящаяся к этому вопросу,

Когда ваше приложение возобновляет активное состояние, оно получает одно событие вызова для каждого вызова, который изменил состояние

Предположим, что приложение получит событие вызова для вызова, который имел место в прошлом, когда приложение было приостановлено. И это возможно в соответствии с ответом на этот вопрос: Как приложение Navita TEM получает информацию журнала звонков?

Мой вопрос: если мое приложение приостановлено и происходит вызов, то когда мое приложение возобновляет активное состояние, как он может получить событие вызова для вызванного вызова?

Я пробовал много, много экспериментов с кодом, но не смог получить информацию о вызовах, когда мое приложение возобновляет активное состояние.

Это самая простая вещь, которую я пробовал: 1) Создайте новый проект, используя шаблон приложения Xcode single view. 2) Добавьте код, показанный ниже, в файл didFinishLaunchingWithOptions 3) Запустите приложение 4) Задача от приложения 5) Сделайте вызов с другого устройства, ответьте на звонок, повесьте трубку с любого устройства 6) Верните приложение обратно на передний план, тем самым возобновив активное состояние.

Код для регистрации событий вызова:

- (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;
}

С помощью этого кода я могу получить события вызова, если приложение находится на переднем плане при вызове. Но если я отвлекся от приложения перед выполнением вызова, то я не смогу получить событие вызова, когда мое приложение снова возобновит активное состояние, - как он заявляет, что он должен быть в документации Apple.

Другие вещи, которые я пробовал:

1) В документации указано, что блок-объект отправляется в очередь глобальных приоритетов по умолчанию, поэтому я попытался разместить регистрацию setCallEventHandler в dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{})

2) Вызов setCallEventHandler: в appBecameActive вместо didFinishLaunchingWithOptions

3) Добавление возможностей фона в приложение - через startBackgroundTaskWithExpirationHandler и/или обновления местоположения с использованием startUpdatingLocation или startMonitoringForSignificantLocationChanges.

4) Различные комбинации указанных выше.

Награда будет присуждена после того, как я получу код на моем устройстве, который сможет получать события вызова, которые произошли во время приостановки приложения.

Это на iOS 7.

Ответ 1

Я нашел решение, но я понятия не имею, почему он работает. Единственное, о чем я могу думать, это ошибка в GCD и/или CoreTelephony.

В принципе, я выделяю два экземпляра CTCallCenter как этот

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;
}

Аналогичный код в 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
    }

Чтобы проверить это, я сделал звонок, ответил на него, а затем повесил его, когда приложение было в фоновом режиме. Когда я запустил его, я получил 3 события вызова: входящий, подключенный, отключенный.

Ответ 2

В моем случае я работал над корпоративным приложением, которое не нужно утверждать на рынке приложений Apple, поэтому, если вы разработаете корпоративное приложение, это решение для вас.

Кроме того, выбранный ответ не работал, когда приложение является фоном.

Решение прост, в основном вам просто нужно добавить 2 возможности (VOIP и Background fetch) на вкладке Capabilities:

  • Цель вашего проекта → Возможности → Режимы фона → отметка "Голос по IP" и "Фоновая выборка"

введите описание изображения здесь

Теперь ваше приложение зарегистрировано в iOS-инфраструктуре, которое вызывает "делегировать", поэтому решение для кода OP:

[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");
         }
     }];  

Будет вызывающе работать, и вы получите уведомления, даже если ваше приложение находится в фоновом режиме.