Sinch PushKit isn't working - sinch

When I test my VoIP cert with a 3rd party client like Pusher, didReceiveIncomingPushWith works. But when I call using Sinch, it doesn’t.
I followed the docs to ensure that I have push set up properly, and have the corresponding SINManagedPushDelegate setup as well:
sinchWrapper.shared.push = Sinch.managedPush(with: SINAPSEnvironment.production)
sinchWrapper.shared.push.delegate = self
sinchWrapper.shared.push.setDesiredPushTypeAutomatically()
sinchWrapper.shared.push.registerUserNotificationSettings()
func pushRegistry(_ registry: PKPushRegistry, didUpdate pushCredentials: PKPushCredentials, forType type: PKPushType) {
print("PUSH CREDENTIALS")
print(pushCredentials.token.map { String(format: "%02.2hhx", $0) }.joined())
print(pushCredentials.token)
sinchWrapper.shared.client.registerPushNotificationData(pushCredentials.token)
}
func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType, completion: #escaping () -> Void) {
print("Getting push")
sinchWrapper.shared.client.relayRemotePushNotification(payload)
}
//Setup Sinch pushKit------------------------------------------
func managedPush(_ managedPush: SINManagedPush!, didReceiveIncomingPushWithPayload payload: [AnyHashable : Any]!, forType pushType: String!) {
print("Getting poush notif")
return
}
func client(_ client: SINCallClient!, localNotificationForIncomingCall call: SINCall!) -> SINLocalNotification! {
print("Getting local notif")
let notif = SINLocalNotification()
notif.alertAction = "Missed call"
notif.alertBody = "Incoming call"
return notif
}
I’m pretty sure I have Since set up properly because didReceiveIncomingCall is called when the app is open.
So my question is, when user A calls user B, do I have to call any other function to ensure that user B gets notified when the app is in terminated state? Simply calling callUser doesn’t seem to work..

Related

iOS Swift app calls the SquarePOS app on iPad -Square app's screen displays briefly then reverts to my screen's calling view controller-did nothing

This app is intentionally tiny - the UI is just a button to initiate the api call. The appDelegate simply shows a console log proving the callback was executed. I did this because I can not get the Square payment system to work even thought the code and url setup etc looks right to me. I am wondering if I misunderstand something in the documentation.
My app ID is: sq0idp-TbgQGqSrC84qkfcXSTntNg
bundleID is: com.craig.POSSDKTest2
My info.plist setup is a screenshot:info.plist and URLScheme setup
My code in the api calling VC:
// ViewController.swift
// POSSDKTest2
//
// Created by Dr Craig E Curphey on 2022-06-14.
//
import UIKit
import SquarePointOfSaleSDK
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}//------------------------------------
#IBAction func doPaymentTapped(_ sender: Any) {
//------ API to Square right here ******-----------------
let callbackURL = URL(string: "POSSDKTest2Scheme://")!
SCCAPIRequest.setApplicationID("sq0idp-TbgQGqSrC84qkfcXSTntNg")
var money: SCCMoney?
do {
// Specify the amount of money to charge - $1.00
let todaysFeePennies = 100
money = try SCCMoney(amountCents: todaysFeePennies, currencyCode: "CAD")
} catch {
print("money conversion failed.")
}
let LoginID = "TestPatient"
//log values used to console:
print("Initiating payment api request")
print("callback: \(callbackURL)")
print("money: \(money!)")
print("LoginID: TestPatient")
print("Creating payment api request")
do
{
let apiRequest =
try SCCAPIRequest(
callbackURL: callbackURL,
amount: money!,
userInfoString: LoginID,
locationID: "",
notes: "TestingSDK2",
customerID: "tester2",
supportedTenderTypes: .all,
clearsDefaultFees: false,
returnsAutomaticallyAfterPayment: false,
disablesKeyedInCardEntry: false,
skipsReceipt: false
)
print("api initiating!")
try SCCAPIConnection.perform(apiRequest)
} catch let error as NSError {
print("Error detected: \(error.localizedDescription)")
}//end API
print("End of payment api function.")
}//------------------------------------
}
Here is my appDelegate code - I just want to see if I get a callback - I'll add the code to process the response later ...
// AppDelegate.swift
// POSSDKTest2
//
// Created by Dr Craig E Curphey on 2022-06-14.
//
var squareTokenID = "not specified - let it fail - I just want a callback!"
import UIKit
import SquarePointOfSaleSDK
#main
class AppDelegate: UIResponder, UIApplicationDelegate {
//-------------------------------------------
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
print("Callback was executed!")
guard SCCAPIResponse.isSquareResponse(url) else {
print("\(SCCAPIResponse.isSquareResponse(url))")
return true
}
do {
let response = try SCCAPIResponse(responseURL: url)
if let error = response.error {
// Handle a failed request.
print(error.localizedDescription)
} else {
// Handle a successful request.
}
} catch let error as NSError {
// Handle unexpected errors.
print(error.localizedDescription)
}
return true
}//-------------------------------------------------------
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
return true
}//-------------------------------------------------------
// MARK: UISceneSession Lifecycle
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
// Called when a new scene session is being created.
// Use this method to select a configuration to create the new scene with.
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
}//--------------------------------------------------------
func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
// Called when the user discards a scene session.
// If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
// Use this method to release any resources that were specific to the discarded scenes, as they will not return.
}//---------------------------------------------------------
}
Screenshot of console log from trying to do the api:
Console log
The work environment is Xcode 13.3, Swift, macOSMonterey 12.1 beta, iPad 10.2" (I think) mounted in a Square iPad kiosk with attached card reader, running iOS 15.5.
I would be very happy to send the whole project in a zip file to anyone who would be willing to take a look into why it is failing.

