My Vue PWA app shows white blank page on iOS - ios

I am currently working one a Vuejs PWA app (using Vue CLI 3). My app works fine on Android, Windows, and macOS, but it only shows a white blank page on ios. More specifically, when I use an iPhone and access my app with Safari or Chrome, it all shows a white page. When I add my app to the home screen of the iPhone, when I open it up, it still shows a white page.
this is link to my app.
White blank screen
I have tried many workarounds here but it not work.
here are some parts of my code:
vue.config.js
module.exports = {
transpileDependencies: ['vuetify'],
pwa: {
workboxPluginMode: 'InjectManifest',
workboxOptions: {
swSrc: 'src/config/firebase-messaging-sw.js',
exclude: [/\.map$/, /_redirects/],
},
manifestOptions: {
start_url: 'index.html',
},
name: 'AppName',
appleMobileWebAppCapable: 'yes',
},
};
router
const router = new VueRouter({
mode: "history",
base: process.env.BASE_URL,
routes: [
{
path: "/",
name: "RenterMainView",
component: RenterView,
children: [
{
path: "index.html",
name: "Home",
component: Home,
meta: { guest: true },
alias: ""
},
{
path: "detail/:typeId",
name: "HostelDetail",
component: HostelDetail,
meta: { guest: true }
}
]
}
]
});
router.beforeEach((to, from, next) => {
if (to.matched.some((record) => record.meta.requiresAuth)) {
if (window.$cookies.get('jwt') === null) {
// not logged in
next({
name: 'Login',
params: { nextUrl: to.path, preUrl: from.path },
});
} else {
// logged in
const role = window.$cookies.get('role');
if (to.matched.some((record) => record.meta.is_vendor)) {
if (role === 'vendors') {
next();
} else {
next(from.path);
}
} else if (to.matched.some((record) => record.meta.is_admin)) {
if (role === 'admins') {
next();
} else {
next(from.path);
}
} else if (to.matched.some((record) => record.meta.is_renter)) {
if (role === 'renters') {
next();
} else {
next(from.path);
}
} else {
next();
}
}
} else if (to.matched.some((record) => record.meta.guest)) {
// not require authen
next();
} else {
// not require authen
next();
}
});

Try changing the start_url
I found that on
Chrome Windows
Chrome MacOS
Android
I was OK to use quite a wide variety of start_url values, such as the "index.html" that you have, or "/index.html", etc.
However on iOS, I had to use
start_url: "."
The other values were fine on the other platforms, but gave a blank white screen on iOS.
Try creating a blank PWA with the Vue CLI
Does that work correctly on iOS?
Then step by step change it to contain your app.
Find out where it breaks.
That's how I found the start_url issue.

