如何在iPhone上从网页检查应用程序是否已安装

183

我想创建一个网页,如果iPhone没有安装应用程序,它会将其重定向到App Store,但如果iPhone已经安装了该应用程序,我希望它能够打开该应用程序。

我已经为iPhone应用程序实现了自定义URL,因此我有一个应用程序的URL,类似于:

myapp://

如果此URL无效,我希望页面重定向到App Store。这真的可能吗?

如果我在Safari中没有安装该应用程序并编写myapp://URL,则只会收到错误消息。

即使存在使用JavaScript的丑陋hack,我也想知道。


1
这在每个iOS版本中都在不断变化 - iOS9又把一切都搞砸了。我建议使用像https://branch.io这样的服务来为您处理此问题。我曾经帮助构建Branch链接服务的部分,它目前处理超过6000种不同的重定向边缘情况...太疯狂了。 - Alex Austin
1
在2017年,如果你需要从电子邮件中链接到你的应用程序,你应该看一下我的回答:https://dev59.com/R2cs5IYBdhLWcg3wOBNC#44671031 - Sebastien Lorber
12个回答

249
据我所知,无法通过浏览器检查应用程序是否已安装。但是您可以尝试将手机重定向到该应用程序,如果没有任何反应,则将手机重定向到指定页面,例如:

setTimeout(function () { window.location = "https://itunes.apple.com/appdir"; }, 25);
window.location = "appname://";

如果第二行代码有结果,则第一行永远不会执行。
类似问题:
- iPhone浏览器:如何检查是否从浏览器安装了iPhone应用程序 - 是否可能为iPhone应用程序注册基于http+域的URL方案,例如YouTube和Maps?

24
目前在iOS 6.1.2版本中,此功能似乎只在Web应用程序模式下起作用。如果直接从浏览器中使用该应用程序,则会按预期打开外部应用程序,但计时器也会触发并重定向浏览器。 - Mika Tuupola
5
如果应用程序未安装,它会抛出一个难看的“页面未找到”警报。不过我们可以抑制它。 - Akshat Agarwal
3
@AkshatAgarwal,请您详细说明如何编写代码来抑制“页面未找到警报”。 - Harpreet
3
现在在Android和iOS中实现这一点的新方法。请查看Android应用链接iOS通用链接 - maledr53
2
打开应用程序,但同时也将浏览器重定向到应用商店/Play商店。有什么方法/技巧可以防止这种情况发生吗? - Sujeet Kumar
显示剩余8条评论

182

进一步完善接受的答案,有时需要添加额外的代码来处理用户在启动应用程序后返回浏览器的情况 - 这样,无论何时他们返回,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://";
那样,如果代码执行已经冻结(例如应用切换),它将不会运行。

2
这个解决方案比其他的好多了,我必须将时间增加50。 - Magico
1
我的关于它在 iOS 7 上失败的评论是错误的:我只需要将100毫秒增加到几秒钟(实际上也没有必要那么短)即可。 - devios1
我写了一个可以在 Twitter 上实现这个功能的东西:https://github.com/omarish/tweetable,而且这个原理也适用于其他深度链接(Facebook、Pinterest 等)。祝使用愉快! - omarish
我需要在我的应用程序中添加任何代码吗?因为这段代码可以打开iOS中的许多应用程序,但不能打开我的应用程序。 - Reyraa
17
这会同时打开我的应用和应用商店,即使在50上也是一样。iOS 12 - Joshua Wolff
显示剩余5条评论

31
iOS Safari有一个功能,可以在您的网页上添加“智能”横幅,链接到您的应用程序(如果已安装),或链接到App Store。要实现这一点,只需在页面中添加一个标签即可。您甚至可以指定详细的应用程序URL,以便在加载时执行特殊操作。详情请参见苹果公司的Promoting Apps with Smart App Banners页面。该机制的优点是易于使用并呈现标准化的横幅。缺点是您对外观或位置没有太多控制权。另外,如果在Safari之外的浏览器中查看页面,则不能保证其有效性。

4
如果用户没有安装应用程序,它就不会显示应用横幅。 - TomSawyer
既不能使用通用链接,也不能使用智能横幅。我需要有多个本地应用程序链接到不同的应用程序。每个链接都要检查是否已安装,如果已安装,则启动它,否则转到商店。我也无法控制域名以上传通用链接的所有权文件。因此,这两个选项对我来说都不可行。需要一个纯JavaScript解决方案。 - FranticRock

