In AppDelegate's didFinishLaunchingWithOptions, I implementation the code for getting authorization status.
UNAuthorizationOptions authOptions = (UNAuthorizationOptionAlert | UNAuthorizationOptionBadge | UNAuthorizationOptionSound);
[[UNUserNotificationCenter currentNotificationCenter] requestAuthorizationWithOptions:authOptions completionHandler:^(BOOL granted, NSError * _Nullable error) {
if (granted) {
[application registerForRemoteNotifications];
}
}];
But, UNUserNotificationCenter.currentNotificationCenter().requestAuthorizationWithOptions ends always with granted = false in the completion handler (no matter if I tap on the allow or deny button on the permission dialog) in very first time. And next times when I open my app, it returns right granted status.
How to fix it?
Related
Is there a way to notify system`s users on real-time that the system is in deployment process(publish to production)?The purpose is to prevent them from starting to do atomic operations?
the system is an ASP.NET-based system and it already has SignalR Dlls, but I do not exactly know how to get to the "source" in the application from which I know that the system is deploying right now.
This is highly dependent on your deployment process, but I achieved something similar in the following way:
I created a method in one of my controllers called AnnounceUpdate:
[HttpPost("announce-update")]
public async Task<IActionResult> AnnounceUpdate([FromQuery] int secondsUntilUpdate, string updateToken)
{
await _tenantService.AnnounceUpdate(secondsUntilUpdate, updateToken);
return Ok();
}
The controller method takes in the amount of seconds till the update, as well as a secret token to ensure not just anyone can call this endpoint.
The idea is that we will call this controller just before we deploy, to announce the pending deployment. I make my deployments using Azure Dev Ops, and so I was able to create a release task that automatically runs the following PowerShell code to call my endpoint:
$domain = $env:LOCALURL;
$updateToken = $env:UPDATETOKEN;
$minutesTillUpdate = 5;
$secondsUntilUpdate = $minutesTillUpdate * 60;
$len = $secondsUntilUpdate / 10;
#notify users every 10 seconds about update
for($num =1; $num -le $len; $num++)
{
$url = "$domain/api/v1/Tenant/announce-update?secondsUntilUpdate=$secondsUntilUpdate&updateToken=$updateToken";
$r = Invoke-WebRequest $url -Method Post -UseBasicParsing;
$minsLeft = [math]::Floor($secondsUntilUpdate/60);
$secsLeft = $secondsUntilUpdate - $minsLeft * 60;
$timeLeft;
if($minsLeft -eq 0){
$timeLeft = "$secsLeft seconds";
}else{
if($secsLeft -eq 0){
$timeLeft = "$minsLeft minute(s)";
}else{
$timeLeft = "$minsLeft minute(s) $secsLeft seconds";
}
};
$code = $r.StatusCode;
Write-Output "";
Write-Output "Notified users $num/$len times.";
Write-Output "Response: $code.";
Write-Output "$timeLeft remaining."
Write-Output "_________________________________"
Start-Sleep -Seconds 10;
$secondsUntilUpdate = $secondsUntilUpdate - 10;
}
Write-Output "Allowing users to log out.";
Write-Output "";
Start-Sleep -Seconds 1;
Write-Output "Users notfied! Proceeding with update.";
As you can see, on the script I have set that the time till the update is 5 minutes. I then call my AnnounceUpdate endpoint every 10 seconds for the duration of the 5 minutes. I have done this because if I announce an update that will occur in 5 minutes, and then 2 minutes later someone connects, they will not see the update message. On the client side I set a variable called updatePending to true when the client receives the update notification, so that they do not keep on getting a message every 10 seconds. Only clients that have not yet seen the update message will get it.
In the tenant service I then have this code:
public async Task AnnounceUpdate(int secondsUntilUpdate, string updateToken)
{
if (updateToken != _apiSettings.UpdateToken) throw new ApiException("Invalid update token");
await _realTimeHubWrapper.AnnouncePendingUpdate(secondsUntilUpdate);
}
I simply check if the token is valid and then conitnue to call my HUB Wrapper.
The hub wrapper is an implementation of signalR's hub context, which allows to invoke signalR methods from within our code. More info can be read here
In the HUB wrapper, I have the following method:
public Task AnnouncePendingUpdate(int secondsUntilUpdate) =>
_hubContext.Clients.All.SendAsync("UpdatePending", secondsUntilUpdate);
On the client side I have set up this handler:
// When an update is on the way, clients will be notified every 10 seconds.
private listenForUpdateAnnouncements() {
this.hubConnection.on(
'PendingUpdate', (secondsUntilUpdate: number) => {
if (!this.updatePending) {
const updateTime = currentTimeString(true, secondsUntilUpdate);
const msToUpdate = secondsUntilUpdate * 1000;
const message =
secondsUntilUpdate < 60
? `The LMS will update in ${secondsUntilUpdate} seconds.
\n\nPlease save your work and close this page to avoid any loss of data.`
: `The LMS is ready for an update.
\n\nThe update will start at ${updateTime}.
\n\nPlease save your work and close this page to avoid any loss of data.`;
this.toastService.showWarning(message, msToUpdate);
this.updatePending = true;
setTimeout(() => {
this.authService.logout(true, null, true);
this.stopConnection();
}, msToUpdate);
}
}
);
}
I show a toast message to the client, notifying them of the update. I then set a timeout (using the value of secondsUntilUpdate) which will log the user out and stop the connection. This was specifically for my use case. You can do whatever you want at this point
To sum it up, the logical flow is:
PowerShell Script -> Controller -> Service -> Hub Wrapper -> Client
The main take away is that somehow we need to still trigger the call to the endpoint to announce the update. I am lucky enough to be able to have it run automatically during my release process. If you are manually publishing and copying the published code, perhaps you can just run the PowerShell script manually, and then deploy when it's done?
I'm using Distriqt Push Notifications Extension and I can't get it working correctly if the user does not allow PNs on first run: the application ends registering the user because it states that PNs are enabled and available.
I do the following:
if (PushNotifications.isSupported()) {
registerPushNotifications();
}
private function registerPushNotifications():void {
PushNotifications.service.addEventListener(PushNotificationEvent.REGISTER_SUCCESS, onPushNotificationToken);
PushNotifications.service.register(MODEL.Configuration.GCM_SENDER_ID);
}
private function onPushNotificationToken(event:PushNotificationEvent):void {
if (PushNotifications.service.isEnabled) { registerDevice(); }
}
Does not PushNotifications.service.isEnabled supposed to be false if the user disallows it? When does it become false? How am I supposed to handle this case scenario?
I've found what was happening in my application:
I'm handling activate/deactivate events to enable and disable background execution: NativeApplication.nativeApplication.executeInBackground = true;. This makes your application able to run on background, ignoring the UI which asks for user permission and it happens that PushNotifications.service.isEnabled is true on first run after installation.
What I've done is delaying adding activation and deactivation listeners till one of this things happen first:
The device does not support push notifications PushNotifications.isEnabled == false
When the device receive a push token
When the device fails receiving a push token
I hope this helps someone.
Just posting this here for anyone else who has issues with the isEnabled flag:
var hasRequestedPermissionsOnce:Boolean = false;
// You should load hasRequestedPermissionsOnce from some persistent storage, defaulting to false
...
PushNotifications.init( APP_KEY );
if (PushNotifications.isSupported)
{
if (PushNotifications.service.isEnabled)
{
// Notifications have been enabled by the user
// You are free to register and expect a registration success
register();
}
else if (!hasRequestedPermissionsOnce)
{
// You should implement hasRequestedPermissionsOnce somewhere to check if this is the first run of the app
// If we haven't called register once yet the isEnabled flag may be false as we haven't requested permissions
// You can just register here to request permissions or use a dialog to delay the request
register();
}
else
{
// The user has disabled notifications
// Advise your user of the lack of notifications as you see fit
}
}
...
private function register():void
{
// You should save hasRequestedPermissionsOnce to a shared object, file or other persistent storage
hasRequestedPermissionsOnce = true;
PushNotifications.service.addEventListener( PushNotificationEvent.REGISTER_SUCCESS, registerSuccessHandler );
PushNotifications.service.addEventListener( PushNotificationEvent.REGISTER_FAILED, registerFailedHandler );
PushNotifications.service.register( GCM_SENDER_ID );
}
Original source here: https://gist.github.com/marchbold/fb0438cf326a44cea0cf#file-distriqt-extensions-pushnotifications-isenabled-as
i'm developing a website that use geolocation, but nothing happens if i access it with location service disabled. It should show user alert message to enable location service.
What you can do, if geolocation is supported by browser, is check the error code of getCurrentPosition
PERMISSION_DENIED - if the user clicks that “Don’t Share” button or otherwise denies you access to their location.
POSITION_UNAVAILABLE - if the network is down or the positioning satellites can’t be contacted.
TIMEOUT - if the network is up but it takes too long to calculate the user’s position. How long is “too long”? I’ll show you how to
define that in the next section.
if(navigator.geolocation){
navigator.geolocation.getCurrentPosition(
docode, handle_error)
}
function handle_error(err) {
if (err.code == 1) {
// PERMISSION_DENIED
}
else if (err.code == 2) {
// POSITION_UNAVAILABLE
}
else if (err.code == 3) {
// TIMEOUT
}
}
this is my problem:
I have a registration UIViewController so the user can send his information via POST.
When the user taps "send", I instantiate a NSURLSessionUploadTask using the NSURLSession uploadTaskWithRequest:fromData:completionHandler: method and, if he wants to go back, he can taps the "back" button to go back to the previous UIViewController.
When the user taps the "back" button, an UIAlertView is shown just to ask the user if he's sure to cancel or not the registration. While the UIAlertView is shown, I mark the NSURLSessionUploadTask as suspended, using the suspend method, so the user can resume the registration if he isn't sure to go back. My problem is there: when I mark the task as suspended, it seems that the NSURLSessionUploadTask ignores it and stills doing the registration showing two UIAlertViews: the one which confirms the cancelation and the one having the server response.
Please, can you help me with this? I'm doing something wrong? Maybe there's something I'm ignoring. I can't suspend and resume it later :/
Doing the upload:
uploadTask = [sesion uploadTaskWithRequest:request
fromData:data
completionHandler:^(NSData * data, NSURLResponse * response, NSError * error)
{
...
}];
[uploadTask resume];
Suspending the upload in the implementation of the "back" button:
if (uploadTask != nil) && (uploadTask == NSURLSessionTaskStateRunning))
{
[uploadTask suspend];
}
I'm trying to create a user account through the apigee JS API. This worked just fine when I was last doing this before the holidays in mid December. Now, however, I get a 401 Unauthorized error reading token_expired.
Is there a way to refresh the token? I don't know why it would have expired.
This is what I'm trying. First I instantiate the data client. No problems here:
var dataClient;
var client_creds = {
orgName: '*******',
appName: '*******'
}
dataClient = new Apigee.Client(client_creds);
Later, when trying to create a new user, I get the token_expired error:
dataClient.request(options, function (error, response) {
if (error) {
console.log(response);
alert("Something went wrong when trying to create the user. " + response.error)
// Error
} else {
// Success - the user has been created, now login.
dataClient.login(user.email, user.password,
function (err) {
if (err) {
//error - could not log user in
console.log("There was an error logging in " + user.name);
} else {
//success - user has been logged in
}
}
);
}
});
I've also tried dataClient.signup, but same error.
There are no refresh tokens within App Services; you'll need to follow the login flow in order to retrieve a new token. Note that you can specify the ttl parameter, like so, so you don't need to do this as frequently:
https://api.usergrid.com/{org}/{app}/token?ttl=604800
By default, this is set to 7 days, but you can change the default app max ttl to 0 (non-expiring) or something else like 31104000000 (365 days).
To do that, you make a PUT request:
https://api.usergrid.com/{org}/{app}/?client_id={app_client_id}&client_secret={app_client_secret}
With JSON payload:
{
"accesstokenttl":0
}
Or for 1 year:
{
"accesstokenttl":31104000000
}
If that doesn't work for you, the authorization tokens for the JavaScript SDK are kept in your browser's local storage. In Chrome, use the Developer Tools. In the Resources tab on the left hand side expand the Local Storage entry. You should see something like "http://usergrid.dev" or something similar. Choose that and on the right hand side you should see an entry for accessToken. Delete that and it should solve your problem.