未调用 CXProviderDelegate 方法
CXProviderDelegate Methods not called
我的目标是在用户接听电话后打开我的CallViewController
。我在这里阅读了一些摘录,因此可以使用以下方式完成:
- (void)provider:(CXProvider *)provider performAnswerCallAction:(CXAnswerCallAction *)action {
NSLog(@"performAnswerCallAction");
}
- (void)provider:(CXProvider *)provider performStartCallAction:(CXStartCallAction *)action {
NSLog(@"performStartCallAction");
}
上面的代码在 AppDelegate 中。
但是,使用 NSLogs,这些方法根本不会被触发。我似乎找不到任何关于使用 Objective-C
实现的教程,这些教程很容易从初学者的 POV 中理解。如果有任何见解,我将不胜感激!
首先,你设置CXProvider
的delegate了吗?
let provider = CXProvider(configuration: your_call_kit_config)
provider.setDelegate(self, queue: nil) // 'nil' means it will run on main queue
此外,让 AppDelegate.swift
文件符合 CXProviderDelegate
当用户点击系统提供的来电屏幕上的 'Answer' 按钮时,将调用 provider:performAnswerCallAction:
。 (Apple 文档 HERE)。
provider:performStartCallAction:
在成功请求 CXCallController
后被调用以执行 CXStartCallAction
(Apple 文档 HERE)。
编辑:对于Objective-C
好的,这是 Objective-C 片段,例如,在 AppDelegate
中。首先,你需要让AppDelegate
符合CXProviderDelegate
,像这样:
@interface AppDelegate () <CXProviderDelegate>
然后,为CXProvider
和CXCallController
添加一个属性,像这样:
@interface AppDelegate () <CXProviderDelegate>
@property (nonatomic, nonnull, strong) CXProvider *provider;
@property (nonatomic, nonnull, strong) CXCallController *callController;
@end
在 AppDelegate 的函数 application:willFinishLaunchingWithOptions: 中,使用调用配置初始化 CXProvider
对象,如下所示:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Setup CallKit
CXProviderConfiguration *providerConfiguration = [[CXProviderConfiguration alloc] initWithLocalizedName:@"MyApp"];
providerConfiguration.supportsVideo = YES;
providerConfiguration.includesCallsInRecents = YES;
self.provider = [[CXProvider alloc] initWithConfiguration: providerConfiguration];
// Since `AppDelegate` conforms to `CXProviderDelegate`, set it to the provider object
// Setting 'nil' to `queue` argument means, that the methods will be executed on main thread.
// Optionally, you can assign private serial queue to handle `CXProvider` method responses
[self.provider setDelegate:self queue:nil];
// Initialize `CallController`
self.callController = [[CXCallController alloc] init];
return YES;
}
并在AppDelegate.m文件的底部,实现CXProviderDelegate
方法:
#pragma mark: - CXProvider delegate methods
- (void)providerDidReset:(CXProvider *)provider {
// Drop all calls here (if there are pending)
}
/*!
This method gets called when user presses on 'Accept' button on the incoming call screen provided by the system.
Here you should configure `AVAudioSession` for VoIP calls and handle logic for answering the call.
*/
- (void)provider:(CXProvider *)provider performAnswerCallAction:(CXAnswerCallAction *)action {
// Put your answering logic here
// Note: It is important to fulfill the action inside the scope of it's function, or to fail, depending if error occured during answering
[action fulfill];
}
/*!
This method gets called when CXCallController object finishes the CXStartCallAction request. You need to request
start call action to the CXCallController instance, when starting an outgoing VoIP call. After successful transaction,
the provider will respond with this delegate method. Here you should also configure `AVAudioSession` for VoIP calls.
*/
- (void)provider:(CXProvider *)provider performStartCallAction:(CXStartCallAction *)action {
// Put your outgoing call action here
// Note: It is important to fulfill the action inside the scope of it's function, or to fail, depending if error occured during starting a call
[action fulfill];
}
要开始拨出 VoIP 呼叫,您需要向 CXCallController 实例请求带有操作的事务,如下所示:
#pragma mark - Call Controller requests
- (void)startOutgoingVoIPCallWithNumber:(NSString *)number {
NSUUID *callUUID = [NSUUID UUID]; // Here you create or assign UUID of call.
CXHandle *callHandle = [[CXHandle alloc] initWithType:CXHandleTypePhoneNumber value:number];
CXStartCallAction *startCallAction = [[CXStartCallAction alloc] initWithCallUUID:callUUID handle:callHandle];
startCallAction.video = YES; // Yes or no is call is video or audio.
CXTransaction *startCallTransaction = [[CXTransaction alloc] initWithAction:startCallAction];
[self.callController requestTransaction:startCallTransaction completion:^(NSError * _Nullable error) {
if (error) {
// Handle start call error here
// Ususally, error occurs if the system cannot handle the new outgoing call, since there are others pending
}
// If there is no error, CXProvider will respond with `provider:performStartCallAction:` method.
}];
}
显示系统调用画面
#pragma mark - Report New incoming call
/*!
You need to call this function each time you receive a new incoming call, usually right from the VoIP push notification.
*/
- (void)reportNewIncomingCallWithNumber:(NSString *)number {
NSUUID *callUUID = [NSUUID UUID]; // Call UUID, you should have this in some Call object, not generating the new here
CXHandle *callHandle = [[CXHandle alloc] initWithType:CXHandleTypePhoneNumber value:number];
CXCallUpdate *callInfo = [[CXCallUpdate alloc] init];
callInfo.remoteHandle = callHandle;
[self.provider reportNewIncomingCallWithUUID:[NSUUID UUID] update:callInfo completion:^(NSError * _Nullable error) {
if (error) {
// Handle error here
}
// If there is no error, system will display incoming call screen and when user taps on 'Answer',
// `CXProvider` will respond with `provider:performAnswerCallAction:`
}];
}
以防万一有人仍然遇到这个问题,我的问题是我无意中为 callUpdate
中的 CXHandle
传递了一个空值 - 然后我只是检查nil
而不是 NSNull
.
老马车:
NSString *callerName = <nullable value from remote message>
CXHandle *callHandle = [[CXHandle alloc] initWithType:CXHandleTypeGeneric value:handle];
[provider reportNewIncomingCallWithUUID:uuid update:callUpdate completion:^(NSError * _Nullable error) {
...
}]
新的,工作的
NSString *callerName = <nullable value from remote message>
CXHandle *callHandle = [[CXHandle alloc] initWithType:CXHandleTypeGeneric value:handle];
if (callerName == (id)[NSNull null] || callerName.length == 0) {
callerName = @"Faceless Man";
}
[provider reportNewIncomingCallWithUUID:uuid update:callUpdate completion:^(NSError * _Nullable error) {
...
}]
我的目标是在用户接听电话后打开我的CallViewController
。我在这里阅读了一些摘录,因此可以使用以下方式完成:
- (void)provider:(CXProvider *)provider performAnswerCallAction:(CXAnswerCallAction *)action {
NSLog(@"performAnswerCallAction");
}
- (void)provider:(CXProvider *)provider performStartCallAction:(CXStartCallAction *)action {
NSLog(@"performStartCallAction");
}
上面的代码在 AppDelegate 中。
但是,使用 NSLogs,这些方法根本不会被触发。我似乎找不到任何关于使用 Objective-C
实现的教程,这些教程很容易从初学者的 POV 中理解。如果有任何见解,我将不胜感激!
首先,你设置CXProvider
的delegate了吗?
let provider = CXProvider(configuration: your_call_kit_config)
provider.setDelegate(self, queue: nil) // 'nil' means it will run on main queue
此外,让 AppDelegate.swift
文件符合 CXProviderDelegate
provider:performAnswerCallAction:
。 (Apple 文档 HERE)。
provider:performStartCallAction:
在成功请求 CXCallController
后被调用以执行 CXStartCallAction
(Apple 文档 HERE)。
编辑:对于Objective-C
好的,这是 Objective-C 片段,例如,在 AppDelegate
中。首先,你需要让AppDelegate
符合CXProviderDelegate
,像这样:
@interface AppDelegate () <CXProviderDelegate>
然后,为CXProvider
和CXCallController
添加一个属性,像这样:
@interface AppDelegate () <CXProviderDelegate>
@property (nonatomic, nonnull, strong) CXProvider *provider;
@property (nonatomic, nonnull, strong) CXCallController *callController;
@end
在 AppDelegate 的函数 application:willFinishLaunchingWithOptions: 中,使用调用配置初始化 CXProvider
对象,如下所示:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Setup CallKit
CXProviderConfiguration *providerConfiguration = [[CXProviderConfiguration alloc] initWithLocalizedName:@"MyApp"];
providerConfiguration.supportsVideo = YES;
providerConfiguration.includesCallsInRecents = YES;
self.provider = [[CXProvider alloc] initWithConfiguration: providerConfiguration];
// Since `AppDelegate` conforms to `CXProviderDelegate`, set it to the provider object
// Setting 'nil' to `queue` argument means, that the methods will be executed on main thread.
// Optionally, you can assign private serial queue to handle `CXProvider` method responses
[self.provider setDelegate:self queue:nil];
// Initialize `CallController`
self.callController = [[CXCallController alloc] init];
return YES;
}
并在AppDelegate.m文件的底部,实现CXProviderDelegate
方法:
#pragma mark: - CXProvider delegate methods
- (void)providerDidReset:(CXProvider *)provider {
// Drop all calls here (if there are pending)
}
/*!
This method gets called when user presses on 'Accept' button on the incoming call screen provided by the system.
Here you should configure `AVAudioSession` for VoIP calls and handle logic for answering the call.
*/
- (void)provider:(CXProvider *)provider performAnswerCallAction:(CXAnswerCallAction *)action {
// Put your answering logic here
// Note: It is important to fulfill the action inside the scope of it's function, or to fail, depending if error occured during answering
[action fulfill];
}
/*!
This method gets called when CXCallController object finishes the CXStartCallAction request. You need to request
start call action to the CXCallController instance, when starting an outgoing VoIP call. After successful transaction,
the provider will respond with this delegate method. Here you should also configure `AVAudioSession` for VoIP calls.
*/
- (void)provider:(CXProvider *)provider performStartCallAction:(CXStartCallAction *)action {
// Put your outgoing call action here
// Note: It is important to fulfill the action inside the scope of it's function, or to fail, depending if error occured during starting a call
[action fulfill];
}
要开始拨出 VoIP 呼叫,您需要向 CXCallController 实例请求带有操作的事务,如下所示:
#pragma mark - Call Controller requests
- (void)startOutgoingVoIPCallWithNumber:(NSString *)number {
NSUUID *callUUID = [NSUUID UUID]; // Here you create or assign UUID of call.
CXHandle *callHandle = [[CXHandle alloc] initWithType:CXHandleTypePhoneNumber value:number];
CXStartCallAction *startCallAction = [[CXStartCallAction alloc] initWithCallUUID:callUUID handle:callHandle];
startCallAction.video = YES; // Yes or no is call is video or audio.
CXTransaction *startCallTransaction = [[CXTransaction alloc] initWithAction:startCallAction];
[self.callController requestTransaction:startCallTransaction completion:^(NSError * _Nullable error) {
if (error) {
// Handle start call error here
// Ususally, error occurs if the system cannot handle the new outgoing call, since there are others pending
}
// If there is no error, CXProvider will respond with `provider:performStartCallAction:` method.
}];
}
显示系统调用画面
#pragma mark - Report New incoming call
/*!
You need to call this function each time you receive a new incoming call, usually right from the VoIP push notification.
*/
- (void)reportNewIncomingCallWithNumber:(NSString *)number {
NSUUID *callUUID = [NSUUID UUID]; // Call UUID, you should have this in some Call object, not generating the new here
CXHandle *callHandle = [[CXHandle alloc] initWithType:CXHandleTypePhoneNumber value:number];
CXCallUpdate *callInfo = [[CXCallUpdate alloc] init];
callInfo.remoteHandle = callHandle;
[self.provider reportNewIncomingCallWithUUID:[NSUUID UUID] update:callInfo completion:^(NSError * _Nullable error) {
if (error) {
// Handle error here
}
// If there is no error, system will display incoming call screen and when user taps on 'Answer',
// `CXProvider` will respond with `provider:performAnswerCallAction:`
}];
}
以防万一有人仍然遇到这个问题,我的问题是我无意中为 callUpdate
中的 CXHandle
传递了一个空值 - 然后我只是检查nil
而不是 NSNull
.
老马车:
NSString *callerName = <nullable value from remote message>
CXHandle *callHandle = [[CXHandle alloc] initWithType:CXHandleTypeGeneric value:handle];
[provider reportNewIncomingCallWithUUID:uuid update:callUpdate completion:^(NSError * _Nullable error) {
...
}]
新的,工作的
NSString *callerName = <nullable value from remote message>
CXHandle *callHandle = [[CXHandle alloc] initWithType:CXHandleTypeGeneric value:handle];
if (callerName == (id)[NSNull null] || callerName.length == 0) {
callerName = @"Faceless Man";
}
[provider reportNewIncomingCallWithUUID:uuid update:callUpdate completion:^(NSError * _Nullable error) {
...
}]