【问题标题】:Web app auto-redirecting to the initial page on iOS problem (it should NOT redirect)Web 应用程序自动重定向到 iOS 问题的初始页面(不应重定向)
【发布时间】:2019-10-07 17:46:36
【问题描述】:

我有一个适用于 Android 和 iOS 的网络应用程序,它可以打开一个网页。该应用程序适用于 Android 和大多数 iOS 设备。

但出于某种原因,对于某些 iOS 用户,该应用会在用户使用该应用几分钟后随机将用户重定向到初始页面。

用户在初始页面打开应用程序,点击不同页面的链接,用户正在阅读该页面,然后由于某种原因应用程序重定向回初始页面。

应用程序中没有重定向的 JavaScript 代码,有一个服务工作者,但没有重定向。

它不会一直发生,但它确实会发生并惹恼用户。

对可能发生的事情有什么想法吗?

更新:代码 sn-p

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)
    }
}
服务人员
// 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)

【问题讨论】:

  • 你需要添加一些代码和/或实现细节
  • 如果是网络应用,能否粘贴 URL 以便其他人尝试复制?
  • 可能是服务人员导致了重定向。需要有关该问题的更多信息。哪些设备引发了问题,在哪里??
  • 我有很多想法,但很少能从问题中继续缩小范围。你有 Xcode 吗?如果是这样,您可以运行 iPhone 模拟器启动 Safari 并从那里运行您的网络应用程序,看看您是否可以重新创建问题。尝试不同的模型和设置。除此之外,尝试整理错误报告,看看是否有共同的主题(相同的型号、相同的 iOS 版本、使用 4g 等)。
  • 谢谢大家。我用一些代码sn-ps更新了这个问题,你能再检查一次吗?如果需要,我很乐意分享任何其他代码。非常感谢您的支持 - 我不知道发生了什么,我自己也无法重现。

标签: ios uiwebview progressive-web-apps


【解决方案1】:

从您所解释的情况来看,如果在意外情况下调用 internetChanged 方法,页面可能会重新加载。

如果在显示 viewController 时更改了设备的网络状态,我可以看到这种情况。您在方法func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) 中观察到了一个通知。所以当网络状态改变时,internetChanged 方法将被调用。在方法的ifelse中都有加载webView的逻辑。

我也可以看到,您已观察到通知,但从未删除观察者。所以 ViewController 会有内存泄漏,可能不会从内存中释放。我建议你删除deinit中的观察者

还有一点,你可以更好地观察-viewDidLoad 中的通知,而不是didFinish 方法。现在按照当前逻辑发生的情况是,每当重新加载 webView 时,都会在方法 func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) 中将新的观察者添加到 ViewController。因此,让我们以某种方式调用 internetChanged 方法 3 次,然后添加 3 个观察者。所以下次换网时,internetChanged会被调用三次,导致webPage一个接一个地被加载3次,循环往复。

【讨论】:

  • ?谢谢!您的反馈似乎很有希望。我解决了这些更改,这是更改的 sn-p gist.github.com/phstc/d4b8a72304ab942e45bbffd453a6f00d 你可以看看吗?
  • 不知道您的业务逻辑,但我可以看到您正在尝试使用变量离线解决问题。您正在尝试根据用户是否离线的事实重新加载网页。我在答案中看到了另一个问题。
  • 如果您觉得我的回答解决了您的问题,能否将我的回答标记为已接受。这将帮助我提高我对 iOS 的 SO 评分。
【解决方案2】:

原因可能是

  • ISP(互联网服务提供商)
  • 对于 ruby​​-on-rails,进行身份验证并使用其 authentication_token 来验证用户对 API 的网络请求。否则,应用程序将终止。

希望对你有帮助。

【讨论】:

  • 嗨@Emre。感谢您的答复。它发生在不同的 iOS 版本上。我无法用我的 iPhone 重现它,也无法用 iOS 模拟器重现。但这肯定会发生在某些用户身上。我附上了一个代码 sn-p 显示我们加载了网络应用程序。我对iOS并不完全熟悉,所以如果需要任何其他代码,我很乐意分享。这不太可能是令牌问题,因为用户登录正常。
猜你喜欢
  • 2017-11-10
  • 1970-01-01
  • 2020-10-24
  • 1970-01-01
  • 1970-01-01
  • 2015-07-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多