24
截至2017年,似乎没有可靠的方法来检测应用程序是否已安装,并且重定向技巧不会在所有地方起作用。
对于像我这样需要直接从电子邮件中进行深度链接(非常普遍)的人,值得注意以下内容:
- 发送带有appScheme://的电子邮件将无法正常工作,因为Gmail中的链接将被过滤。 - 自动重定向到appScheme://被Chrome阻止:我怀疑Chrome要求重定向是同步的用户交互(如单击)。 - 现在可以不使用appScheme://进行深度链接,这更好,但它需要一个现代平台和额外的设置。 Android iOS
值得注意的是,其他人已经深入思考过这个问题。如果您查看Slack如何实现其“魔术链接”功能,您会发现:
  • 它发送一封带有常规HTTP链接的电子邮件(在Gmail中可用)
  • 网页上有一个大按钮,链接到appScheme://(在Chrome中可用)

11

@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>

8

我添加了这个,但是即使我安装了应用程序,它仍然重定向到Appstore。 - Qasim

2

以下答案仍然适用,已在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>

2
编译了几个答案后,我得出了以下代码。让我惊讶的是,在PC上(Chrome和Firefox)或Android Chrome上,计时器并没有被冻结 - 触发器在后台工作,而可见性检查是唯一可靠的信息。
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://');

JS Fiddle


2

我需要做类似这样的事情,最终我选择了以下解决方案。

我有一个特定的网站URL,将打开一个页面,其中包含两个按钮。

  1. Button one go to the website

  2. 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)

  3. 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">&nbsp;</td>
                 <td>
                     <!-- Intro -->
                     <span class="iphone_copy_intro">Check out our new app or go to website</span>
                 </td>
                 <td class="iphone_table_leftRight">&nbsp;</td>
             </tr>
             <tr>
                 <td class="iphone_table_leftRight">&nbsp;</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">&nbsp;</td>
                                 <td class="iphone_btn_on_mid">
                                     <span class="iphone_copy_btn">
                                         Get The Mobile Applications
                                     </span>
                                 </td>
                                 <td class="iphone_btn_on_right">&nbsp;</td>
                             </tr>
                         </table>
    
                     </div>
                 </td>
                 <td class="iphone_table_leftRight">&nbsp;</td>
             </tr>
             <tr>
                 <td class="iphone_table_leftRight">&nbsp;</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">&nbsp;</td>
                                 <td class="iphone_btn_mid">
                                     <span class="iphone_copy_btn">
                                         Visit Website.com
                                     </span>
                                 </td>
                                 <td class="iphone_btn_right">&nbsp;</td>
                             </tr>
                         </table>
    
                     </div>
                 </td>
                 <td class="iphone_table_leftRight">&nbsp;</td>
             </tr>
             <tr>
                 <td class="iphone_table_leftRight">&nbsp;</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">&nbsp;Don&rsquo;t show this screen again.</label>
                                     </span>
                                 </td>
                             </tr>
                         </table>
    
                     </div>
                 </td>
                 <td class="iphone_table_leftRight">&nbsp;</td>
             </tr>
         </table>
    
         </div>
    
     </body>
    
     </html>
    

我需要这种类型的代码来满足我的需求,但无法放置我的链接。需求:如果已安装app1,则转到Link1,否则转到Link2。有人可以帮忙吗? - SFDC_Learner
当应用程序安装时,需要取消 cookie,以防用户首选 web。 - Charlie Reitzel

1

我一直在尝试在iOS15的Safari扩展中实现相同的功能。似乎所有以前的策略都失败了 - “打开”对话框和“无效地址”对话框完全相同,都是非阻塞的,因此基于计时器的解决方案提供的结果不一致,取决于加载页面所需的时间。

我的解决方法是在模态弹出窗口中创建一个应用商店重定向消息,模拟系统提示的外观,将其隐藏在系统提示后面,并在选项卡失去焦点时使用事件侦听器将其关闭。用户体验还有两个问题:

  1. 没有办法抑制“无效地址”提示。我们能做的只有(如果我们不走通用链接的路线)是用自己的提示来解释它。
  2. 如果用户从“打开”提示中选择“取消”,仍然会出现我们的重定向提示。

以下代码既受到上面答案的帮助,也受到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>


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接