如何检测当前正在运行的应用程序是否已从应用商店安装?

kev*_*lar 26 app-store ios

在iOS中是否有办法以编程方式检查当前正在运行的应用程序是否已从iOS App Store安装?这与通过Xcode,TestFlight或任何非官方分发源运行的应用程序形成对比.

这是在SDK无法访问应用程序源代码的情况下.

要清楚 - 我正在寻找一些签名,可以这么说,给应用程序(可能是Apple),在不依赖任何预处理程序标志或其他构建配置的情况下,可以在运行时访问任何应用程序.

mag*_*gma 27

从App Store下载的应用程序有一个iTunesMetadata.plist由商店添加的文件:

NSString *file=[NSHomeDirectory() stringByAppendingPathComponent:@"iTunesMetadata.plist"];
if ([[NSFileManager defaultManager] fileExistsAtPath:file]) {
    // probably a store app
}
Run Code Online (Sandbox Code Playgroud)

也许您可能想要检查此文件是否存在.

更新:

在iOS8中,应用程序包已被移动.根据@silyevsk的说法,plist现在比[private application var>主路径]高一级,位于/ private/var/mobile/Containers/Bundle/Application/4A74359F-E6CD-44C9-925D-AC82E B5EA837/iTunesMetadata. plist,不幸的是,这不能从应用程序访问(权限被拒绝)

2015年11月4日更新:

看来检查收据名称可以提供帮助.必须注意的是,这个解决方案略有不同:它不会返回我们是否正在运行App Store应用程序,而是我们是否正在运行beta Testflight应用程序.根据您的具体情况,这可能有用也可能没用.

最重要的是,它是一个非常脆弱的解决方案,因为收据名称可能随时更改.无论如何我都会报告,如果您没有其他选择:

// Objective-C
BOOL isRunningTestFlightBeta = [[[[NSBundle mainBundle] appStoreReceiptURL] lastPathComponent] isEqualToString:@"sandboxReceipt"];

// Swift
let isRunningTestFlightBeta = NSBundle.mainBundle().appStoreReceiptURL?.lastPathComponent=="sandboxReceipt"
Run Code Online (Sandbox Code Playgroud)

来源:检测iOS App是否从Apple的Testflight下载

HockeyKit是如何做到的

通过组合各种检查,您可以猜测应用程序是在Simulator,Testflight构建还是在AppStore构建中运行.

这是HockeyKit的一个片段:

BOOL bit_isAppStoreReceiptSandbox(void) {
#if TARGET_OS_SIMULATOR
  return NO;
#else
  NSURL *appStoreReceiptURL = NSBundle.mainBundle.appStoreReceiptURL;
  NSString *appStoreReceiptLastComponent = appStoreReceiptURL.lastPathComponent;

  BOOL isSandboxReceipt = [appStoreReceiptLastComponent isEqualToString:@"sandboxReceipt"];
  return isSandboxReceipt;
#endif
}

BOOL bit_hasEmbeddedMobileProvision(void) {
  BOOL hasEmbeddedMobileProvision = !![[NSBundle mainBundle] pathForResource:@"embedded" ofType:@"mobileprovision"];
  return hasEmbeddedMobileProvision;
}

BOOL bit_isRunningInTestFlightEnvironment(void) {
#if TARGET_OS_SIMULATOR
  return NO;
#else
  if (bit_isAppStoreReceiptSandbox() && !bit_hasEmbeddedMobileProvision()) {
    return YES;
  }
  return NO;
#endif
}

BOOL bit_isRunningInAppStoreEnvironment(void) {
#if TARGET_OS_SIMULATOR
  return NO;
#else
  if (bit_isAppStoreReceiptSandbox() || bit_hasEmbeddedMobileProvision()) {
    return NO;
  }
  return YES;
#endif
}

BOOL bit_isRunningInAppExtension(void) {
  static BOOL isRunningInAppExtension = NO;
  static dispatch_once_t checkAppExtension;

  dispatch_once(&checkAppExtension, ^{
    isRunningInAppExtension = ([[[NSBundle mainBundle] executablePath] rangeOfString:@".appex/"].location != NSNotFound);
  });

  return isRunningInAppExtension;
}
Run Code Online (Sandbox Code Playgroud)

资料来源:GitHub - bitstadium/HockeySDK-iOS - BITHockeyHelper.m

一个可能的Swift类,基于HockeyKit的类,可能是:

//
//  WhereAmIRunning.swift
//  https://gist.github.com/mvarie/63455babc2d0480858da
//
//  ### Detects whether we're running in a Simulator, TestFlight Beta or App Store build ###
//
//  Based on https://github.com/bitstadium/HockeySDK-iOS/blob/develop/Classes/BITHockeyHelper.m
//  Inspired by https://stackoverflow.com/questions/18282326/how-can-i-detect-if-the-currently-running-app-was-installed-from-the-app-store
//  Created by marcantonio on 04/11/15.
//

import Foundation

class WhereAmIRunning {

    // MARK: Public

    func isRunningInTestFlightEnvironment() -> Bool{
        if isSimulator() {
            return false
        } else {
            if isAppStoreReceiptSandbox() && !hasEmbeddedMobileProvision() {
                return true
            } else {
                return false
            }
        }
    }

    func isRunningInAppStoreEnvironment() -> Bool {
        if isSimulator(){
            return false
        } else {
            if isAppStoreReceiptSandbox() || hasEmbeddedMobileProvision() {
                return false
            } else {
                return true
            }
        }
    }

    // MARK: Private

    private func hasEmbeddedMobileProvision() -> Bool{
        if let _ = NSBundle.mainBundle().pathForResource("embedded", ofType: "mobileprovision") {
            return true
        }
        return false
    }

    private func isAppStoreReceiptSandbox() -> Bool {
        if isSimulator() {
            return false
        } else {
            if let appStoreReceiptURL = NSBundle.mainBundle().appStoreReceiptURL,
                let appStoreReceiptLastComponent = appStoreReceiptURL.lastPathComponent
                where appStoreReceiptLastComponent == "sandboxReceipt" {
                    return true
            }
            return false
        }
    }

    private func isSimulator() -> Bool {
        #if arch(i386) || arch(x86_64)
            return true
            #else
            return false
        #endif
    }

}
Run Code Online (Sandbox Code Playgroud)

要点:GitHub - mvarie/WhereAmIRunning.swift

2016年12月9日更新:

用户halileohalilei报告"这不再适用于iOS10和Xcode 8".我没有验证这一点,但请检查更新的HockeyKit源(见功能bit_currentAppEnvironment):

资料来源:GitHub - bitstadium/HockeySDK-iOS - BITHockeyHelper.m

随着时间的推移,上面的类已被修改,它似乎也处理iOS10.

  • @MichaelDautermann:好点!但是在iOS中,NSHomeDirectory()的行为有所不同:"在iOS中,主目录是应用程序的沙箱目录.在OS X中,它是应用程序的沙箱目录或当前用户的主目录(如果应用程序不在沙箱中)" (2认同)