I hope that this could help someone. In this project, I make an PWA app using Vue, and I use Firebase Cloud Messagging to send notification from server to client. Unfortunaly, due to some restrictions on iOS, FCM doesn't work on it, that is why the application show a white page on iOS. So, the solution is to disable FCM on iOS
if (firebase.messaging.isSupported()) { // your code go here }
using above code to disable FCM on firebase service worker file

Related

Video as a background image not working in Gatsby PWA on iOS

I created a opt-in app for potential interims for our company, i worked with Gatsby and for now am quite satisfied with the result. I made it an Progressive Web App as that is fairly easy with the gatsby plugin.
The PWA works great on Android and shows the background video as expected, but on iOS the video doesn't show.
I updated all the packages and dependencies to the last versions but that doesn't change a thing. I tried googling the issue but got a lot of search results off people trying to let a PWA play video in the background when the app is closed (not my case).
{
resolve: `gatsby-plugin-manifest`,
options: {
name: `Afstuderen bij Arcady`,
short_name: `Afstuderen`,
start_url: `/`,
background_color: `#FFF`,
theme_color: `#00a667`,
display: `standalone`,
icon: `src/images/bear_green.png`,
},
},
'gatsby-plugin-offline',
And the content of the service worker
importScripts("workbox-v3.6.3/workbox-sw.js");
workbox.setConfig({modulePathPrefix: "workbox-v3.6.3"});
workbox.core.setCacheNameDetails({prefix: "gatsby-plugin-offline"});
workbox.skipWaiting();
workbox.clientsClaim();
/**
* The workboxSW.precacheAndRoute() method efficiently caches and responds to
* requests for URLs in the manifest.
*/
self.__precacheManifest = [
{
"url": "webpack-runtime-aec2408fe3a97f1352af.js"
},
{
"url": "app-5b624d17337895ddf874.js"
},
{
"url": "component---node-modules-gatsby-plugin-offline-app-shell-js-b97c345e19bb442c644f.js"
},
{
"url": "offline-plugin-app-shell-fallback/index.html",
"revision": "ac0d57f6ce61fac4bfa64e7e08d076c2"
},
{
"url": "0-d2c3040ae352cda7b69f.js"
},
{
"url": "component---src-pages-404-js-cf647f7c3110eab2f912.js"
},
{
"url": "static/d/285/path---404-html-516-62a-0SUcWyAf8ecbYDsMhQkEfPzV8.json"
},
{
"url": "static/d/604/path---offline-plugin-app-shell-fallback-a-30-c5a-BawJvyh36KKFwbrWPg4a4aYuc8.json"
},
{
"url": "manifest.webmanifest",
"revision": "5a580d53785b72eace989a49ea1e24f7"
}
].concat(self.__precacheManifest || []);
workbox.precaching.suppressWarnings();
workbox.precaching.precacheAndRoute(self.__precacheManifest, {});
workbox.routing.registerRoute(/(\.js$|\.css$|static\/)/, workbox.strategies.cacheFirst(), 'GET');
workbox.routing.registerRoute(/^https?:.*\.(png|jpg|jpeg|webp|svg|gif|tiff|js|woff|woff2|json|css)$/, workbox.strategies.staleWhileRevalidate(), 'GET');
workbox.routing.registerRoute(/^https?:\/\/fonts\.googleapis\.com\/css/, workbox.strategies.staleWhileRevalidate(), 'GET');
/* global importScripts, workbox, idbKeyval */
importScripts(`idb-keyval-iife.min.js`)
const WHITELIST_KEY = `custom-navigation-whitelist`
const navigationRoute = new workbox.routing.NavigationRoute(({ event }) => {
const { pathname } = new URL(event.request.url)
return idbKeyval.get(WHITELIST_KEY).then((customWhitelist = []) => {
// Respond with the offline shell if we match the custom whitelist
if (customWhitelist.includes(pathname)) {
const offlineShell = `/offline-plugin-app-shell-fallback/index.html`
const cacheName = workbox.core.cacheNames.precache
return caches.match(offlineShell, { cacheName }).then(cachedResponse => {
if (cachedResponse) return cachedResponse
console.error(
`The offline shell (${offlineShell}) was not found ` +
`while attempting to serve a response for ${pathname}`
)
return fetch(offlineShell).then(response => {
if (response.ok) {
return caches.open(cacheName).then(cache =>
// Clone is needed because put() consumes the response body.
cache.put(offlineShell, response.clone()).then(() => response)
)
} else {
return fetch(event.request)
}
})
})
}
return fetch(event.request)
})
})
workbox.routing.registerRoute(navigationRoute)
let updatingWhitelist = null
function rawWhitelistPathnames(pathnames) {
if (updatingWhitelist !== null) {
// Prevent the whitelist from being updated twice at the same time
return updatingWhitelist.then(() => rawWhitelistPathnames(pathnames))
}
updatingWhitelist = idbKeyval
.get(WHITELIST_KEY)
.then((customWhitelist = []) => {
pathnames.forEach(pathname => {
if (!customWhitelist.includes(pathname)) customWhitelist.push(pathname)
})
return idbKeyval.set(WHITELIST_KEY, customWhitelist)
})
.then(() => {
updatingWhitelist = null
})
return updatingWhitelist
}
function rawResetWhitelist() {
if (updatingWhitelist !== null) {
return updatingWhitelist.then(() => rawResetWhitelist())
}
updatingWhitelist = idbKeyval.set(WHITELIST_KEY, []).then(() => {
updatingWhitelist = null
})
return updatingWhitelist
}
const messageApi = {
whitelistPathnames(event) {
let { pathnames } = event.data
pathnames = pathnames.map(({ pathname, includesPrefix }) => {
if (!includesPrefix) {
return `${pathname}`
} else {
return pathname
}
})
event.waitUntil(rawWhitelistPathnames(pathnames))
},
resetWhitelist(event) {
event.waitUntil(rawResetWhitelist())
},
}
self.addEventListener(`message`, event => {
const { gatsbyApi } = event.data
if (gatsbyApi) messageApi[gatsbyApi](event)
})
I expect the iOS PWA (safari) to show the video as it does on Android but instead it gives a grey screen.
I hope some one can help me out or point me in the right direction.
How big is your video ?
Last time I checked, iOS has a limit of 50MB for the cache of a PWA, so if your video is bigger than 50MB, that may be the reason it works only on Android (which doesn't have such restrictions).
I found this blog post that helped me fix this problem
To add Range request handling to gatsby-plugin-offline, I added a script called range-request-handler.js with the following:
// range-request-handler.js
// Define workbox globally
importScripts('https://storage.googleapis.com/workbox-cdn/releases/5.0.0/workbox-sw.js');
// Bring in workbox libs
const { registerRoute } = require('workbox-routing');
const { CacheFirst } = require('workbox-strategies');
const { RangeRequestsPlugin } = require('workbox-range-requests'); // The fix
// Add Range Request support to fetching videos from cache
registerRoute(
/(\.webm$|\.mp4$)/,
new CacheFirst({
plugins: [
new RangeRequestsPlugin(),
],
})
);
Then in my gatsby-config.js I configured the plugin to append the above script:
// gatsby-config.js
module.exports = {
// ...
plugins: [
// ...plugins
{
resolve: 'gatsby-plugin-offline',
options: {
appendScript: require.resolve('./range-request-handler.js'),
},
},
// ...plugins
],
// ...
};
Videos now work in the Safari browser for me. If there is a better way to implement this, I am all ears. For now it works as intended

Error when refreshing the page with router vue.js

when I press F5 the components disappear.only happens when I use beforeEnter: auth. if I navigate within the pages with router-link it works well
router/Auth.js
import * as firebase from 'firebase';
export default (to, from, next) => {
firebase.auth().onAuthStateChanged((user) => {
if (user) {
next();
} else {
next('/user/login');
}
});
};
router/index.js
{
path: 'edit',
name: 'edit_profile',
component: edit,
beforeEnter: auth,
}
this happens when I press F5: Nothing is shown

Vue - watch url without VueRouter

Is it possible to react on URL changes in a Vue component, without including VueRouter?
In case VueRouter is present, we can watch $route, however, my application does not rely on VueRouter.
export default {
watch: { '$route': route => console.log(window.location.href) }
}
Before I used vue router, I did something like this...
data: function() {
return {
route: window.location.hash,
page: 'home'
}
},
watch: {
route: function(){
this.page = window.location.hash.split("#/")[1];
}
}

Ionic 2/Angular- Pinging an express server to prevent using app with no connection

My app requires being connected to our server for any use. I wrote this function in the app component to prevent the user from using the app if the server is not available
app.component.ts
pingServer(){
this.api.pingServer().subscribe(result => {
if (result.success) {
return true;
}
else {
return false
}
},
error=>{
return false;
}
);
}
}
this function just makes the request to our server, which I verified is hitting the right address
initializeApp() {
console.log("initialize");
this.platform.ready().then(() => {
console.log("platform ready");
this.screenOrientation.lock('portrait');
console.log('Orientation is ' + this.screenOrientation.type);
// Okay, so the platform is ready and our plugins are available.
// Here you can do any higher level native things you might need.
let serverAlert = this.alertCtrl.create({
title: 'Error',
subTitle: 'We are unable to reach our servers at this time. Please try again later',
buttons: ['Dismiss']
});
setTimeout(() => {
if(this.pingServer){
console.log('pinged server successfully');
this.statusBar.styleDefault();
this.splashScreen.hide();
}
else{
console.log('unable to ping server');
return serverAlert.present();
}
}, 3000);
On the server side:
router.get( '/ping', ( req, res, next ) => {
return res.json( { success: true } );
} )
This worked fine when testing on the browser. However, when using iOS it says it pings successfully every time, meanwhile, the server isn't receiving the request.

Login Authentication with Devise (rails) & Angular

I currently have a mobile application where you can create issues, and view a list of issues others have created around your location.
The mobile app is is talking to a rails server and properly creating issues, and I have followed this guide covering Authentication with Rails & Devise.
In my controllers/app.js file, I have a local variable stored isLoggedIn that is set to false by default. When the application is loaded, I check to see whether the variable is true and if so, send the user to app.issues. Otherwise, the user is sent to app.auth.login.
$scope.$storage = $localStorage.$default({
isLoggedIn: false
});
$scope.$watch('$storage.isLoggedIn', function() {
if ($scope.$storage.isLoggedIn){
console.log('is logged in');
$state.go('app.issues');
}
else {
console.log('isnt logged in');
$state.go('app.auth.login');
}
});
Here is my root app.js file in my js folder.
angular.module('orangecone', ['ionic', 'orangecone.controllers', 'orangecone.services', 'ngCordova', 'ngStorage', 'ngResource'])
.run(function($ionicPlatform) {
$ionicPlatform.ready(function() {
// Hide the accessory bar by default (remove this to show the accessory bar above the keyboard
// for form inputs)
if (window.cordova && window.cordova.plugins && window.cordova.plugins.Keyboard) {
cordova.plugins.Keyboard.hideKeyboardAccessoryBar(true);
cordova.plugins.Keyboard.disableScroll(true);
}
if (window.StatusBar) {
// org.apache.cordova.statusbar required
StatusBar.styleDefault();
}
});
})
.value('Constants', {
serverUrl: 'http://localhost:3000/'
})
.config(function($stateProvider, $urlRouterProvider, $httpProvider) {
$httpProvider.defaults.withCredentials = true;
$stateProvider
// setup an abstract state for the tabs directive
.state('app', {
url: '/app',
abstract: true,
templateUrl: 'templates/tabs.html'
})
.state('app.issues', {
url: '/issues',
views: {
'app-issues': {
templateUrl: 'templates/issues.html',
controller: 'IssuesCtrl'
}
}
})
.state('app.issue', {
url: '/issues/:issueId',
views: {
'app-issues': {
templateUrl: 'templates/issue.html',
controller: 'IssueCtrl'
}
}
})
.state('app.auth', {
url: '/auth',
abstract: true,
templateUrl: 'templates/auth/tabs.html'
})
.state('app.auth.login', {
url: '/login',
views: {
'login': {
templateUrl: 'templates/auth/login.html',
controller: 'AuthCtrl'
}
}
})
.state('app.auth.register', {
url: '/register',
views: {
'register': {
templateUrl: 'templates/auth/register.html',
controller: 'AuthCtrl'
}
}
})
// if none of the above states are matched, use this as the fallback
$urlRouterProvider.otherwise('/app/auth/login');
});
I have a waning feeling that I am overlooking something rather trivial. The application always console.logs logged in even though the rails server is wiped and there are no users in the system at all. The application should be redirecting the user to app/auth/login but never does. Whenever I type this URL into the browser it also redirects me to the app/issues page.
Any thoughts or opinions would be greatly appreciated.

Resources