How to disconnect call in CallKit, when a call is in the Ringing state? I am using below code for disconnect callKit Call.
This code is working when I Disconnect call after Accept. but when a call is in the Ringing state then this code is not working. call keep Ringing.
I have checked that UUID is not nil .
Please let me know what is the proper way to do this
#property (nonatomic, strong) CXCallController *callKitCallController;
- (void)performEndCallActionWithUUID:(NSUUID *)uuid {
if (uuid == nil) {
return;
}
CXEndCallAction *endCallAction = [[CXEndCallAction alloc] initWithCallUUID:uuid];
CXTransaction *transaction = [[CXTransaction alloc] initWithAction:endCallAction];
[self.callKitCallController requestTransaction:transaction completion:^(NSError *error) {
if (error) {
NSLog(#"EndCallAction transaction request failed: %#", [error localizedDescription]);
}
else {
NSLog(#"EndCallAction transaction request successful");
}
}];
}
I have tried this link also ->. Callkit Call End
i am getting this error..please check screenshot
Add this method in your class,
+ (CXTransaction *)transactionWithActions:(NSArray <CXAction *> *)actions {
CXTransaction *transcation = [[CXTransaction alloc] init];
for (CXAction *action in actions) {
[transcation addAction:action];
}
return transcation;
}
Related
I am trying to integrate Hyperpay payment into React Native project and I have problems with objective-c, I followed an article and found many issues and with searching, I solve them, but still two issues I can't solve because I am not familiar with objective-c
Issue 1,
No known class method for selector 'presentCheckoutForSubmittingTransactionCompletionHandler:cancelHandler:'
Issue 2,
No known class method for selector 'dismissCheckoutAnimated:completion:'
I am sorry if my code is long but I don't to miss something
// RCTCalendarModule.m
#import "HyperPay.h"
#import "UIKit/UIKit.h"
#import <OPPWAMobile/OPPWAMobile.h>
#implementation HyperPay{
RCTResponseSenderBlock onDoneClick;
RCTResponseSenderBlock onCancelClick;
UIViewController *rootViewController;
NSString *isRedirect;
OPPPaymentProvider *provider;
}
// To export a module named RCTCalendarModule
RCT_EXPORT_METHOD(openHyperPay:(NSDictionary *)indic createDialog:(RCTResponseSenderBlock)doneCallback createDialog:(RCTResponseSenderBlock)cancelCallback) {
onDoneClick = doneCallback;
onCancelClick = cancelCallback;
NSArray *events = #[];
if ([indic[#"is_sandbox"] isEqualToString:#"1"]) {
provider = [OPPPaymentProvider paymentProviderWithMode:OPPProviderModeTest];
} else {
provider = [OPPPaymentProvider paymentProviderWithMode:OPPProviderModeLive];
}
OPPCheckoutSettings *checkoutSettings = [[OPPCheckoutSettings alloc] init];
// Set available payment brands for your shop
checkoutSettings.paymentBrands = #[#"VISA", #"MASTER"];
// Set shopper result URL
checkoutSettings.shopperResultURL = #"com.simicart.enterprise.payments://result";
OPPCheckoutProvider *checkoutProvider = [OPPCheckoutProvider checkoutProviderWithPaymentProvider:provider checkoutID:indic[#"checkoutId"]
settings:checkoutSettings];
dispatch_async(dispatch_get_main_queue(), ^{
[OPPCheckoutProvider presentCheckoutForSubmittingTransactionCompletionHandler:^(OPPTransaction * _Nullable transaction, NSError * _Nullable error) {
if (error) {
// Executed in case of failure of the transaction for any reason
if (isRedirect && ![isRedirect isEqualToString:#"1"]) {
onCancelClick(#[#"cancel", events]);
}
} else if (transaction.type == OPPTransactionTypeSynchronous) {
// Send request to your server to obtain the status of the synchronous transaction
// You can use transaction.resourcePath or just checkout id to do it
NSDictionary *responeDic = #{#"resourcePath" : transaction.resourcePath};
onDoneClick(#[responeDic, events]);
NSLog(#"%#", transaction.resourcePath);
} else {
// The SDK opens transaction.redirectUrl in a browser
// See 'Asynchronous Payments' guide for more details
}
} cancelHandler:^{
onCancelClick(#[#"cancel", events]);
// Executed if the shopper closes the payment page prematurely
}];
});
}
- (instancetype)init{
self = [super init];
if (self) {
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(getStatusOder:) name:#"getStatusOrder" object:nil];
}
return self;
}
- (void)getStatusOder:(NSNotification*)noti{
[OPPCheckoutProvider dismissCheckoutAnimated:YES completion:^{
isRedirect = #"1";
NSURL *url = noti.object;
NSString *urlString = [url absoluteString];
NSLog(#"%#", urlString);
if (![urlString isEqualToString:#"com.simicart.enterprise.payments://result"]) {
NSArray *events = #[];
NSDictionary *responeDic = #{#"url" : urlString};
onDoneClick(#[responeDic, events]);
}
}];
}
#end
I'm trying to "send to" and "receive from" on same GCDAsyncSocket object, but it return's error as Attempting to accept while connected or accepting connections. Disconnect first. while trying to initialising the object.
My Code :
-(instancetype)initWithHost:(NSString *)host port:(NSInteger)port userData:(NSDictionary *)userData delegate:(id<AKSenderSocketDelegate>)delegate
{
self = [super init];
if (self)
{
self.delegate = delegate;
self.senderSocket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_main_queue()];
self.senderSocket.userData = userData;
NSError *err = nil;
//---- Sender
if (![self.senderSocket connectToHost:host onPort:port error:&err])
{
NSLog(#"Failed to connect: %#", err);
}
else
{
NSLog(#"Connected to :%#:%ld",host,(long)port);
}
//---- Listener
if (![self.senderSocket acceptOnPort:0 error:&err])
{
NSLog(#"Failed to open lintening port. :%#",err.localizedDescription);
}
else
{
NSLog(#"Listening to port :%hu and host :%#",self.senderSocket.localPort,self.senderSocket.localHost);
}
}
return self;
}
Please help!!
You're overcooking the pudding (grin). Once you've connected, you're ready to start communicating. There's no need for the accept message.
connectToHost:onPort:error: is like placing a phone call: you call, the other party picks up (because that other party has already made itself available with acceptOnPort:error: or its equivalent). Once the connection is made, you can read and write data over the same socket; sockets are bi-directional.
So if you want to "place a call", use connectToHost:onPort:error:. If instead you want to wait for the other party to place the call, use acceptOnPort:error:. Regardless of which side of the conversation is the "connecter" or the "accepter", both sides of the conversation can both send (write) and listen (read).
I am working on a VOIP application (let's call it SampleApp) using CallKit and am struggling with an issue.
I can answer an inbound SampleApp call in a way that appears to work correctly using CallKit. However, when I reject a call, the call is instantiated anyway, which is incorrect behaviour.
To the extend of my understanding I need to get a boolean somehow depending on whether the call was accepted and rejected and then use this to decide whether the phone controller answers the incoming call.
PhoneViewController.m
- (void) presentIncomingCallAlertForCall:(SampleAppClientCall*)call
{
callIdentifier = [[NSUUID alloc] init];
// Use CallKit if iPhone
if(UI_USER_INTERFACE_IDIOM() != UIUserInterfaceIdiomPad)
{
[_pDelegate reportIncomingCall:callIdentifier
handle:call.remoteAddress
hasVideo:hasVideo
completionHandler: nil];
[self answerIncomingCall];
}
// Use Notification mechanism if iPad
else
{
// etc.
}
}
ProviderDelegate.m
-(void) reportIncomingCall: (NSUUID *) uuid
handle: (NSString *) handle
hasVideo: (BOOL) hasVideo
completionHandler:
(nullable void (^) (NSArray * _Nullable results, NSError * _Nonnull error))completionHandler
{
CXCallUpdate *update = [[CXCallUpdate alloc] init];
update.remoteHandle = [[CXHandle alloc] initWithType:CXHandleTypePhoneNumber
value:handle];
update.hasVideo = hasVideo;
[provider reportNewIncomingCallWithUUID:uuid
update:update
completion: ^(NSError *error)
{
// not sure what to put here?
}];
}
- (void) provider: (CXProvider *)provider
performAnswerCallAction : (CXAnswerCallAction *)action
{
NSDate *now = [[NSDate alloc] init];
[action fulfillWithDateConnected: now];
}
This is my first question, I've read the Stack Overflow guidelines but please tell me if I did anything wrong.
However, when I reject a call, the call is instantiated anyway, which
is incorrect behaviour.
It's unclear what you are trying to do in your code here. You always call [self answerIncomingCall]; (which we do not see the code to, but which I assume reports to your app logic and to your backend that the user has answered the call) right after [_pDelegate reportIncomingCall:...], so it seems like you are assuming that the user has decided to answer it, even before the user has done anything.
Rather, you should be telling your app logic and your backend that the user has answered the call, only in your -provider:performAnswerCallAction: method, and in your -provider:performEndCallAction:, tell your app logic and your backend that the user has declined the call (if it was a ringing incoming call).
Here is the solution based on the advice of #user102008 that resulted in what I wanted to achieve.
I moved the responsibility for answering the incoming call to the provider delegate instead of the phone view controller, which invokes the answer call or end call method depending on whether a call is accepted or rejected respectively in its provider methods.
PhoneViewController.m
- (void) presentIncomingCallAlertForCall:(SampleAppClientCall*)call {
callIdentifier = [[NSUUID alloc] init];
// Use CallKit if iPhone
if(UI_USER_INTERFACE_IDIOM() != UIUserInterfaceIdiomPad) {
[_pDelegate reportIncomingCall:callIdentifier
handle:call.remoteAddress
hasVideo:hasVideo
completionHandler: nil];
}
// Use Notification mechanism if iPad
else {
...
}
}
ProviderDelegate.h
-(void) reportIncomingCall: (NSUUID *) uuid
handle: (NSString *) handle
hasVideo: (BOOL) hasVideo
completionHandler: (nullable void (^) (NSArray * _Nullable results, NSError * _Nonnull error))
completionHandler {
CXCallUpdate *update = [[CXCallUpdate alloc] init];
update.remoteHandle = [[CXHandle alloc] initWithType:CXHandleTypePhoneNumber value:handle];
update.hasVideo = hasVideo;
[provider reportNewIncomingCallWithUUID:uuid
update:update
completion: ^(NSError *error) {
if (error) {
[self reportCallEnded:uuid reason:(NSInteger *)CXCallEndedReasonFailed];
}
}];
}
- (void) provider: (CXProvider *)provider performAnswerCallAction: (CXAnswerCallAction *)action {
[_phoneVC answerIncomingCall];
NSDate *now = [[NSDate alloc] init];
[action fulfillWithDateConnected: now];
}
- (void) provider: (CXProvider *)provider performEndCallAction: (CXEndCallAction *)action {
[_phoneVC rejectIncomingCall];
NSDate *now = [[NSDate alloc] init];
[action fulfillWithDateEnded: now];
}
I am using the the iOS client quick start project hosted on https://github.com/twilio/voice-callkit-quickstart-objc
on server I am using python (as recommended on github project)
When I clicked on "Place outgoing call" it worked fine and I got "Welcome to Twilio" voice. Great!
Then I changed the code a bit and tried to make an outgoing call to specific number. Here's the modified code
Button click event
- (IBAction)placeCall:(id)sender {
NSUUID *uuid = [NSUUID UUID];
NSString *handle = #"Real Number";
[self performStartCallActionWithUUID:uuid handle:handle];
}
Here's the CallKit handle
- (void)performStartCallActionWithUUID:(NSUUID *)uuid handle:(NSString *)handle {
if (uuid == nil || handle == nil) {
return;
}
CXHandle *callHandle = [[CXHandle alloc] initWithType:CXHandleTypeGeneric value:handle];
CXStartCallAction *startCallAction = [[CXStartCallAction alloc] initWithCallUUID:uuid handle:callHandle];
CXTransaction *transaction = [[CXTransaction alloc] initWithAction:startCallAction];
[self.callKitCallController requestTransaction:transaction completion:^(NSError *error) {
if (error) {
NSLog(#"StartCallAction transaction request failed: %#", [error localizedDescription]);
} else {
NSLog(#"StartCallAction transaction request successful");
CXCallUpdate *callUpdate = [[CXCallUpdate alloc] init];
callUpdate.remoteHandle = callHandle;
callUpdate.supportsDTMF = YES;
callUpdate.supportsHolding = NO;
callUpdate.supportsGrouping = NO;
callUpdate.supportsUngrouping = NO;
callUpdate.hasVideo = NO;
[self.callKitProvider reportCallWithUUID:uuid updated:callUpdate];
}
}];
}
And the number to call
- (void)provider:(CXProvider *)provider performStartCallAction:(CXStartCallAction *)action {
NSLog(#"provider:performStartCallAction:");
[[VoiceClient sharedInstance] configureAudioSession];
NSDictionary *toParam = #{#"To": #"+14805058877"};
//THIS IS WHERE WE NEED TO INSERT CALLING NUMBER
self.outgoingCall = [[VoiceClient sharedInstance] call:[self fetchAccessToken]
params:toParam
delegate:self];
if (!self.outgoingCall) {
[action fail];
} else {
self.outgoingCall.uuid = action.callUUID;
[self toggleUIState:NO];
[self startSpin];
[action fulfillWithDateStarted:[NSDate date]];
}
}
No matter what I enter in the parameter value I always get "Welcome to Twilio" msg. I need to know if I need change anything on the Python server or in the iOS client code. Please help!
Twilio developer evangelist here.
Have you set your TwiML application up correctly? The Voice Request URL should be pointing to your python server. I only ask as the message from the Python server, which comes from this line in the code, should be "Congratulations! You have made your first oubound call! Good bye." and you said it was "Welcome to Twilio"
Once you are definitely set up pointing at your Python app, once you have made your first outbound call you will get that message. Now you need to update your Python app as well as the iOS app.
You're sending a parameter To with the number you're trying to call. You need to change the Python so that it reads that number and outputs the TwiML that will dial that number.
That should look a bit like this:
#app.route('/outgoing', methods=['GET', 'POST'])
def outgoing():
resp = twilio.twiml.Response()
resp.dial(request.form['To'])
return str(resp)
Let me know if that helps at all.
I am working on iOS application where I am using Twilio SDK to manage client calling through device. To implement this i am using hello monkey demo application which i have successfully imported in Xcode.
After initial setup i am able to establish connection successfully but receiving delegate is no longer working. I have gone through complete twilio documentation but no success. Please suggest any alternative or solution ASAP.
Here is my code of Hello Monkey sample project
- (void)viewDidLoad
{
NSLog(#"CLINT ID----------------------- %#",name);
//check out https://github.com/twilio/mobile-quickstart to get a server up quickly
NSString *urlString = [NSString stringWithFormat:#"https://testdemo786.herokuapp.com/token?client=%#", name];
NSURL *url = [NSURL URLWithString:urlString];
NSError *error = nil;
NSString *token = [NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:&error];
if (token == nil) {
NSLog(#"Error retrieving token: %#", [error localizedDescription]);
} else {
_phone = [[TCDevice alloc] initWithCapabilityToken:token delegate:self];
}
}
- (IBAction)dialButtonPressed:(id)sender
{
NSString *to;
if ([name isEqualToString:#"jenny"]) {
to=#"client:tommy";
}
else
{
to=#"client:jenny";
}
to=#"4nmf5j";
NSLog(#"TO---------------------%#",to);
NSDictionary *params = #{#"To": to};
_connection = [_phone connect:params delegate:nil];
}
- (IBAction)hangupButtonPressed:(id)sender
{
[_connection disconnect];
}
- (void)device:(TCDevice *)device didReceiveIncomingConnection:(TCConnection *)connection
{
NSLog(#"Incoming connection from: %#", [connection parameters][#"From"]);
if (device.state == TCDeviceStateBusy) {
[connection reject];
} else {
[connection accept];
_connection = connection;
}
}
-(void)connection:(TCConnection*)connection didFailWithError: (NSError*)error{
NSLog(#"Connection failed with error : %#", error);
}
-(void)connectionDidStartConnecting:(TCConnection*)connection{
NSLog(#"connection started");
}
-(void)connectionDidDisconnect:(TCConnection*)connection{
NSLog(#"connection disconnected");
}
-(void)connectionDidConnect:(TCConnection*)connection{
NSLog(#"connected");
}
- (void)deviceDidStartListeningForIncomingConnections: (TCDevice*)device
{
NSLog(#"Device: %# deviceDidStartListeningForIncomingConnections", device);
}
- (void)device:(TCDevice *)device didStopListeningForIncomingConnections:(NSError *)error
{
NSLog(#"Device: %# didStopListeningForIncomingConnections: %#", device, error);
}
Twilio evangelist here.
Its a bit hard to tell from the code you included, which does look correct to me.
Here are a couple of things to check:
Did you add the TCDeviceDelegate as a protocol on your interface:
#interface FooViewController() <TCDeviceDelegate>
Are you sure you passing the correct client name to the connect method, and that the TwiML being returned from your TwiML Apps Voice Request URL is including that name properly?
You could check this by looking at the Twilio Monitor to see if Twilio logged any errors when retrieving or parsing your TwiML. You can also check your Twilio call logs to see what Twilio says the outcome of the inbound and outbound call legs were.
Hope that helps.
did you set the delegate to self?
_device = [[TCDevice alloc] initWithCapabilityToken:capabilityToken delegate:self];