I'm trying to enable sound for my Firebase push notifications and I'm not sure if there is code in the App Delegate which I need to implement, or if the code in my index.js is wrong.
I thought there was something I needed to import in AppDelegate related to sound, but all the guides I've found for implementing push notifications only have the basic code where [options] contains the only thing related to the notification's sound.
index.js Code:
var notification = {
notification: {
title: conversation.conversationName,
body: user.username + ': ' + message.text,
sound: 'default'
},
topic: topic
}
App Delegate Code: Function called in didFinishLaunchingWithOptions.
import UIKit
import Firebase
import UserNotifications
private func attemptRegisterForNotifications(application: UIApplication) {
Messaging.messaging().delegate = self
UNUserNotificationCenter.current().delegate = self
let options: UNAuthorizationOptions = [.alert, .badge, .sound]
UNUserNotificationCenter.current().getNotificationSettings { (settings) in
if settings.authorizationStatus == .authorized {
// Push notifications already authorized, do nothing
print ("push notifications authorized")
} else if settings.authorizationStatus == .notDetermined {
// User hasn't specified notification status
UNUserNotificationCenter.current().requestAuthorization(options: options, completionHandler: { (granted, error) in
if let error = error {
print ("Failed to request authorization:", error)
return
}
guard granted else {return}
DispatchQueue.main.async {
application.registerForRemoteNotifications()
}
})
} else if settings.authorizationStatus == .denied {
// User has denied notifications
UNUserNotificationCenter.current().requestAuthorization(options: options, completionHandler: { (granted, error) in
if let error = error {
print ("Failed to request authorization:", error)
return
}
let alertController = UIAlertController(title: "Enable Push Notifications", message: "Enable push notifications for optimal chat experience", preferredStyle: .alert)
let settingsAction = UIAlertAction(title: "Settings", style: .default) { (_) -> Void in
guard let settingsUrl = URL(string: UIApplication.openSettingsURLString) else {
return
}
if UIApplication.shared.canOpenURL(settingsUrl) {
UIApplication.shared.open(settingsUrl, completionHandler: { (success) in
})
}
}
let cancelAction = UIAlertAction(title: "Cancel", style: .default, handler: nil)
alertController.addAction(cancelAction)
alertController.addAction(settingsAction)
alertController.preferredAction = settingsAction
DispatchQueue.main.async {
self.window?.rootViewController?.present(alertController, animated: true, completion: nil)
}
})
}
}
}
Related
I need your help to mock a twilio service which sends a message, using jest to mock the service
I have the next code:
import { SQSEvent } from "aws-lambda";
import { GetSecretValueResponse } from "aws-sdk/clients/secretsmanager";
export async function sendSms(event: SQSEvent, data: GetSecretValueResponse) {
const secrets = JSON.parse(data.SecretString);
const accountSid = secrets.TWILIO_ACCOUNT_SID;
const authToken = secrets.TWILIO_AUTH_TOKEN;
const twilioNumber = secrets.TWILIO_PHONE_NUMBER;
if (accountSid && authToken && twilioNumber) {
//Create a Twilio Client
const client = new Twilio(accountSid, authToken);
//Loop into al records of the event, every record is every message sent from Sqs
for (const record of event.Records) {
const body = JSON.parse(record.body);
const userNumber = "+" + body.number;
//SendMessage function
try {
const message = client.messages.create({
from: twilioNumber,
to: userNumber,
body: body.message,
});
return message;
} catch (error) {
return `Failed to send sms message. Error Code: ${error.errorCode} / Error Message: ${error.errorMessage}`;
}
}
} else {
return "You are missing one of the variables you need to send a message";
}
}
The I call this function from my index:
import { SQSEvent } from "aws-lambda";
import { sendSms } from "./services/sendSms/sendSms";
import { getSecret } from "./services/obtainSecrets/getSecret";
import { SecretsManager } from "aws-sdk";
export const lambdaHandler = async (event: SQSEvent) => {
try {
const obtainedSecret = await getSecret()
.then((credentials: SecretsManager.GetSecretValueResponse) => {
return credentials;
})
.catch(error => {
return error;
});
const response = sendSms(event, obtainedSecret)
.then(response => {
return response;
})
.catch(error => {
return error;
});
return {
message: "OK " + obtainedSecret + response,
code: 200,
};
} catch (error) {
throw new Error(error);
}
};
I have already make some tests, but them always makes a connection with Twilio api(requiring the real token, sid,etc), and I need to mock the Twilio service, so the function I call in my test.ts doesn't connects to internet.
import { Twilio } from "twilio";
import { MessageInstance } from "twilio/lib/rest/api/v2010/account/message";
import { sendSms } from "../../services/sendSms/sendSms";
//mock Twilio library and sendSms service
jest.mock("twilio");
jest.mock("../../services/sendSms/sendSms");
const smsMessageResultMock: Partial<MessageInstance> = {
status: "sent",
sid: "AC-lorem-ipsum",
errorCode: undefined,
errorMessage: undefined,
};
describe("SMS Service", () => {
describe("Send Message", () => {
it("Should fail", async () => {
// update smsMessageResultMock to simulate a faled response
const smsMessageMock = {
...smsMessageResultMock,
status: "failed",
errorCode: 123,
errorMessage: "lorem-ipsum",
};
// simulated response of secret management
let data = {
ARN: "arn:aws:secretsmanager:us-west-2:123456789012:secret:MyTestDatabaseSecret-a1b2c3",
Name: "MyTestDatabaseSecret",
SecretString:
'{"TWILIO_ACCOUNT_SID": "ACTWILIO_ACCOUNT_SID","TWILIO_AUTH_TOKEN": "TWILIO_AUTH_TOKEN","TWILIO_PHONE_NUMBER": "TWILIO_PHONE_NUMBER"}',
VersionId: "EXAMPLE1-90ab-cdef-fedc-ba987SECRET1",
VersionStages: ["AWSPREVIOUS"],
};
// simulated response of SqsEvent
let event = {
Records: [
{
messageId: "19dd0b57-b21e-4ac1-bd88-01bbb068cb78",
receiptHandle: "MessageReceiptHandle",
body: '{"message": "Hello world","number": "(506)88888888"}',
attributes: {
ApproximateReceiveCount: "1",
SentTimestamp: "1523232000000",
SenderId: "123456789012",
ApproximateFirstReceiveTimestamp: "1523232000001",
},
messageAttributes: {},
md5OfBody: "{{{md5_of_body}}}",
eventSource: "aws:sqs",
eventSourceARN: "arn:aws:sqs:us-east-1:123456789012:MyQueue",
awsRegion: "us-east-1",
},
],
};
// simulate tokens for Twilio
const accountSid = "ACfjhdskjfhdsiuy876hfijhfiudsh";
const authToken = "fjfuewfiuewfbodfiudfgifasdsad";
//create client with mocked Twilio
const client = new Twilio(accountSid, authToken);
//call messages.create of Twilio client, and give it the expected result created
client.messages.create = jest
.fn()
.mockResolvedValue({ ...smsMessageMock });
console.log(await sendSms(event, data));
//expectes the function sendSms(event, data) to throw an error
await expect(sendSms(event, data)).rejects.toThrowError(
`Failed to send sms message. Error Code: ${smsMessageMock.errorCode} / Error Message: ${smsMessageMock.errorMessage}`
);
});
});
});
(event and data are simulated responses of SqsEvent and GetSecretValueResponse)
The problem is that when I run the npm test it throws me an error of Twilio's authentication, an it is because I'm passing self created tokens.
Expected substring: "Failed to send sms message. Error Code: 123 / Error Message: lorem-ipsum"
Received message: "Authentication Error - invalid username"
at success (node_modules/twilio/lib/base/Version.js:135:15)
at Promise_then_fulfilled (node_modules/q/q.js:766:44)
at Promise_done_fulfilled (node_modules/q/q.js:835:31)
at Fulfilled_dispatch [as dispatch] (node_modules/q/q.js:1229:9)
at Pending_become_eachMessage_task (node_modules/q/q.js:1369:30)
at RawTask.Object.<anonymous>.RawTask.call (node_modules/asap/asap.js:40:19)
at flush (node_modules/asap/raw.js:50:29)
So what I suppose is that the test is connecting to internet and calling Twilio's api.
I appreciate if you could help me.
I think what you want to do is mock the class returned by the module, using jest.mock('twilio', mockImplementation) and in mockImplementation return a function to act as a constructor that will take your account SID and auth token arguments and then return a mockClient implementation, which in this case needs to return an object which has a messages property, which in turn is an object with a create property that is a mock function.
It's probably easier to just show the code.
const mockClient = {
messages: {
create: jest.fn().mockResolvedValue({ ...smsMessageMock });
}
};
jest.mock("twilio", () => {
return function(accountSid, authToken) {
return mockClient;
}
});
I'm getting the error below. My problem is NOT with the actual error but the fact that it is saying that the error was Uncaught. If you take a look at my auth.service.ts and sign-in.component.ts files I am catching the error.
My question is, why am getting the Error: Uncaught (in promise) error in the console? What am I missing?
I'm using
"#angular/fire": "^7.0.4"
"firebase": "^9.0.2"
"rxjs": "6.6.7"
auth.service.ts
/**
* Sign in
*
* #param credentials
*/
signIn(credentials: { email: string; password: string }): Promise<any>
{
return this.auth.signInWithEmailAndPassword(credentials.email, credentials.password)
.then((userCredential) => {
// Signed in
const user = userCredential.user;
//console.log(user);
// Store the access token in the local storage
userCredential.user.getIdToken().then(token => {
this.accessToken = token;
//console.log(token);
})
// Set the authenticated flag to true
this._authenticated = true;
// Store the user on the user service
//this._userService.user = user;
// ...
})
.catch((error) => {
const errorCode = error.code;
const errorMessage = error.message;
console.log('Show Error', error.code);
throw errorCode;
});
}
sign-in.component.ts
/**
* Sign in
*/
signIn(): void
{
// Return if the form is invalid
if ( this.signInForm.invalid )
{
return;
}
// Disable the form
this.signInForm.disable();
// Hide the alert
this.showAlert = false;
// Sign in
this._authService.signIn(this.signInForm.value)
.then(
() => {
// Set the redirect url.
// The '/signed-in-redirect' is a dummy url to catch the request and redirect the user
// to the correct page after a successful sign in. This way, that url can be set via
// routing file and we don't have to touch here.
const redirectURL = this._activatedRoute.snapshot.queryParamMap.get('redirectURL') || '/signed-in-redirect';
// Navigate to the redirect url
this._router.navigateByUrl(redirectURL);
},
(response) => {
console.log('error from auth.service', response);
// Re-enable the form
this.signInForm.enable();
// Reset the form
this.signInNgForm.resetForm();
// Set the alert
this.alert = {
type : 'error',
message: 'Wrong email or password'
};
// Show the alert
this.showAlert = true;
}
);
}
First of all, my native language is not English, so if I write like a fool you know why.
try this:
_authService.service.ts
import { getAuth, signInWithEmailAndPassword, Auth, inMemoryPersistence, browserLocalPersistence } from '#angular/fire/auth';
constructor(private _fireAuth: Auth,) {
/**
* Sign-in
*
* #param credentials
* #param rememberMe
*/
async signIn(credentials: { email: string; password: string }, rememberMe: boolean): Promise<any> {
// firebase Persistence.LOCAL browserLocalPersistence
// firebase Persistence.SESSION browserSessionPersistence
// firebase Persistence.NONE inMemoryPersistence
return new Promise(async (resolve, reject) => {
//Initialize auth()
const auth = getAuth();
// Extra function
if (rememberMe) {
await getAuth().setPersistence(browserLocalPersistence).catch(error => reject(-1));
} else {
await getAuth().setPersistence(inMemoryPersistence).catch(error => reject(-1));
}
signInWithEmailAndPassword(auth, credentials.email, credentials.password).then(async (userCredential) => {
// Signed in
const user = userCredential.user;
console.log(user);
// Store the access token in the local storage
await userCredential.user.getIdTokenResult().then(token => {
this.accessToken = token.token;
console.log(token);
})
// Set the authenticated flag to true
this._authenticated = true;
}).catch(error => reject(error.code));
});
}
Note: As you can see I have added some extra functions that you can remove if you are not interested (setPersistence), this allows you to take into account the user's choice to stay logged in if he wants to, or to remove his login when he closes the tab.
sign-in.component.ts
alert = {
userNotFound : false,
wrongPassword: false,
unknownError : false,
};
/**
* Sign in
*/
signIn(): void
{
// Return if the form is invalid
if ( this.signInForm.invalid )
{
return;
}
// Disable the form
this.signInForm.disable();
// Hide the alert
this.showAlert = false;
// Sign in
this._authService.signIn(this.signInForm.value)
.then(
() => {
// Set the redirect url.
// The '/signed-in-redirect' is a dummy url to catch the request and redirect the user
// to the correct page after a successful sign in. This way, that url can be set via
// routing file and we don't have to touch here.
const redirectURL = this._activatedRoute.snapshot.queryParamMap.get('redirectURL') || '/signed-in-redirect';
// Navigate to the redirect url
this._router.navigateByUrl(redirectURL);
},
(response) => {
console.log('error from auth.service', response);
// Re-enable the form
this.signInForm.enable();
// Reset the form
this.signInNgForm.resetForm();
// Set the alert
if (error === - 1) {
this.alert.unknownError = true;
} else if (error === 'auth/email-not-found' || error === 'auth/user-not-found') {
this.alert.userNotFound = true;
} else if (error === 'auth/wrong-password') {
this.alert.wrongPassword = true;
}
}
);
}
For me, explicitly catching the error as in the above answers still resulted in the error being sent to the console as Uncaught (in Promise). For whatever reason, in addition to sending the error to be caught via Promise.catch, Angular was routing the error through its default ErrorHandler and claiming that the error was uncaught. I had to override the default ErrorHandler with my own, detect that these were FirebaseErrors and then ignore them (since I already had an explicit Promise.catch defined where I needed it).
Some tips in case they are helpful:
Angular only recognizes error handlers defined on the root module. Definitions on child modules seem to be ignored. You get one global error handler that needs to do everything.
The core FirebaseErrors seem to be stored in a rejection property on the main error object. You can detect them like so:
import { ErrorHandler, Injectable } from "#angular/core";
import { FirebaseError } from "firebase/app";
interface AngularFireError extends Error {
rejection: FirebaseError;
}
function errorIsAngularFireError(err: any): err is AngularFireError {
return err.rejection && err.rejection.name === 'FirebaseError';
}
// Not providedIn 'root': needs special handling in app.module to override default error handler.
#Injectable()
export class YourErrorHandler implements ErrorHandler {
handleError(error: any) {
// AngularFire errors should be catchable and handled in components; no need to further process them.
if (!errorIsAngularFireError(error)) {
console.error(error);
}
}
}
And in your root module:
providers: [
...,
{ provide: ErrorHandler, useClass: YourErrorHandler }
],
as Frank mentionned you are throwing an error without catching it back at a higher lever what I would try would be to do as so:
try {
this._authService.signIn(this.signInForm.value)
.then(
() => {
// Set the redirect url.
// The '/signed-in-redirect' is a dummy url to catch the request and redirect the user
// to the correct page after a successful sign in. This way, that url can be set via
// routing file and we don't have to touch here.
const redirectURL = this._activatedRoute.snapshot.queryParamMap.get('redirectURL') || '/signed-in-redirect';
// Navigate to the redirect url
this._router.navigateByUrl(redirectURL);
},
(response) => {
console.log('error from auth.service', response);
// Re-enable the form
this.signInForm.enable();
// Reset the form
this.signInNgForm.resetForm();
// Set the alert
this.alert = {
type : 'error',
message: 'Wrong email or password'
};
// Show the alert
this.showAlert = true;
}
);
} catch(e) {}
just surround your code with a try catch block so the error is muted. I didn't tested it but maybe calling another catch method after then could do the trick still it would be at the same level (Promise level) so I'm not sure it would work.
I recently had to revamp an old Electron app. I found out that it had contextIsolation set to false, so I went ahead and set it to true (and btw set nodeIntegration to false).
As expected, it broke the ipc communications
So I used, as advised in many places, a preload script that enables some communications channels
Here is my preload.js:
const {
contextBridge,
ipcRenderer
} = require("electron");
contextBridge.exposeInMainWorld(
"api", {
send: (channel, data) => {
console.log("Send on channel " + channel)
// whitelist channels
let validChannels = [];
if (validChannels.includes(channel)) {
ipcRenderer.send(channel, data);
}
},
receive: (channel, func) => {
console.log("Receive on channel " + channel)
let validChannels = [
"set-auth-token",
"set-window-name",
"get-window-name",
"send-message-to-one-drive",
"update-badge",
"is-hidden",
"open-google-sign-in"
];
if (validChannels.includes(channel)) {
// Deliberately strip event as it includes `sender`
ipcRenderer.on(channel, (event, ...args) => func(...args));
}
}
}
);
My issue is the "api" parameter, I don't know what to put in there.
Knowing that the website that's loaded by that app sends messages this way:
let electron = false;
if ('require' in window) {
electron = window.require('electron');
}
const isAppDesktop = !!electron;
const DesktopHelper = {
isAppDesktop() {
return isAppDesktop;
},
updateBadge(badgeCount) {
return isAppDesktop ? electron.ipcRenderer.send('update-badge', badgeCount) : undefined;
},
setAuthToken(token) {
return electron.ipcRenderer.send('set-auth-token', token);
},
isHidden() {
return isAppDesktop ? electron.ipcRenderer.sendSync('is-hidden') : undefined;
},
};
export default DesktopHelper;
Can someone help?
contextBridge.exposeInMainWorld(apiKey, api) - the apiKey argument is the name of the object that will be exposed as window[apiKey].
In your example, the apiKey is set to the string "api", so the exposed methods are available in the window.api object. You can access them as window.api.send and window.api.receive.
The methods in DesktopHelper should look like this:
updateBadge(badgeCount) {
return isAppDesktop ? window.api.send('update-badge', badgeCount) :
undefined;
},
Or simply api.send('update-badge', badgeCount), since window is the global. You may obviously want to pick a more descriptive name for the API.
I'm trying to create an application where the user sign's in through my UI. I have textLabel's that I'm pulling data from, I want to taje that data and use it to log into a website online and get data once logged in. What I have right now is a WKWebView that is invisible most of the time that loads the website I want to log into it, then fills out the log in forms and clicks the button - all via evaluateJavaScript. My only problem right now is that I'm trying to come up with something that will be able to check if the user logged in incorrectly. What I am trying to do is wait till the javascript executes, then check the webView and see if the url has changed to the log in page. This works, however only if the user hasn't failed to enter a correct password. If they get an incorrect password once, all the entries after that say the password is incorrect. Im using an observe value to check if the page is done loading but i need help implementing this.
#IBAction func loginBtnPressed(_ sender: Any) {
if(!validate(usernameText) || !validate(passwordText))
{
self.validationLabel.text = "One or more fields are empty."
UIView.animate(withDuration: 0.25, animations: {
self.validationLabel.isHidden = false
})
return;
}
let oldUrl = webView.url?.absoluteString;
self.validationLabel.isHidden = true
webView.evaluateJavaScript("document.getElementById('fieldAccount').value = \(usernameText!.text!)", completionHandler: {(result,err) in print(result ?? "No Result"); print (err ?? "No Error") })
webView.evaluateJavaScript("document.getElementById('fieldPassword').value = '\(passwordText!.text!)'", completionHandler: {(result,err) in print(result ?? "No Result"); print (err ?? "No Error") })
webView.evaluateJavaScript("document.getElementById('btn-enter').click()", completionHandler: {(result,err) in
print(result ?? "No Result"); print (err ?? "No Error") })
DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(6), execute: {
// Put your code which should be executed with a delay here
if(self.webView.url?.absoluteString.contains("termGrades"))!
{
print("We're in bois: ")
self.view = self.webView
}else
{
print("Did not get to grades page..")
self.validationLabel.text = "Invalid Username/Password"
UIView.animate(withDuration: 0.25, animations: {
self.validationLabel.isHidden = false
})
let portalURL = URL(string: "*website*")
let request = URLRequest(url: portalURL!)
self.webView.load(request)
return;
}
}) }
I'm a new ios developer but I don't want to give up on this, If you have any help , or suggestions on a better way to wait for the javascript to click the button and the page to fullyload please share
EDIT:
I think I found a solution but for some reason now the login button has to be clicked twice for it to process anything...
#IBAction func loginBtnPressed(_ sender: Any) {
if(!validate(usernameText) || !validate(passwordText))
{
self.validationLabel.text = "One or more fields are empty."
UIView.animate(withDuration: 0.25, animations: {
self.validationLabel.isHidden = false
})
return;
}
self.validationLabel.isHidden = true
webView.evaluateJavaScript("document.getElementById('fieldAccount').value = \(usernameText!.text!.description)", completionHandler: {(result,err) in print(result ?? "No Result"); print (err ?? "No Error") })
webView.evaluateJavaScript("document.getElementById('fieldPassword').value = '\(passwordText!.text!)'", completionHandler: {(result,err) in print(result ?? "No Result"); print (err ?? "No Error") })
webView.evaluateJavaScript("document.getElementById('btn-enter').click()", completionHandler: {(result,err) in
print(result ?? "No Result"); print (err ?? "No Error") })
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if keyPath == "loading"
{
if(!webView.isLoading) {
print("Finished navigating to url \(webView.url!)");
if(webView.url! == URL(string: "**"))
{
statusLabel.text = "Done."
}else if(webView.url! == URL(string: "**"))
{
self.validationLabel.text = "Invalid Username/Password"
UIView.animate(withDuration: 0.25, animations: {
self.validationLabel.isHidden = false
})
}else if(webView.url! == URL(string: "**"))
{
self.view = webView
}
}
}
func webView(webView: WKWebView!, didFinishNavigation navigation: WKNavigation!) {
print("Finished navigating to url \(webView.url)");
}
Make sure to set the delegate of the WKWebView to your view controller
This is specific to the Opentok API. I'm quite new to javascript and struggling to get this all to work nicely.
Here's a link to the docs if you need it:
https://tokbox.com/developer/guides/
My error is:
signal error (500): Unable to send signal - you are not connected to the session.
My html file looks like this:
var apiKey = 45317102;
var sessionID = "#{#session.session_id}";
if (OT.checkSystemRequirements() == 1) {
var session = OT.initSession(apiKey, sessionID);
} else {
// the client does not support WebRTC
console.log('does not support WebRTC');
}
var token = "#{#token.token}";
var publisher;
var publisherOptions = {
width: 600,
height: 400,
name: 'Publisher',
// publishAudio: true,
// publishVideo: false
}
var subscriberOptions = {
width: 300,
height: 200,
name: 'Subscriber'
}
// for publisher
session.connect(token, function(error) {
if (error) {
console.log(error.message);
} else {
session.publish('myPublisherDiv', publisherOptions);
console.log('Publishing a stream.');
}
});
// for subscribers
session.on({
streamCreated: function(event) {
session.subscribe(event.stream, 'subscribersDiv', subscriberOptions);
console.log("New stream in the session: " + event.stream.streamId);
}
});
// check if the user is a moderator
if (session.capabilities.forceDisconnect == 1) {
console.log('you can moderate.')
} else {
console.log('you cant moderate');
}
session.signal(
{
data:"hello"
},
function(error) {
if (error) {
console.log("signal error ("
+ error.code
+ "): " + error.message);
} else {
console.log("signal sent.");
}
}
);
and my backend (ruby / rails) controller looks like this:
def create
session = OPENTOK.create_session media_mode: :routed
session_id = session.session_id
#session = Session.new(session_id: session_id)
if #session.save
redirect_to #session
else
render 'new'
end
end
def show
#session = Session.find(params[:id])
unless params[:id].blank?
s = Session.find(params[:id])
token = OPENTOK.generate_token s.session_id
#token = Token.create(session_id: s.id, token: token, active: true)
#token.save
end
end
now I'm not able to make the person who creates the session a moderator (ability to remove others) and it seems like I'm in-fact even unable to connect to the session. Even though when I run it I'm able to have 2 people in the same session at the same time.
Does anybody know what's happening (or more likely what I'm missing from my code)?