I have a web app for Android and iOS that opens a web page. The app works fine for Android and most of the iOS devices.
But for some reason, for some iOS users, the app randomly redirects users to the initial page after a few minutes using the app.
A user opens the app on the initial page, clicks on a link for a different page, the user is reading the page, then for some reason the app redirects back to the initial page.
There's no JavaScript code that does redirect in the application, there's a service worker, but with no redirects.
It does not happen all times, but it does happen and annoys the users.
Any ideas on what could be happening?
Update: code snippet
ViewController.swift
import UIKit
import WebKit
import Firebase
import UserNotifications
class ViewController: UIViewController , WKNavigationDelegate, WKUIDelegate{
@IBOutlet weak var loading: UIActivityIndicatorView!
@IBOutlet weak var screenSplash: UIImageView!
@IBOutlet weak var webView: WKWebView!
let reachability = Reachability()!
var request = URLRequest(url: URL(string: "https://app.com/webview")!)
var screen = CGRect.zero
var flag = 0
override func viewDidLoad() {
super.viewDidLoad()
screen = UIScreen.main.bounds
webView.frame.origin.x = 0
webView.frame.origin.y = 0
webView.frame.size.height = screen.height
webView.frame.size.width = screen.width
webView?.navigationDelegate = self
webView.scrollView.bounces = false
webView.isOpaque = false
webView.backgroundColor = UIColor.clear
webView?.load(request)
self.view.addSubview(webView)
screen = UIScreen.main.bounds
webView.frame.origin.x = 0
webView.frame.origin.y = 0
webView.frame.size.height = screen.height
webView.frame.size.width = screen.width
webView.navigationDelegate = self
self.webView?.uiDelegate = self
view.addSubview(webView!)
}
@objc func internetChanged(note: Notification){
let reachability = note.object as! Reachability
if reachability.connection != .none{
print("Volvio la conexion")
webView?.load(request)
viewDidLoad()
}else{
//let alert = UIAlertController(title: "", message: "Es necesario tener una conexión activa a internet", preferredStyle: .alert)
//alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
//self.present(alert, animated: true, completion: nil)
print("Es necesario tener una conexión activa a internet")
let htmlPath = Bundle.main.path(forResource: "error", ofType: "html")
let folderPath = Bundle.main.bundlePath
let baseUrl = URL(fileURLWithPath: folderPath, isDirectory: true)
do{
let htmlString = try NSString(contentsOfFile:htmlPath!, encoding:String.Encoding.utf8.rawValue)
self.webView.loadHTMLString(htmlString as String,baseURL: baseUrl)
}catch{
}
}
}
func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!)
{
}
func webView(webView: WKWebView!, createWebViewWithConfiguration configuration: WKWebViewConfiguration!, forNavigationAction navigationAction: WKNavigationAction!, windowFeatures: WKWindowFeatures!) -> WKWebView! {
if navigationAction.targetFrame == nil {
webView.load(navigationAction.request)
}
return nil
}
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!)
{
let fmcToken=InstanceID.instanceID().token() as! String
print(fmcToken)
webView.evaluateJavaScript("(function() { if(typeof sendDeviceToken === 'function'){ return sendDeviceToken('ios','(fmcToken)'); }else{ return false; } })()", completionHandler: { (data, error) in
if let err = error {
print(err)
print(err.localizedDescription)
} else {
guard let dataValue = data else {return}
print("res sendDeviceToken")
print(dataValue)
}
})
screenSplash.isHidden = true
loading.isHidden = true
webView.evaluateJavaScript("document.getElementsByTagName('meta')['viewport'].content='initial-scale=1.0, user-scalable=no';"){
(result,error) in if error != nil {
print(result ?? "")
}
}
if(flag==0){
NotificationCenter.default.addObserver(self, selector: #selector(internetChanged), name: Notification.Name.reachabilityChanged, object: reachability)
do{
try reachability.startNotifier()
}catch{
print("No se pudo iniciar la notificacion")
}
flag=flag+1
}
}
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
if navigationAction.navigationType == .linkActivated {
if let url = navigationAction.request.url,
let host = url.host, !host.hasPrefix("app.com") || url.absoluteString.contains("/share/"),
UIApplication.shared.canOpenURL(url) {
UIApplication.shared.open(url)
print(url)
print("Redirected to browser. No need to open it locally")
decisionHandler(.cancel)
} else {
// print("Open it locally")
decisionHandler(.allow)
}
} else {
// print("not a user click")
decisionHandler(.allow)
}
}
//fix the alert
func webView(_ webView: WKWebView, runJavaScriptAlertPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping () -> Void) {
let alertController = UIAlertController(title: nil, message: message, preferredStyle: .actionSheet)
alertController.addAction(UIAlertAction(title: "OK", style: .default, handler: { (action) in
completionHandler()
}))
self.present(alertController, animated: true, completion: nil)
}
func webView(_ webView: WKWebView, runJavaScriptConfirmPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping (Bool) -> Void) {
let alertController = UIAlertController(title: nil, message: message, preferredStyle: .actionSheet)
alertController.addAction(UIAlertAction(title: "OK", style: .default, handler: { (action) in
completionHandler(true)
}))
alertController.addAction(UIAlertAction(title: "Cancel", style: .default, handler: { (action) in
completionHandler(false)
}))
self.present(alertController, animated: true, completion: nil)
}
func webView(_ webView: WKWebView, runJavaScriptTextInputPanelWithPrompt prompt: String, defaultText: String?, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping (String?) -> Void) {
let alertController = UIAlertController(title: nil, message: prompt, preferredStyle: .actionSheet)
alertController.addTextField { (textField) in
textField.text = defaultText
}
alertController.addAction(UIAlertAction(title: "OK", style: .default, handler: { (action) in
if let text = alertController.textFields?.first?.text {
completionHandler(text)
} else {
completionHandler(defaultText)
}
}))
alertController.addAction(UIAlertAction(title: "Cancel", style: .default, handler: { (action) in
completionHandler(nil)
}))
self.present(alertController, animated: true, completion: nil)
}
//end fix the alert
func clickActionOpen(action: String) {
request = URLRequest(url: URL(string: "https://app.com/webview?click_action="+action)!)
var requestClick = URLRequest(url: URL(string: "https://app.com/webview?click_action="+action)!)
webView.load(requestClick)
}
}
Service worker
// Incrementing CACHE_VERSION will kick off the install event and force previously cached
// resources to be cached again.
// https://github.com/GoogleChrome/samples/blob/gh-pages/service-worker/custom-offline-page/service-worker.js
var CACHE_VERSION = 'v4'
var CACHE_NAME = CACHE_VERSION + ':sw-cache-'
function onInstall(event) {
console.log('[Serviceworker]', 'Installing!', event)
event.waitUntil(
caches.open(CACHE_NAME).then(function prefill(cache) {
return cache.addAll([
'<%= asset_path "admin.js" %>',
'<%= asset_path "admin.css" %>',
'<%= asset_path "site/homepage-logo.png" %>'
])
})
)
}
function onActivate(event) {
console.log('[Serviceworker]', 'Activating!', event)
event.waitUntil(
caches.keys().then(function(cacheNames) {
return Promise.all(
cacheNames
.filter(function(cacheName) {
// Return true if you want to remove this cache,
// but remember that caches are shared across
// the whole origin
return cacheName.indexOf(CACHE_VERSION) !== 0
})
.map(function(cacheName) {
return caches.delete(cacheName)
})
)
})
)
}
// Borrowed from https://github.com/TalAter/UpUp
// then from https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerGlobalScope/onfetch
function onFetch(event) {
event.respondWith(
caches.match(event.request).then(function(response) {
if (response) {
// console.log('Found response in cache:', response)
return response
}
// console.log('No response found in cache. About to fetch from network...')
return fetch(event.request).then(function(response) {
// console.log('Response from network is:', response)
return response
}).catch(function(error) {
// console.error('Fetching failed:', error)
throw error
})
})
)
}
self.addEventListener('install', onInstall)
self.addEventListener('activate', onActivate)
self.addEventListener('fetch', onFetch)
See Question&Answers more detail:
os