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

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

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

要清楚 – 我正在寻找一些签名,可以说,给予应用程序(大概是由苹果公司),这将不依赖于任何预处理器标志或其他构buildconfiguration,在任何应用程序可以在运行时访问。

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

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

也许你可能想检查这个文件是否存在。

更新

在iOS8中,应用程序包已被移动。 根据@silyevsk的说法, plist现在位于/ private / var / mobile / Containers / Bundle / Application / 4A6359F-E6CD-44C9-925D-AC82E B5EA837 / iTunesMetadata之上一层。 plist,不幸的是,这不能从应用程序访问(权限被拒绝)

2015年11月4日更新

看来,检查收据名称可以帮助。 必须注意的是,这个解决scheme稍有不同:不pipe我们运行的是App Store应用程序,而是运行betatesting版应用程序。 这可能会或可能不会有用,具体取决于您的上下文。

最重要的是,这是一个非常脆弱的解决scheme,因为收据名称可能会随时更改。 无论如何,我正在报告,以防您没有其他select:

 // Objective-C BOOL isRunningTestFlightBeta = [[[[NSBundle mainBundle] appStoreReceiptURL] lastPathComponent] isEqualToString:@"sandboxReceipt"]; // Swift let isRunningTestFlightBeta = NSBundle.mainBundle().appStoreReceiptURL?.lastPathComponent=="sandboxReceipt" 

来源: 如果iOS应用程序是从苹果的TestFlight下载的检测

HockeyKit如何做到这一点

通过结合各种检查,您可以猜测应用程序是否在模拟器,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; } 

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

基于HockeyKit的类可能的Swift类可能是:

 // // 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 } } 

Gist: GitHub – mvarie / WhereAmIRunning.swift

2016年12月9日更新

用户halileohalilei报告“这不再适用于iOS10和Xcode 8.”。 我没有validation这一点,但请检查更新的HockeyKit源码(请参阅函数bit_currentAppEnvironment ):

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

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

如果你正在谈论你自己的应用程序,你可以添加一个状态,如果它是作为一个存储版本的一部分(例如一个编译器有条件的)构build而返回true,而在其他情况下则为false。

如果您正在谈论另一个应用程序,那么在沙箱外部查询其他应用程序并不容易或简单(甚至不可能)。

我的观察是,当连接到Xcode的设备,然后我们打开pipe理器,切换到设备窗格,它会列出所有未从App Store安装的应用程序。 所以你必须做的是下载Xcode,然后连接你的设备,进入设备窗格,看看哪些应用程序是从非App Store源安装的。 这是最简单的解决scheme。

由于@magma的代码不再适用IOS11.1这里有一个长长的解决scheme。

我们检查app store的应用版本,并将其与Bundle中的版本进行比较

 static func isAppStoreVersion(completion: @escaping (Bool?, Error?) -> Void) throws -> URLSessionDataTask { guard let info = Bundle.main.infoDictionary, let currentVersion = info["CFBundleShortVersionString"] as? String, let identifier = info["CFBundleIdentifier"] as? String else { throw VersionError.invalidBundleInfo } let urlString = "https://itunes.apple.com/gb/lookup?bundleId=\(identifier)" guard let url = URL(string:urlString) else { throw VersionError.invalidBundleInfo } let task = URLSession.shared.dataTask(with: url) { (data, response, error) in do { if let error = error { throw error } guard let data = data else { throw VersionError.invalidResponse } let json = try JSONSerialization.jsonObject(with: data, options: [.allowFragments]) as? [String: Any] guard let result = (json?["results"] as? [Any])?.first as? [String: Any], let appStoreVersion = result["version"] as? String else { throw VersionError.invalidResponse } completion(appStoreVersion == currentVersion, nil) } catch { completion(nil, error) } } task.resume() return task } 

这样调用

 DispatchQueue.global(qos: .background).async { _ = try? VersionManager.isAppStoreVersion { (appStoreVersion, error) in if let error = error { print(error) } else if let appStoreVersion = appStoreVersion, appStoreVersion == true { // app store stuf } else { // other stuff } } } enum VersionError: Error { case invalidResponse, invalidBundleInfo } 

您可以使用DEBUG预处理macros来确定Xcode是否构build了应用程序,或者是否为App Store构build。

 BOOL isInAppStore = YES; #ifdef DEBUG isInAppStore = NO; #endif 

除了从App Store下载外,这应该将BOOL设置为NO。