I am using Sinch to manage calls in my app, but I can't figure out how to report the incoming call to CallKit, as required by iOS 13. I went through some of their documentation listed here, but that's not working either. Also, the documentation states that you should report it as:
- (void)managedPush:(id<SINManagedPush>)managedPush
didReceiveIncomingPushWithPayload:(NSDictionary *)payload
forType:(NSString *)pushType {
id<SINNotificationResult> notification = [SINManagedPush queryPushNotificationPayload:payload];
if ([notification isValid] && [notification isCall]) {
NSUUID *callId = [[NSUUID alloc] initWithUUIDString:notification.callResult.callId];
CXCallUpdate *callUpdate = [[CXCallUpdate alloc] init];
callUpdate.remoteHandle = [[CXHandle alloc] initWithType:CXHandleTypeGeneric value:notification.callResult.remoteUserId];
[self.provider reportNewIncomingCallWithUUID:callId
update:callUpdate
completion:^(NSError *_Nullable error) {
if (error) {
// Hangup call
}
}];
}
}
When I try to implement this, I get the error that SINManagedPush doesn't contain any method such as queryPushNotificationPayload:payload, even though I'm using the latest version of Sinch, so as a workaround I found that SINPushHelper contains these properties/methods, so I'm using that. I am new in Swift and iOS environment in general, so any help would be very much appreciated!
you can follow our SinchCallKit or SinchVideoCallKit sample apps, they are available inside the Samples folder on our IOS SDK.
https://download.sinch.com/ios/4.2.5/Sinch-iOS-4.2.5-a5beacdb.tar.bz2
Sinch Voice & Video Team
Related
My iOS VoIP app uses CallKit in order to support native call integration feature. At first launch everything is working fine, but if I reinitialize CXProvider and CXCallController (in order to disable/enable feature), after incoming call hangup I receive error "com.apple.CallKit.error.requesttransaction Code=4".
#implementation CallKitHandler
- (void) configureCallKitWith
{
...
self.callKitProvider = [[CXProvider alloc] initWithConfiguration:_cxpConfiguration];
[_callKitProvider setDelegate:self queue:nil];
self.callKitCallController = [CXCallController new];
[_callKitCallController.callObserver setDelegate:self queue:nil];
...
}
- (void) requestEndCallActionWithCall:(Call*) callEnded
{
CXEndCallAction* endCallAction = [[CXEndCallAction alloc] initWithCallUUID:self.callUUId];
CXTransaction* transaction = [[CXTransaction alloc] initWithAction:endCallAction];
OTCLogVerbose (#"requestEndCallActionWithCall '%#' : %#", callEnded.reference, transaction);
[self.callKitCallController requestTransaction:transaction completion:^(NSError* error) {
if (error)
{
OTCLogWarn (#"requestEndCallActionWithCall failed for '%#': %#", _callUUId, [self errorDescriptionOf: error]);
I tried to make my CallKitHandler class as singleton, and it seems to be working, is it the only possible solution? Should you avoid reinitializing CallKit during app's runtime?
The documentation says:
A VoIP app should create only one instance of CXProvider and store it for use globally.
So, yes: you should avoid instantiating the CXProvider every time you want to reconfigure it. Just keep a global reference and reconfigure it if you need to.
I am trying to open Facebook Messenger to send a message from my iOS app. I am currently running FaceBook SDK version 4.37.0. According to iOS - Sharing, this should be possible. It says
People can also share content from your app to Facebook Messenger with Messenger Expression Platform or from Sharing's Message Dialog in iOS SDK.
If you click on the link for Message Dialog, it provides you with the following example:
FBSDKShareLinkContent *content = [[FBSDKShareLinkContent alloc] init];
content.contentURL = [NSURL URLWithString:myFacebookURL];
content.quote = #"My message";
[FBSDKMessageDialog showWithContent:content delegate:self];
I have implemented this as well as the FBSDKSharingDelegate delegate methods of:
- (void)sharer:(id<FBSDKSharing>)sharer didCompleteWithResults:(NSDictionary *)results
{
NSLog(#"complete");
}
- (void)sharer:(id<FBSDKSharing>)sharer didFailWithError:(NSError *)error
{
NSLog(#"Sharer Error");
}
- (void)sharerDidCancel:(id<FBSDKSharing>)sharer
{
NSLog(#"Cancelled");
}
The delegate methods never get called. What I would expect is that a Facebook Messenger dialog would open allowing me to select a friend to message. But nothing opens. And there are no errors being logged.
I do know that facebook is receiving something because I get the following:
FBSDKLog: param {
"advertiser_id" = "xxxxxxxxx";
"advertiser_tracking_enabled" = 1;
"anon_id" = "xxxxxxxx";
"application_
tracking_enabled" = 1;
"custom_events" = "[{\"_eventName\":\"fb_mobile_content_view\",\"_logTime\":1541721304,\"fb_description\":\"MY FEATURE Sent\",\"_ui\":\"no_ui\"},{\"_ui\":\"no_ui\",\"_eventName\":\"fb_messenger_dialog_share_show\",\"_logTime\":1541721310,\"_implicitlyLogged\":\"1\",\"fb_dialog_share_content_type\":\"Status\"}]";
event = "CUSTOM_APP_EVENTS";
extinfo = "[\"i2\",\"com.myApp\",\"1\",\"8.3.6\",\"12.0.1\",\"iPhone10,6\",\"en_US\",\"CST\",\"AT&T\",375,812,\"3.00\",6,60,8,\"America\\/Chicago\"]";
"url_schemes" = "[\"myappID\",\"myscheme1\",\"myscheme2\",\"myscheme3\"]";
}
Any help or pointers would be appreciated.
FBSDKMessageDialog.show(with: FBSDKShareLinkContent(), delegate: nil)
I have the same issue but experimentally I only pass FBSDKShareLinkContent() as a content param and messenger app start
You should also add fb-messenger-share-api key in Info.plist under LSApplicationQueriesSchemes
On an app to app call with using SinchCallKit demo app that is provided in the latest version (3.12), if the caller hangs up an ongoing call by calling [SINCall hangup] before the callee answers, the CallKit UI won't be removed from the callee's lock screen. It stays there forever.
So my question is here that how can we remove the CallKit lock screen UI from the callee's screen automatically. Is this is a server side issue or Apple handles this via push notifications?
It is a bug in the Sinch SDK and it has been fixed since 3.12.1, please update to the latest version and give it a try.
Perform an EndCallAction. You need the ID number you used to create the call object initially.
// Where you handle your call disconnect
CXEndCallAction *endCallAction = [[CXEndCallAction alloc] initWithCallUUID:call.callKitUUID];
CXTransaction *transaction = [[CXTransaction alloc] init];
[transaction addAction:endCallAction];
[self requestTransaction:transaction];
Here's the supporting -requestTransaction method:
- (void)requestTransaction:(CXTransaction *)transaction {
[self.callController requestTransaction:transaction completion:^(NSError * _Nullable error) {
if (error) {
SCILog(#"Error requesting transaction: %#", error.localizedDescription);
} else {
SCILog(#"Requested transaction successfully");
}
}];
}
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.
On the Watch i send an AppMessage like this
DictionaryIterator *iter;
app_message_outbox_begin(&iter);
Tuplet value = TupletInteger(MESSAGE_TYPE, MESSAGETYPE_REFRESH);
dict_write_tuplet(iter, &value);
app_message_outbox_send();
I set the background modes and protocols for my app as described in the tutorial.
In iOS i set the listeners like this:
[PBPebbleCentral defaultCentral].delegate = self;
self.watch = [PBPebbleCentral defaultCentral].lastConnectedWatch;
NSLog(#"Pebble name: %#", _watch.name);
NSLog(#"Pebble serial number: %#", _watch.serialNumber);
[_watch appMessagesAddReceiveUpdateHandler:^BOOL(PBWatch *watch, NSDictionary *update) {
NSLog(#"Update received!");
return YES;
}];
[_watch appMessagesAddReceiveAllUpdatesHandler:^BOOL(PBWatch *watch, NSUUID *uuid, NSDictionary *update) {
NSLog(#"AllUpdate received!");
return YES;
}];
[_watch appMessagesAddAppLifecycleUpdateHandler:^(PBWatch *watch, NSUUID *uuid, PBAppState newAppState) {
NSLog(#"AppLifecycleUpdate received!");
}];
I already did send messages from the phone to the watch. So that way it works. But the listeners for incoming messages on the phone wont get called.
On the clock i get APP_MSG_SEND_TIMEOUT as error code. What did i wrong?
Do you have src/js/pebble-js-app.js in your watch app? I had the same problem and when i removed this generated file it started working.
Check to make sure you are using
appMessagesAddReceiveUpdateHandler:withUUID:
instead of
appMessagesAddReceiveUpdateHandler:
Pay attention to where you put the listener. For example if you are using the WeatherDemo app (provided by Pebble) you should do that after setting the the app UUID.
// Test if the Pebble's firmware supports AppMessages / Weather:
[watch appMessagesGetIsSupported:^(PBWatch *watch, BOOL isAppMessagesSupported) {
if (isAppMessagesSupported) {
...
[_targetWatch appMessagesAddReceiveUpdateHandler:^BOOL(PBWatch *watch, NSDictionary *update) {
NSLog(#"Received message: %#", update);
return YES;
}];
} else {
....
Another thing to pay attention is not to put it under
- (void)pebbleCentral:(PBPebbleCentral*)central watchDidConnect:(PBWatch*)watch isNew:(BOOL)isNew
because this function isn't called if the device is already connected.