Mobile Safari 中的 React PWA Image Upload 会破坏应用程序吗?

React PWA Image Upload in Mobile Safari breaks application?

我们很惊讶我们没有在网上的任何地方找到任何关于此的提及,所以我们在这里发帖希望我们找到解决方案。

将 iPhone 与移动 safari 结合使用是我们遇到此问题的时候 运行在下面的 2 个简单易懂的测试中,一个有效,一个无效。

这是link https://pwa-react.netlify.com/

这是我们 运行 的 2 个测试(均在 link 中列出),一个在非 PWA 模式下有效,另一个在 PWA 模式下失败。

测试 #1:完美运行(预期行为)

Visit https://pwa-react.netlify.com/ from iPhone in mobile safari
1. Make sure you have google drive on the phone but not logged in.
2. Click "Choose File". It will show you the list of options to choose from.
3. Click "Browse" to look for the photo.
4. Click "Cancel" and you're back here.
5. Click "Choose File" it will still show you the list of options to choose from.
   This works perfectly in mobile safari but NOT in PWA mode below.

测试 #2:不起作用(意外行为)(PWA)

Visit https://pwa-react.netlify.com/ from iPhone in mobile safari, hit the share
button, then add to home screen. This will add the PWA app on your phone. Open App.
1. Make sure you have google drive on the phone but not logged in.
2. Click "Choose File". It will show you the list of options to choose from.
3. Click "Browse" to look for the photo.
4. When it shows you the Google Drive logo with Sign In, double click the home 
   button, then go back to the PWA.
5. Click "Choose File" it will NOT show you the list of options to choose from. 
   This is now 100% broken.
   The ONLY way to fix it is to go to Settings>Safari>Clear History and Website Data (all the way down)
   How can we fix this so when the user hits "Choose File" it shows the list of 
   options to choose from in the PWA?

Screenshot #1: 这些选项出现在 Test #1 中并停止出现在 Test # 2

屏幕截图 #2: 此屏幕允许我们在 测试 #1 中取消,但它在 测试 # 中消失2

知道如何让测试 #2 正常工作,方法是允许我们选择 Screenshot #1 中的上传选项而不中断应用程序并必须转到 Safari 设置以清除历史记录和网站数据才能再次运行?

PS - 这是存储库文件 pwa-react/src/App.js

我们在 PWA 中遇到了几乎完全相同的问题,所以首先,我要感谢您帮助我们缩小问题范围。

在回顾 iOS PWA 生命周期 (article here) 和几个令人发狂的试错小时后,我找到了一个半可接受的解决方案。

我猜测当您在上传过程中离开应用程序时会发生什么(测试#2)是 iOS 如何处理未重置的 PWA 有一些内部上下文,所以当您离开时返回并尝试再次上传文件它认为上传对话框已经打开。

文章提到在没有target=_blank的情况下打开外部links会导致PWA上下文被删除,所以当应用内浏览器关闭时,PWA页面会在standalone window。我认为这可能会重置 "detached upload" 上下文,它最终起作用了。

所以我创建了一个托管在另一个域上的页面,并link在 PWA 中的上传按钮下方将其添加到该页面:

// not sure the target={'_self'} is necessary but not risking it
<a href={'https://externalDomain.com/reset'} target={'_self'}>
    Having Issues? Reset Upload
</a>

这工作得很好,少了一个问题。当您单击此 link 时,它会打开应用内浏览器,但没有 "Done" 按钮或导航工具供用户了解如何退出。链接回 PWA 不起作用,因为 iOS 检测到并没有重置应用程序上下文。我注意到的是,如果我从第一个外部页面导航到另一个页面(我最初只是用 google.com 测试过),"Done" 按钮会出现,让如何退出变得显而易见。

根据这些知识,我猜想您可能只需执行 window.history.pushState 即可达到相同的效果,这很有效。我的最终解决方案如下。当用户从应用程序内浏览器中按下“完成”时,它会导致整个应用程序重新加载,但在我看来,这比让它们重新添加到主屏幕要好得多。

const Reset: React.FC = props => {
    React.useEffect(() => {
        // Redirect any wayward users who find this page from outside the PWA
        if (!window.matchMedia('(display-mode: standalone)').matches) {
            navigate('/');
        }
        // push an additional page into history
        const newUrl = `${window.location.href}?reset`;
        window.history.pushState({ path: newUrl }, '', newUrl);
    }, []);
    return (
        <Grid container>
            <ArrowUpIcon />
            <Typography variant={'h5'}>Press done above to return to App</Typography>
            <Typography variant={'body1'}>Sorry for the inconvenience!</Typography>
        </Grid>
    );
};

希望对您有所帮助!很想听听它是否适合您。

生产测试后编辑: 另一个重要注意事项是,您的 "reset" 页面必须位于完全不同的域中才能正常工作。 运行 今天在生产中加入这个,因为我们的重置页面位于与 PWA 具有相同根的子域上,iOS 没有重置整个 PWA 生命周期。

摘要

关键问题:

  1. 在打开任何 "file upload" 对话框('Take Photo'、'Photo Library' 或 'Browse')时离开 iOS PWA 会破坏 iOS PWA lifecycle.This 损坏导致用户无法在单击文件输入时打开任何 "file upload" 对话框。
  2. 为了解决这个问题,必须完全重置 PWA 上下文。
  3. 似乎重置 PWA 上下文的唯一方法是重新启动 phone、删除应用程序并将其重新添加到主屏幕,或者打开外部 link。
  4. 打开外部 link 时,关闭 iOS PWA 嵌入式浏览器的 "Done" 按钮将不会显示在初始页面上。用户必须导航到其他外部页面才能显示 "Done" 按钮。
  5. 外部 link 在 target="_blank".
  6. 时不会触发 PWA 上下文重置

解决方案:

为了让用户能够再次上传文件,必须重置 PWA 上下文。最简单的方法(在我看来)是让他们打开一个外部 link.

  1. (在 PWA 中):向用户显示 link 以修复上传对话框未显示的事实。 link 目的地必须是完全不相关的域(不是子域)并且必须具有 target="_self"(问题 #5)。
  2. (外部页面):一旦用户点击 link 并且外部页面打开,将没有可见的离开页面的方式(问题 #4)。要解决此问题,您可以使用 history.pushState 模拟导航到其他页面。
  3. (外部页面 - 奖励):为了让用户清楚地知道问题已经解决,在左上角添加一个指向 "Done" 按钮的箭头(如我的屏幕截图所示)。