Integrate actix websocket with rabbitmq (lapin) in Rust

I've written a websocket server in Rust using actix. If anyone wants to check the full repo here https://github.com/fabracht/actix-websocket.
Now, I want to integrate rabbitmq into the project. For that, I found the lapin crate https://docs.rs/lapin/1.8.0/lapin/. But I'm having problems integrating it with the actix framework.
I would like to use my current implementation of the websocket to proxy the messages from rabbitmq back to the client.
This is the beginning of my attempt. Very early stages, so let me know if I'm going the wrong way, because now would be the time to change the approach.
First some context:
The websocket actor communicates with another actor that holds information about the sockets and the rooms.
pub struct WSConn {
pub id: Uuid,
room_id: Uuid,
hb: Instant,
lobby_address: Addr<Lobby>,
}
impl WSConn {
pub fn new(lobby: Addr<Lobby>, rabbit: Addr<MyRabbit>) -> Self {
Self {
id: Uuid::new_v4(),
room_id: Uuid::nil(),
hb: Instant::now(),
lobby_address: lobby,
}
}
fn hb(&self, context: &mut WebsocketContext<Self>) {
/// HEARTBEAT CODE GOES HERE ///
}
}
So, when the actor starts, I send a connect message to the Lobby, that handles all the logic for adding the connection to the Lobby struct.
impl Actor for WSConn {
type Context = WebsocketContext<Self>;
fn started(&mut self, ctx: &mut Self::Context) {
info!("Starting hearbeat");
self.hb(ctx);
let wsserver_address = ctx.address();
info!("A new client has connected with id {}", self.id);
self.lobby_address.send(Connect {
address: wsserver_address.recipient(),
room_id: self.room_id,
id: self.id
}).into_actor(self).then(|res, _, ctx| {
match res {
Ok(_) => (),
_ => ctx.stop()
}
fut::ready(())
}).wait(ctx);
}
fn stopping(&mut self, _ctx: &mut Self::Context) -> Running {
info!("Stopping actor");
self.lobby_address.do_send(Disconnect {
room_id: self.room_id,
id: self.id
});
Running::Stop
}
}
Here's what the lobby looks like:
type Socket = Recipient<WSServerMessage>;
pub struct Lobby {
pub sessions: DashMap<Uuid, Socket>,//self id to self
pub rooms: DashMap<Uuid, DashSet<Uuid>>,//room id to list of users id
}
The entire Lobby code is quite big, so I won't put it here. Let me know if you need to see that and I'll provide the code.
So, once the client connects, it gets assigned to the default room. When a client sends a message to the server, the message gets processed by the StreamHandler.
impl StreamHandler<Result<ws::Message, ws::ProtocolError>> for WSConn {
fn handle(&mut self, item: Result<Message, ProtocolError>, ctx: &mut Self::Context) {
match item.unwrap() {
ws::Message::Binary(bin) => ctx.binary(bin),
ws::Message::Ping(bin) => {
self.hb = Instant::now();
ctx.pong(&bin);
}
ws::Message::Pong(_) => self.hb = Instant::now(),
ws::Message::Close(reason) => {
ctx.close(reason);
ctx.stop();
}
ws::Message::Text(text) => {
let command = serde_json::from_str::<Command>(&text)
.expect(&format!("Can't parse message {}", &text));
info!("{:?}", command);
if command.command.starts_with("/") {
info!("This is a {} request", command.command);
match command.command.as_ref() {
"/join" => {
info!("Join Room {}", command.payload);
let uid = Uuid::from_str(command.payload.as_str().unwrap()).expect("Can't parse message {} to uuid");
self.lobby_address.send(Join {
current_room: self.room_id,
room_id: uid,
id: self.id
}).into_actor(self).then(|res, _, ctx| {
match res {
Ok(_) => (),
_ => ctx.stop()
}
fut::ready(())
}).wait(ctx);
self.room_id = uid;
},
_ => ()
}
} else {
info!("Text is {}", text);
self.lobby_address.do_send(ClientActorMessage {
id: self.id,
msg: command,
room_id: self.room_id,
});
}
}
_ => {
info!("Something weird happened. Closing");
self.lobby_address.do_send(Disconnect {
room_id: self.room_id,
id: self.id
});
ctx.stop();}
}
}
}
So, as you can see, if you send a message with the command /join and a payload with a valid uuidv4, you join the room. I'm only allowing the client to be a part of one room at a time. So, when you join one, you're automatically removed from the last one.
Ok, so now let's talk about the rabbitmq connection.
The way I thought about this was to use a connection pool to keep the rabbitmq connection and use that connection to create the channels.
So I started by defining the struct that will hold my connection pool.
use actix::{Actor, Context, Handler, StreamHandler};
use deadpool_lapin::{Config, Pool, Runtime};
use deadpool_lapin::lapin::Error;
use lapin::message::Delivery;
use crate::lapin_server::messages::CreateChannel;
pub struct MyRabbit {
pub pool: Pool,
}
impl MyRabbit {
pub fn new() -> Self {
let mut cfg = Config::default();
cfg.url = Some("amqps://ghqcmhat:KbhPAA309QRg7TjdgFEV14pQRheoh44P#codfish.rmq.cloudamqp.com/ghqcmhat".into());
let new_pool = cfg.create_pool(Some(Runtime::Tokio1)).expect("Can't create pool");
MyRabbit {
pool: new_pool
}
}
}
impl Actor for MyRabbit {
type Context = Context<Self>;
}
Having this as an actor allows me to start the actor as I start the websocket server. This is done in main.
#[actix_web::main]
async fn main() -> std::io::Result<()>{
std::env::set_var("RUST_LOG", "actix_web=info,info");
env_logger::init();
// Start Lobby actor and get his address
let websocket_lobby = Lobby::default().start();
let rabbit = MyRabbit::new().start();
let application_data = web::Data::new(Appdata::new());
info!("Starting server on 127.0.0.1:8080");
let server = HttpServer::new(move || {
App::new()
.wrap(Logger::default())
.route("/ws/",web::get().to(websocket_handler))
.app_data(Data::new(websocket_lobby.clone()))
.app_data(Data::new(rabbit.clone()))
.app_data(application_data.clone())
});
server.bind("127.0.0.1:8080")?.run().await
}
To accommodate for the new actor, I added a new parameter to my request handler.
pub async fn websocket_handler(request: HttpRequest, stream: web::Payload, srv: Data<Addr<Lobby>>, rab: Data<Addr<MyRabbit>>, data: Data<Appdata>) -> Result<HttpResponse, Error> {
let mut counter = data.counter.lock().unwrap();
counter.add_assign(1);
info!("This is request # {}", counter);
let ws = WSConn::new(srv.get_ref().clone(), rab.get_ref().clone());
let response = ws::start(ws, &request, stream);
debug!("Response: {:?}", &response);
response
}
Now, my WSConn struct looks like this.
pub struct WSConn {
pub id: Uuid,
room_id: Uuid,
hb: Instant,
lobby_address: Addr<Lobby>,
rabbit_address: Addr<MyRabbit>
}
I know that, if I want to consume from a topic in rabbitmq, I need the exchange name, the type, the routing key and the queue name.
So, I put these in a struct as well
pub struct Channel {
pub queue_name: String,
pub exchange_name: String,
pub exchange_type: ExchangeKind,
pub routing_key: String
}
impl Default for Channel {
fn default() -> Self {
Self {
queue_name: "".to_string(),
exchange_name: "".to_string(),
exchange_type: Default::default(),
routing_key: "".to_string()
}
}
}
But that's where I'm stuck. First, I'm not sure this deadpool_lapin is the right crate to use for this. I'm also not sure how to translate the example on lapin's page, which uses
async_global_executor::block_on
And spawns new threads using async_global_executor::spawn to consume messages.
So, again, what I want is to be able to proxy messages coming from the websocket to rabbitmq and vice versa.
So, if a client connects to the websocket and sends a message like:
{
command: "SUBSCRIBE"
payload: "topic_name"
}
The result should be that messages published on that topic will get sent to him.
Sending an UNSUBSCRIBE should undo that.
Any help here would be greatly appreciated.
Please let me know if more information is needed.
Thank you
Fabricio

