我想创建一个网页,如果iPhone没有安装应用程序,它会将其重定向到App Store,但如果iPhone已经安装了该应用程序,我希望它能够打开该应用程序。
我已经为iPhone应用程序实现了自定义URL,因此我有一个应用程序的URL,类似于:
myapp://
如果此URL无效,我希望页面重定向到App Store。这真的可能吗?
如果我在Safari中没有安装该应用程序并编写myapp://URL,则只会收到错误消息。
即使存在使用JavaScript的丑陋hack,我也想知道。
我想创建一个网页,如果iPhone没有安装应用程序,它会将其重定向到App Store,但如果iPhone已经安装了该应用程序,我希望它能够打开该应用程序。
我已经为iPhone应用程序实现了自定义URL,因此我有一个应用程序的URL,类似于:
myapp://
如果此URL无效,我希望页面重定向到App Store。这真的可能吗?
如果我在Safari中没有安装该应用程序并编写myapp://URL,则只会收到错误消息。
即使存在使用JavaScript的丑陋hack,我也想知道。
setTimeout(function () { window.location = "https://itunes.apple.com/appdir"; }, 25);
window.location = "appname://";
进一步完善接受的答案,有时需要添加额外的代码来处理用户在启动应用程序后返回浏览器的情况 - 这样,无论何时他们返回,setTimeout函数都会运行。 所以,我会做这样的事情:
var now = new Date().valueOf();
setTimeout(function () {
if (new Date().valueOf() - now > 100) return;
window.location = "https://itunes.apple.com/appdir";
}, 25);
window.location = "appname://";
那样,如果代码执行已经冻结(例如应用切换),它将不会运行。@Alistair在这个回答中指出,有时用户会在打开应用程序后返回到浏览器。该回答的评论者指出,使用的时间值必须根据iOS版本进行更改。
当我们的团队不得不处理这个问题时,我们发现初始超时和判断是否已返回到浏览器的时间值必须进行调整,并且通常无法为所有用户和设备工作。
与其使用任意时间差阈值来确定是否已返回到浏览器,不如检测“pagehide”和“pageshow”事件。
我开发了以下网页来帮助诊断正在发生的事情。它随着事件的展开添加HTML诊断,主要是因为在这个工作流程中使用类似控制台日志记录、警报或Web Inspector、jsfiddle.net等技术都有缺点。 JavaScript通过计算“pagehide”和“pageshow”事件的数量来判断它们是否已经发生,而不是使用时间阈值。 我发现最强大的策略是使用1000的初始超时(而不是其他人报告和建议的25、50或100)。
这可以在本地服务器上提供,例如python -m SimpleHTTPServer
,并在iOS Safari上查看。
要使用它,请按“打开已安装的应用程序”或“未安装的应用程序”链接。 这些链接应分别导致地图应用程序或App Store打开。 然后,您可以返回Safari以查看事件的顺序和时间。
(注意:这仅适用于Safari。 对于其他浏览器(如Chrome),您需要安装处理程序以处理pagehide / show等价事件)。
更新:正如@Mikko在评论中指出的那样,我们正在使用的pageshow / pagehide事件显然不再受iOS8支持。
<html>
<head>
</head>
<body>
<a href="maps://" onclick="clickHandler()">Open an installed app</a>
<br/><br/>
<a href="xmapsx://" onclick="clickHandler()">App not installed</a>
<br/>
<script>
var hideShowCount = 0 ;
window.addEventListener("pagehide", function() {
hideShowCount++;
showEventTime('pagehide');
});
window.addEventListener("pageshow", function() {
hideShowCount++;
showEventTime('pageshow');
});
function clickHandler(){
var hideShowCountAtClick = hideShowCount;
showEventTime('click');
setTimeout(function () {
showEventTime('timeout function ' + (hideShowCount-hideShowCountAtClick) + ' hide/show events');
if (hideShowCount == hideShowCountAtClick){
// app is not installed, go to App Store
window.location = 'http://itunes.apple.com/app';
}
}, 1000);
}
function currentTime()
{
return Date.now()/1000;
}
function showEventTime(event){
var time = currentTime() ;
document.body.appendChild(document.createElement('br'));
document.body.appendChild(document.createTextNode(time + ' ' + event));
}
</script>
</body>
</html>
以下答案仍然适用,已在iOS 10至14上测试。它基于先前的答案。我添加了window.close()
以消除在重定向或页面返回后留下的空白标签窗口。它修复了4种情况中的两种,也许其他人可以修复第三和第四种情况。
<script>
var now = new Date().valueOf();
setTimeout(function () {
// time stamp comaprison prevents redirecting to app store a 2nd time
if (new Date().valueOf() - now > 100) {
window.close() ; // scenario #4
// old way - "return" - but this would just leave a blank page in users browser
//return;
}
if (isIOS == 1) {
// still can't avoid the "invalid address" safari pops up
// but at least we can explain it to users
var msg = "'invalid address' = MyApp NOT DETECTED.\n\nREDIRECTING TO APP STORE" ;
} else {
var msg = "MyApp NOT DETECTED\n\nREDIRECTING TO APP STORE" ;
}
if (window.confirm(msg)) {
window.location = "<?=$storeUrl?>";
// scenario #2 - will leave a blank tab in browser
} else {
window.close() ; // scenario #3
}
}, 50);
window.location = "<?=$mobileUrl?>";
// scenario #1 - this will leave a blank tab
</script>
var timestamp = new Date().getTime();
var timerDelay = 5000;
var processingBuffer = 2000;
var redirect = function(url) {
//window.location = url;
log('ts: ' + timestamp + '; redirecting to: ' + url);
}
var isPageHidden = function() {
var browserSpecificProps = {hidden:1, mozHidden:1, msHidden:1, webkitHidden:1};
for (var p in browserSpecificProps) {
if(typeof document[p] !== "undefined"){
return document[p];
}
}
return false; // Actually inconclusive, assuming not
}
var elapsedMoreTimeThanTimerSet = function(){
var elapsed = new Date().getTime() - timestamp;
log('elapsed: ' + elapsed);
return timerDelay + processingBuffer < elapsed;
}
var redirectToFallbackIfBrowserStillActive = function() {
var elapsedMore = elapsedMoreTimeThanTimerSet();
log('hidden:' + isPageHidden() + '; time: ' + elapsedMore);
if (isPageHidden() || elapsedMore) {
log('not redirecting');
}else{
redirect('appStoreUrl');
}
}
var log = function(msg){
document.getElementById('log').innerHTML += msg + "<br>";
}
setTimeout(redirectToFallbackIfBrowserStillActive, timerDelay);
redirect('nativeApp://');
我需要做类似这样的事情,最终我选择了以下解决方案。
我有一个特定的网站URL,将打开一个页面,其中包含两个按钮。
Button one go to the website
Button two go to the application (iPhone / Android phone / tablet). You can fall back to a default location from here if the app is not installed (like another URL or an app store)
Cookie to remember the user's choice
<head>
<title>Mobile Router Example </title>
<script type="text/javascript">
function set_cookie(name,value)
{
// JavaScript code to write a cookie
}
function read_cookie(name) {
// JavaScript code to read a cookie
}
function goToApp(appLocation) {
setTimeout(function() {
window.location = appLocation;
// This is a fallback if the app is not installed.
// It could direct to an app store or a website
// telling user how to get the app
}, 25);
window.location = "custom-uri://AppShouldListenForThis";
}
function goToWeb(webLocation) {
window.location = webLocation;
}
if (readCookie('appLinkIgnoreWeb') == 'true' ) {
goToWeb('http://somewebsite');
}
else if (readCookie('appLinkIgnoreApp') == 'true') {
goToApp('http://fallbackLocation');
}
</script>
</head>
<body>
<div class="iphone_table_padding">
<table border="0" cellspacing="0" cellpadding="0" style="width:100%;">
<tr>
<td class="iphone_table_leftRight"> </td>
<td>
<!-- Intro -->
<span class="iphone_copy_intro">Check out our new app or go to website</span>
</td>
<td class="iphone_table_leftRight"> </td>
</tr>
<tr>
<td class="iphone_table_leftRight"> </td>
<td>
<div class="iphone_btn_padding">
<!-- Get iPhone app button -->
<table border="0" cellspacing="0" cellpadding="0" class="iphone_btn" onclick="set_cookie('appLinkIgnoreApp',document.getElementById('chkDontShow').checked);goToApp('http://getappfallback')">
<tr>
<td class="iphone_btn_on_left"> </td>
<td class="iphone_btn_on_mid">
<span class="iphone_copy_btn">
Get The Mobile Applications
</span>
</td>
<td class="iphone_btn_on_right"> </td>
</tr>
</table>
</div>
</td>
<td class="iphone_table_leftRight"> </td>
</tr>
<tr>
<td class="iphone_table_leftRight"> </td>
<td>
<div class="iphone_btn_padding">
<table border="0" cellspacing="0" cellpadding="0" class="iphone_btn" onclick="set_cookie('appLinkIgnoreWeb',document.getElementById('chkDontShow').checked);goToWeb('http://www.website.com')">
<tr>
<td class="iphone_btn_left"> </td>
<td class="iphone_btn_mid">
<span class="iphone_copy_btn">
Visit Website.com
</span>
</td>
<td class="iphone_btn_right"> </td>
</tr>
</table>
</div>
</td>
<td class="iphone_table_leftRight"> </td>
</tr>
<tr>
<td class="iphone_table_leftRight"> </td>
<td>
<div class="iphone_chk_padding">
<!-- Check box -->
<table border="0" cellspacing="0" cellpadding="0">
<tr>
<td><input type="checkbox" id="chkDontShow" /></td>
<td>
<span class="iphone_copy_chk">
<label for="chkDontShow"> Don’t show this screen again.</label>
</span>
</td>
</tr>
</table>
</div>
</td>
<td class="iphone_table_leftRight"> </td>
</tr>
</table>
</div>
</body>
</html>
我一直在尝试在iOS15的Safari扩展中实现相同的功能。似乎所有以前的策略都失败了 - “打开”对话框和“无效地址”对话框完全相同,都是非阻塞的,因此基于计时器的解决方案提供的结果不一致,取决于加载页面所需的时间。
我的解决方法是在模态弹出窗口中创建一个应用商店重定向消息,模拟系统提示的外观,将其隐藏在系统提示后面,并在选项卡失去焦点时使用事件侦听器将其关闭。用户体验还有两个问题:
以下代码既受到上面答案的帮助,也受到this SO代码创建模态弹出窗口的帮助。
// Change the following vars to suit your needs
var my_app_name = "My App";
var my_app_id = "id1438151717"
var my_app_scheme = "myapp://do.this"
function toggleModal(isModal, inputs, elems, msg) {
for (const input of inputs) input.disabled = isModal;
modal.style.display = isModal ? "block" : "none";
elems[0].textContent = isModal ? msg : "";
}
function myConfirm(msg) {
const inputs = [...document.querySelectorAll("input, textarea, select")].filter(input => !input.disabled);
const modal = document.getElementById("modal");
const elems = modal.children[0].children;
return new Promise((resolve) => {
toggleModal(true, inputs, elems, msg);
elems[3].onclick = () => resolve(true);
elems[4].onclick = () => resolve(false);
}).then(result => {
toggleModal(false, inputs, elems, msg);
return result;
});
}
function redirectMessage() {
var r = myConfirm("To download " + my_app_name + ", tap OK.");
return r.then(ok => {
if (ok) {
console.log("Redirecting to the App Store...");
window.location = "itms-apps://itunes.apple.com/app/" + my_app_id;
} else {
console.log("User cancelled redirect to the App Store");
}
return ok;
});
}
function prepareListener() {
document.addEventListener("visibilitychange", function() {
const inputs = [...document.querySelectorAll("input, textarea, select")].filter(input => !input.disabled);
const modal = document.getElementById("modal");
const elems = modal.children[0].children;
if (!document.hasFocus()) {
console.log("User left tab. Closing modal popup")
toggleModal(false, inputs, elems, "");
}
});
}
function onTap() {
setTimeout(function() {
// We can't avoid the "invalid address" Safari popup,
// but at least we can explain it to users.
// We will create a modal popup behind it, which the
// event listener will close automatically if the app
// opens and we leave the tab
redirectMessage()
}, 50);
window.location = my_app_scheme;
}
prepareListener()
#modal {
display: none;
position: fixed;
z-index: 1;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: auto;
background: rgb(0, 0, 0);
background: rgba(0, 0, 0, 0.4);
font-family: "ms sans serif", arial, sans-serif;
font-size: medium;
border-radius: 15px;
}
#modal>div {
position: relative;
padding: 10px;
width: 320px;
height: 60px;
margin: 0 auto;
top: 50%;
margin-top: -45px;
background: white;
border: 2px outset;
border-radius: 15px;
}
#cancel_button {
position: fixed;
right: 50%;
margin-right: -95px;
bottom: 50%;
margin-bottom: -32px;
padding: 0;
border: none;
background: none;
color: rgb(0, 122, 255);
font-size: medium;
font-weight: normal;
}
#ok_button {
position: fixed;
right: 50%;
margin-right: -140px;
bottom: 50%;
margin-bottom: -32px;
padding: 0;
border: none;
background: none;
color: rgb(0, 122, 255);
font-size: medium;
font-weight: semi-bold;
}
<div id="modal">
<div>
<div></div><br><br>
<button id="ok_button">OK</button>
<button id="cancel_button">Cancel</button>
</div>
</div>
<p><a href="#" onclick="onTap();"> Tap here to open app </a></p>