performSegue not working since update to Xcode 9 (and iOS 11) - ios11

On my app I have a Splash screen which checks if an user is already logged in with Facebook. If he is not, the splash screen pushes a login screen to the navigation stack. If the user is logged in, the splash screen also pushes the login screen but calls, on viewDidAppear of the login screen, a method that pushes the main app screen.
So, when the user is logged in I should see the following view controllers stack on my app:
SplashViewController > LoginViewController > MainViewController.
This flow worked without problems on iOS 9 and iOS 10.
However since I upgraded to iOS 11 this flow stopped working.
Now, if the user is logged in I the main view controller does not appear, only the login view controller remains visible. I checked on the debugger that performSegue to the main app screen is indeed called.
There are two weird details. The first is that the buttons on this login view controller don't work anymore (there is a button which calls the Facebook Login flow from the FacebookLogin SDK).
The second is that if I go to Xcode and click on the "Debug View Hierarchy" button, while Xcode is processing and creating the view hierarchy the main view controller suddenly appears on my app.
It seems that the main view controller is being pushed but its view is not being presented; so the segues on the LoginViewController don't work anymore. However, pressing "Debug View Hierarchy" seems to trigger something that updates the view hierarchy.
I am working with Xcode 9.0.1. This problem is happening both on the Xcode simulators and on my iPad Mini 2, which is currently running iOS 11.0.3.
Thanks.

I don't know exactly why this was happening, but I was able to solve this problem the following way.
On the SplashViewController, where I called the direct segue to the login view controller, which would call another segue on the viewWillAppear() method, I forced the segue to be performed on the main queue:
// SplashViewController
struct Identifier {
static let login = "Login"
static let direct = "Direct"
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
login()
}
func login() {
// If there already exists a Facebook token we try to login directly.
if let accessToken = AccessToken.current {
bm.login(facebookToken: accessToken.authenticationToken, completion: { (error, user) in
guard error == nil else {
self.performSegue(withIdentifier: Identifier.login, sender: self)
return
}
// This call on the main queue wasn't needed on iOS 10
DispatchQueue.main.async {
self.performSegue(withIdentifier: Identifier.direct, sender: self)
}
})
} else {
performSegue(withIdentifier: Identifier.login, sender: self)
}
}
then on LoginViewController:
// LoginViewController
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if direct {
// Without calling the previous segue on the main queue this
// segue would be called but the pushed view controller's
// view would not appear on the screen until a refresh event
// was triggered.
self.performSegue(withIdentifier: Identifier.direct, sender: nil)
}
}

Related

symfony 5 web debug toolbar showing anonymous and cannot redirect after onAuthenticationSuccess

