SwiftUI: Dismiss Alert Programatically - swiftui

I have an alert that pops if location services are off or denied that will direct the user to Settings so that Location Services can be turned on. It is shown by setting a state variable, showAlert to true. The code I have is:
Alert(title: Text("Location Services"),
message: Text("Location Services is off or denied. The app must have your location to function. Please enable Location Services for the app in Settings."),
primaryButton: .default(Text("Settings"), action: {
self.showAlert = false // my attempt to clear the alert
UIApplication.shared.open(URL(string: UIApplication.openSettingsURLString)!)
}),
secondaryButton: .default(Text("Okay")))
The issue I have is that the alert doesn't dismiss when the user clicks the settings button to go to Settings to turn on Location Services. So, when the user returns to the app after enabling
them, the alert is still present stating services are not on. It is a bad user experience and I would like to have it dismissed before the user can read it upon return. As you can see from the code, I tried setting the showAlert state variable to false, but that didn't work. Any thoughts?

I think it is due to application becomes inactive, so lost event. Try the following
primaryButton: .default(Text("Settings"), action: {
// delay action a bit, to give chance to close alert
DispatchQueue.main.async {
UIApplication.shared.open(URL(string: UIApplication.openSettingsURLString)!)
}
}),

Related

Using SNS to send push notifications - Expo App