VOIP notification is being missing or delay

How do I know that my device is blocked from receiving VoIP notifications?
The application stops receiving VoIP notifications after receiving for 3-4 times. I understand that from iOS 13 VoIP notifications should be reported to CallKit. Even after reporting to CallKit, I'm going through this issue of not receiving VoIP notifications.
We have set apns-expiration to 0 and the priority to immediately(10).
func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType, completion: #escaping () -> Void) {
dictPayload = payload.dictionaryPayload[K.KEY.APS] as? [String : Any]
if dictPayload![K.KEY.ALERTTYPE] as? String == K.KEY.VOIPCALL {
self.displayIncomingCall(uuid: appDelegate.uudiForCall, handle: (self.dictPayload!["handle"] as? String)!) { (error) in
}
CallProviderDelegate.sharedInstance.callDidReceiveIncomingCallfromKill(callInfo: self.dictPayload!)
} else if dictPayload![K.KEY.ALERTTYPE] as? String == K.KEY.PUSHTOTALK {
isPTTON = true
pjsua_set_no_snd_dev()
CallHandler.sharedCallManager()?.muteCall(true)
CallHandler.sharedCallManager()?.setAudioSessionSpeaker()
CallProviderDelegate.sharedInstance.callDidReceivePTTFromKIll(callFromName: dictPayload!["title"] as? String, callfromExt: dictPayload![K.KEY.BODY] as? String)
} else if dictPayload![K.KEY.ALERTTYPE] as? String == K.KEY.HANGUP {
isPTTON = false
CallProviderDelegate.sharedInstance.endCallFromPTT(endCallUDID: appDelegate.uudiForCall)
}
}
func displayIncomingCall(
uuid: UUID,
handle: String,
hasVideo: Bool = false,
completion: ((Error?) -> Void)?
) {
let update = CXCallUpdate()
update.remoteHandle = CXHandle(type: .phoneNumber, value:(handle))
CallProviderDelegate.sharedInstance.provider.reportNewIncomingCall(with: uuid, update: update, completion: { error in })
}
XCODE: 11.3.1,
SWIFT: 4.2 &
iOS: 13.0 +
I am trying to figure out this issue since the last 2 months but not able to resolve it. Please help me
Thanks In Advance.
Actually, it seems that you aren't reporting a new incoming call for every VoIP push notification. It's true that when there is an active CallKit call, you can receive VoIP pushes without reporting a new incoming call, but it's not as simple as it might seem. Since CallKit and PushKit are asynchronous, you are not guaranteed that when you receive a push of type K.KEY.PUSHTOTALK or K.KEY.HANGUP the call has already started. Moreover, if dictPayload is nil, you fail to report a new incoming call.
Anyway, I think that the biggest problem in your code is that you're not calling the completion handler of the pushRegistry(:didReceiveIncomingPushWith...) method. You should do the following:
self.displayIncomingCall(uuid: appDelegate.uudiForCall, handle: (self.dictPayload!["handle"] as? String)!) { (error) in
completion() // <---
}
and
CallProviderDelegate.sharedInstance.provider.reportNewIncomingCall(with: uuid, update: update, completion: { error in
completion()
})
// or
CallProviderDelegate.sharedInstance.provider.reportNewIncomingCall(with: uuid, update: update, completion: completion)