I have been following along with the following Symfony tutorials, but I believe they are using version 4 and I am using version 5. They reach a point in the tutorial which shows that the web debug toolbar shows the user's email logged and they even pointed out that if you see logged as anonymous, then just refresh. I did refresh, but it still shows as anon.
As you can see by the following screen shot, login was successful and it shows the correct username as well:
I started to watch the first part of the tutorial - listed below - when I reached a point in the second part that pointed out that I should watch the first part, which made sense, that I might have missed something, but that was an even older version of Symfony and things have changed in version 5.
First part of the tutorial
Second part of the tutorial
After going through the tutorials, I still have the web debug tool showing anon. Now, I am using React as a form to POST the email and password - see next screen shot - would that effect how the web debug toolbar, but I do not see how, because the console shows that the system knows the user.
Does anyone know a config that needs to be changed?
I have tried changing the following within src\Security\TokenAuthenticator - getUser from:
return $this->em->getRepository(User::class)
->findOneBy(['apiToken' => $credentials])
;
To:
return $this->em->getRepository(User::class)->findOneBy(['email' => $credentials['email']]);
But no change, still shows anon
Also, as the subject states, I cannot redirect via onAuthenticationSuccess
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
{
// on success, let the request continue
return new RedirectResponse($this->urlGenerator->generate('app_homepage'));
}
I do not see why this does not work. Again, is it because I am posting via a React app?
Turns that it is because I am running an older version of the browser Firefox and the log in is working. You can see by the screen shot of both Firefox and Chrome, that it is working Chrome
As far as the redirect goes, PHPStorm was saying that I did not have urlGenerator available in the TokenAuthenticator class. As a result, I should have noticed before and this is what I did to correct it:
In my src\Security\TokenAuthenticator I have the following:
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
In my constructor:
private $em;
private $urlGenerator;
public function __construct(EntityManagerInterface $em, UrlGeneratorInterface $urlGenerator)
{
$this->em = $em;
$this->urlGenerator = $urlGenerator;
}
My onAuthenticationSuccess:
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
{
// on success, let the request continue
// redirect to some "app_homepage" route - of wherever you want
return new RedirectResponse($this->urlGenerator->generate('app_homepage'));
}
But it is still not working
I have tried
use Symfony\Component\HttpFoundation\RedirectResponse;
private $redirectResponse;
public function __construct(EntityManagerInterface $em, RedirectResponse $redirectResponse)
{
$this->em = $em;
$this->redirectResponse = $redirectResponse;
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
{
// on success, let the request continue
// redirect to some "app_homepage" route - of wherever you want
return $this->redirectResponse->redirectToRoute('app_homepage');
}
But PHPStorm tells me that it cannot find method redirectToRoute within class RedirectResponse
The only thing that I have found to work with redirecting users to the home page after successful is login, is within my React login app. I have an async to my handleClick method, after the fetch POST, I have a setTimeout of 3000 that uses a plain javascript:
window.location.href = '/';
I would love to know the answer to why I cannot redirect via the Authenticator class that I have created, but at least someone who is using Firefox will not have to wonder why their web debug tool is not showing that the user has successfully logged in while still showing anon

Check if shortcut is present on homescreen as in normal mobile apps

I am trying to get the user to 'install' the shortcut icon to a webpage and use it as an icon to the PWA.
I am wondering if any of you had a chance to somehow discover if the user installed the shortcut on home screeen?
This is an interesting case, because when developing on Android or such one has access to such information, on the other hand I don't recall browser giving that information away.
The beforeinstallprompt will only fire if the user has NOT installed the PWA
First: Use that to check if installed
window.addEventListener('beforeinstallprompt', (e) => {
// If you get inside here, the PWA is not installed
});
Example code here
https://developers.google.com/web/fundamentals/app-install-banners
Second: If you are showing your own deferred prompt like the examples at the link above, you can listen to know if they close that without adding to home screen (A2HS)
// Wait for the user to respond to the prompt
this.deferredPrompt.userChoice
.then((choiceResult) => {
if (choiceResult.outcome === 'accepted') {
// User said yes to your A2HS
} else {
this.deferredPromptRejected = true;
}
});
Third: You can check if your PWA is running as a standalone
function isRunningStandalone() {
return (window.matchMedia('(display-mode: standalone)').matches);
}

Why is session storage being drawn upon between different browser instances?

Background
In an application I'm working on, I've found that I can define values in sessionStorage in Chrome 62 on Windows 10, and that apparently changing that value in one tab affects other tabs that point to the same key.
I was operating under the assumption that localStorage is supposed to persist information across all browser windows, while sessionStorage is only supposed to persist information for a specific window or tab.
More specifically, I have an AngularJS service I'm using as a layer for sessionStorage interactions:
export class PersistenceSvc {
public static $inject: string[] = ['$window'];
public constructor(public $window: ng.IWindowService) {}
public save<T>(name: string, data: T): void {
const saveData: string = JSON.stringify(data);
this.$window.sessionStorage.setItem(name, saveData);
}
public load<T>(name: string): T {
const loadData: string = this.$window.sessionStorage.getItem(name);
const result: T = JSON.parse(loadData) as T;
return result;
}
}
...That I use from a run block in order to implement some data persistence in my application.
export function persistSomeData(
someSvc: Services.SomeService,
userAgentSvc: Services.UserAgentSvc,
persistenceSvc: Services.PersistenceSvc,
$window: ng.IWindowService) {
if(userAgentSvc.isMobileSafari()) {
// Special instructions for iOS devices.
return;
}
const dataToPersist: Models.DataModel = persistenceSvc.load<Models.DataModel>('SomeData');
if(dataToPersist) {
// Set up the state of someSvc with the data loaded.
} else {
// Phone home to the server to get the data needed.
}
$window.onbeforeunload = () => {
persistenceSvc.save<Models.DataModel>('SomeData', someSvc.dataState);
};
}
persistSomeData.$inject = [
// All requisite module names, omitted from example because lazy.
];
angular
.module('app')
.run(persistSomeData);
When only operating using a single tab, this works fine (unless running from an iOS device, but that's tangential to what I'm encountering.) When you do the following though, you start seeing some more interesting behavior...
Steps:
1. Open a Chrome instance. Create a new tab, and drag that out such that it becomes its own window.
2. Navigate to your site, that's using the above code.
3. Do things on your site that cause someSvc's data state to have different data in the first browser.
4. Do things on your site that cause someSvc's data state to have different data in the second browser.
5. Do something on your site that draws upon someSvc's data state in the first browser.
6. Observe that the data utilized on the first browser instance, was sourced by the second browser instance. (This is the problem, right here.)
Question:
In the past I haven't done a lot of cookie/localStorage/sessionStorage programming, so it's very possible that I've terribly misunderstood something. Bearing that in mind, why is it that window.sessionStorage is behaving in a way that the MDN documentation as well as the winning answer to this SO question says it shouldn't be behaving in?
EDIT: It turns out there is a problem, but it's not clientside. Closing this question, as I was operating under the assumption that the client was the problem.
There is something wrong with your code as a quick and easy test on the browser console shows that sessionStorage only impacts the browser tab that is open. A change in the right tab is not reflecting to the left tab:

Ext.Msg.Confirm - wait for a user response?

I understand that Ext.Msg.Confirm is asynchronous, and will proceed if you do not provide a callback.
Below is my code - this is called if the user tries to navigate from the current screen while in edit mode. I'm passing in a callback to the method - fn is my callback event, but regardless of how I code this the logic continues to navigate away from the page. Is there anyway to stop propagation until the user has selected yes or no in the confirmation box?
Here is the code for the Confirmation:
displaySaveConfirmation: function(fn) {
var title = 'Do you want to Save?'
var msg = 'You are currently Editing Standard Text. Would you like to save your changes?'
var box = Ext.Msg.confirm(title, msg, function(buttonId, value) {
if (buttonId === 'no'){
fn.call();
} else {
return false;
}
}, this );
box.setZIndex(400);
box.setCls('alert-box');
var buttons = Ext.ComponentQuery.query('button', box);
for (var i=0; i<buttons.length; i++) {
if (buttons[i].getItemId()=="no")
buttons[i].addCls('blackBtn');
else if (buttons[i].getItemId()=="yes")
buttons[i].addCls('blueBtn');
this.addReleaseEvent(box, buttons[i]);
}
},
As of now you do the following:
The event is fired
You open the MessageBox
Your function returns, and it does not return "false".
The browser navigates away before the user can click "yes" or "no".
What you want to do is the following:
The event is fired
You open the MessageBox
Your function returns false.
The browser does not navigate away.
The user clicks "yes" or "no".
If the answer is "yes", you navigate to the previously requested target page manually (e.g. close the browser window, use history.back() or similar).
Alternatively, if the work to get that up and running (and tested, with all the possible cases how to navigate away and how to find the target) is too much, you can opt for the window.alert dialog instead, which does not look nearly as nice, but works synchronously.
Or you can do as I did: I just returned false after bringing up a MessageBox that says: "You cannot leave the page during edit. Please save or abort the post."

call services of that page , if (mobile app) net online angularjs + cordova (android and IOS)

If mobile data is on, I want to reload the current page, how do I know that the net is on.
added below code deviceready event
document.addEventListener("online", function toggleCon(e, $route) {
if (e.type == "offline") {
navigator.notification.alert("Sorry, you are offline.", function() {}, "Offline!");
} else {
// navigator.notification.alert("Woot, you are back online.", function () { }, "Online!");
$route.reload();
}
}, false);
It's working once the device is ready, what if the connection lost in between.
I want to reload current page services ,is there is a way to identify that.
online event will be called every time your app got back online.
deviceready event is fired when you app is loaded. online event is registered after that.
if your app lost connection after that and came back online that event and its callback will always be called.

Resources