I am creating an Expo app that will use push notifications. I do not want to use Expo's server for that and instead I want to use AWS SNS. This is what I have done:
Created the app in Firebase and obtained the API Key.
Created the platform application on SNS using the API key obtained in step 1.
Used expo to obtained the device token.
Created an endpoint on SNS in the application created in step 2.
When I try to send a push notification, it doesn't work. I tried:
I made sure that use .getDevicePushTokenAsync() instead of getExpoPushTokenAsync() to get the device token that can be used with SNS.
When I test the app on my Android device I am able to console.log the device token (which is what I use to add the endpoint mentioned in step 4 above. Still nothing :(
I built a stand alone app, downloaded the .apk file, installed it on my device, BUT NOW THE PUSH TOKEN IS NOT BEING RETRIEVED. I really do not know why after building the app and installing it on my device this doesn't work anymore. It returns
I am thinking that if I am able to build the app and obtain the device token I may be able to make it work, but no luck. Any idea on what I am doing wrong or what I am supposed to be doing?
After 2 days of fighting to get this to work I finally did it. I am going to provide all the steps I followed in hopes that this can help other people:
Create a Firebase Project.
Click on "Add Project"
Provide a name. Click on "Continue"
Click on "Create Project"
Once the project is ready, click on "Continue"
Click on the gear icon right next to where it says "Project Overview"
Click on Project Settings.
In the page that shows up click on Cloud Messaging
Copy the value for "Server key". You will use it later when creating an application platform in SNS.
In that same "settings" page click on the "General" tab.
At the bottom of the page you are going to see a section that says "Your Apps". It should say: "There are no apps in your project". Click on the icon for Android.
This is an important step. You need to provide the "Android package name". Go to your Expo app and find the value for android.package. Copy it and put it where it asks for the Android package name on Firebase.
Click on "Register App".
Important step. Click on the button that says "Download google-services.json".
Save the file AT THE ROOT OF YOUR EXPO PROJECT.
Go back to your app.json file in your Expo project. Where it has the android value make sure this is what you have at least:
"android": {
"googleServicesFile": "./google-services.json",
"package": "com.astest.mypackage",
"useNextNotificationsApi": true
},
Follow the instructions here on how to set up your client app. Make sure you do not include the sendPushNotification() function since you will actually be using SNS instead.
In the registerForPushNotificationsAsync() function make sure you use .getDevicePushTokenAsync() instead of .getExpoPushTokenAsync()
Create a platform application in SNS
Push notification platform: Choose FCM
In your code, make sure you create an application endpoint in SNS. Or do it through the console.
Test the set up by sending a test message using the console in SNS. Select your endpoint and then click on "Publish message".
Click on "custom payload for each delivery".
use this code:
{
"GCM": "{ \"notification\": {\"title\": \"Title of notification\", \"body\": \"It works\" } }"
}
Click on publish message.
To publish a message programmatically you can do:
var sns = new AWS.SNS({ apiVersion: '2010-03-31', region: 'us-east-1'})
let notification = JSON.stringify({
'notification': {
'title': "title of notification",
'body': "Your message goes here",
'data': {}
}
});
var params = {
Message: JSON.stringify({
GCM: notification
}),
MessageStructure: "json",
TargetArn: "###Your Target ARN##"
};
sns.publish(params, function(err, data) {
if (err) {
console.log("There was an error sending the push notification----> ", err)
} // an error occurred
else{
console.log("Push notification sent successfully!!!! ", data);
} // successful response
});
Now, this is only for Android of course. But it works! Adapting it to APN shouldn't be too difficult.
EDIT 1
If you want your Expo, managed flow, app to respond to the notification you are sending you need to make a couple of changes. I spent the last 3 days trying to figure this out and I finally did.
According to the Expo documentation there are 2 types of push notifications you can send, "notification" and "data". In the example I provided in the steps above I was using "notification". The problem with that is that Expo "is not made aware that the notification is being received" when you use notifications, which means you can't respond to notifications. Therefore, if you need to respond or parse the response received in the notification you need to use a push notification of type "data".
Therefore, in the code example I gave above you need to change the word "notification" for "data".
In addition to sending "data" push notification instead of just "notifications", you must include the "experienceId in your payload, which you can get from your app
Defaults to Constants.manifest.id exposed by expo-constants. In the bare workflow, you must provide a value which takes the shape #username/projectSlug, where username is the Expo account that the project is associated with, and projectSlug is your slug from app.json.
The other change you need to make is the part where the content of your push notification goes. In the code I gave above I included it in a property called "body". With the "data" type of notification it needs to be changed to "message"
Also, the part where you add your key-value pairs also need to change. In the example I gave above the propertyis called "data", but this time we need to change it to "body"
An this is the resulting code:
let dataNotification = JSON.stringify({
'data': {
'title': title,
'message': message,
'body': { your key-value-pairs},
'experienceId': "#yourExpoUsername/Your-Project-Slug"
}
});

setAccessToken on embedded dashboard instance doesn't work initially

I'm using the PowerBi-Javascript library to embed a powerBI dashboard:
let embedConfig = {
type: 'dashboard',
tokenType: window.powerbi-client.models.TokenType.Aad,
id: '...',
embedUrl: '...',
accessToken: '...'
};
let embedInstance = powerbi.embed(elem, embedConfig);
Later I want to update the access token to ensure the dashboard can keep updating its data. We use the following approach:
embedInstance.setAccessToken('...')
.then(function (r) {
console.log('New access token set succesfully');
})
.catch(function (e) {
console.error('Error setting access token', e);
});
The setAccessToken promise resolves (I see the success log), however, I'm not certain that the access token is correctly propagated to the PowerBI instance.
What I find is that the next time the data on the embedded dashboard updates, I get a 401 response logged in the console for a request to https://wabi-uk-south-redirect.analysis.windows.net/powerbi/metadata/dashboards/[my-id].
If I subsequently refresh the data (before the access token expires again), everything seems to work fine.
It seems that the first data refresh after a new access token is set over an expired one fails.
This is a big problem for me because the data we have refreshes (automatically) less frequently than the access token expires, so after the first access token expires, the data never refreshes in my embedded instance.
Has anyone else had this problem, or does anyone know of a way around this?
UPDATE:
The requests that fail are always to /powerbi/metadata/dashboards/[my-id], whereas requests that work and cause the data to refresh are to /powerbi/metadata/dashboards/[my-id]/tiles. Note that I am not triggering these refreshes programatically, just by going to the powerbi web app (app.powerbi.com) and clicking "Refresh dashboard tiles".
a call to /powerbi/metadata/dashboards/[my-id] happens only on dashboard load to get the dashboard metadata.
It is NOT the best practice to embed a dashboard with a near expiry token.
In order to embed a dashboad make sure the AAD token you have does have few minutes until expiry.
Calls to sync service which update the tiles size and position and tile data are working OK.
In addition, in your application, set a timer which updates the token few minutes before expiry, this will make your dashboard more "live", because if the sync service fails with un-authorized exceptions few times, it goes into a sleep for almost 30 seconds.
please let me know if that helps.

Relay connection with parameters not getting reloaded on Field_Change mutation

My app loads a list/connection on startup and I'm using FB login. For auth with Relay I'm setting a cookie but the cookie is removed in case the app is removed from running in the background.
To avoid that the user has to login again I'm caching (AsyncStorage) the user info, if the user exists in there I'm auto login the user at the server so I get my Cookie back. The problem is that I need to reset/reload the connection, I tried a mutation with Field_Change returning the parent (which should include all it's children but it doesn't load the connection which is a child of a child).
I also tried to reset/recreate the store, also without success.
The list is loaded on my start page so I'm not changing pages.
My connection does have multiple params for paging as well as others and an #include.
The only way I could get it to work is to use forceUpdate in the component that also defines the connection, is there a better way?
Update:
Here's the fat query:
fragment on AuthorizationChangePayload #relay(pattern: true) {
viewer {
user
}
}
And the config for the mutation:
return [{
type: 'FIELDS_CHANGE',
fieldIDs: {
viewer: this.props.viewer.id
}
}];
Within user there's the connection which doesn't get updated, I didn't specify it as it has parameters and really I want to update the whole user and it's children objects.

500 error (CSRF) when login button doubleclicked in Django

I have a django application with a custom user system (that inherits from the base user system). However, I've noticed that if you happen to click the login button twice, you will sometimes get a CSRF error (if running in debug) or 500 error on our site. If you press the back button, it will redirect you to the first page you should see when you login. However, is there a way to prevent this error on a doubleclick? It turns out that our users are prone to this behaviour.
disable the login button after (the first) click. Using JQuery you can
$('#id_form').one('submit', function() {
$(this).find('input[type="submit"]').attr('disabled','disabled');
});

Facebook SDK : the operation couldn’t be completed (com.facebook.sdk error 2) on IOS7

I'm developing an app that post a state on Facebook but I receive this error:
the operation couldn’t be completed (com.facebook.sdk error 2)
although I have pressed OK when asked if it could publish on Facebook a post.
on iPhone settings I have already reset privacy and location, and though I give permission to publish I cannot find my app listed under "privacy" -> "Facebook"
here is my code to get permissions:
-(void)postOnFacebook
{
if (FBSession.activeSession.isOpen)
[self postOnWall];
else
{
[FBSession openActiveSessionWithPublishPermissions:[NSArray arrayWithObjects:#"publish_actions", nil]
defaultAudience:FBSessionDefaultAudienceEveryone
allowLoginUI:YES
completionHandler:^(FBSession *session,
FBSessionState status,
NSError *error)
{
if (error)
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Error"
message:error.localizedDescription
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
}
else if (FB_ISSESSIONOPENWITHSTATE(status))
{
NSLog(#"Sending post on wall request...");
[self postOnWall];
}
}];
};
}
Try this:
Yes, after you see this error, if you go to Settings, you will see that the setting for this app is turned "OFF". But the problem in this case is that the user was never prompted to allow access -- i.e. the setting was turned to OFF automatically on first time access. If the user was asked, then of course that is understandable, but this is not the case (it's as if the SDK silently and automatically pressed Don't Allow for the user). That's why this is a problem.
Before you read any further, I want to note that once the setting is set, you cannot simply repeat the process to test it, because once the setting is set, it will never ask the user (even deleting and reinstalling the app does not help). To test this issue, you need to reset the permissions by going to Settings -> General -> Reset -> Reset Location & Privacy, before you can try to replicate this again.
From testing, I've discovered that if you have offline_access in the permissions you are requesting for the first time, then it will give this login error (and not prompt the user and set the permission to OFF). The SDK does not check and tell you that this permission is not allowed; it just fails to login.