Square Connect SDK opens a blank page

I am attempting to implement Square Connect iOS SDK, and after implementing and clicking the pay button it opens up the Square Payment app and redirects to a blank page .. have you guys had the same issues ?
My App delegate has the proper section:
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
guard let sourceApplication = options[.sourceApplication] as? String,
sourceApplication.hasPrefix("com.squareup.square") else {
return false
}
do {
let response = try SCCAPIResponse(responseURL: url)
if let error = response.error {
// Handle a failed request.
print(error.localizedDescription)
} else {
// Handle a successful request.
}
} catch let error as NSError {
// Handle unexpected errors.
print(error.localizedDescription)
}
return true
}
I have created a proper URL scheme inside the Square portal. Also my view controller has the correct code:
func charge()
{
// connect v1
if let callbackURL = URL(string: "myscheme://")
{
do
{
SCCAPIRequest.setClientID("xxxxxxxxxxx")
let amount = try SCCMoney(amountCents: 100, currencyCode: "USD")
let request = try SCCAPIRequest(
callbackURL: callbackURL,
amount: amount,
userInfoString: nil,
locationID: nil,
notes: "Purchase for cleaning",
customerID: nil, supportedTenderTypes: .all,
clearsDefaultFees: true,
returnAutomaticallyAfterPayment: true)
try SCCAPIConnection.perform(request)
}
catch let error as NSError {
print(error.localizedDescription)
}
}
}
https://snag.gy/R4A30L.jpg
Andrei
You need to make sure you delete all apps from the phone with having duplicate bundle ids. This way you will make sure you go back to the original app which triggered the payment.

How to make a CompletionHandler with Firebase Swift 3

I need to get some informations from Firebase to out it on UIViewCell. But the problem is that the function return before he get all the value. I know that's because it's asynchronous, i tried to make a completion Handler like that:
static func hardProcessingWithString(input: String, completion: #escaping (_ result: String) -> Void) {
Database.database().reference().child("Player_1").observe(.value) {
(firDataSnapshot) in
completion((firDataSnapshot.value as? String)!)
}
}
This is how i try to get the value :
var myVar: String!
hardProcessingWithString(input: "commands"){
(result: String) in
myVar = result
}
I get this error when I call the function:
Could not cast value of type '__NSDictionaryM' (0x102e292b0) to 'NSString' (0x102434c60).
Here's my Firebase database:
Or if you know how to make a Promise with Firebase let me know!
Step 1:
Create a swift file like Constant.swift. Then place the below code inside it
typealias DownloadComplete = () -> ()
Step 2:
Create a function where you want.
func hardProcessingWithString(completed: #escaping DownloadComplete) {
Database.database().reference().child("Player_1").observe (.value, with: { (firDataSnapshot) in
// put your code here
completed()
})
}
Step 3:
when you call above method in your code
hardProcessingWithString() {
// you can get the result here
}

Resources