从 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/4A74359F-E6CD-44C9-925D-AC82EB5EA837 /iTunesMetadata.plist,不幸的是,无法从应用程序访问它(权限被拒绝)
2015 年 11 月 4 日更新:
看来检查收据名称会有所帮助。必须注意的是,这个解决方案略有不同:它不会返回我们是否正在运行 App Store 应用程序,而是返回我们是否正在运行测试版 Testflight 应用程序。根据您的上下文,这可能有用也可能没用。
最重要的是,这是一个非常脆弱的解决方案,因为收据名称可能随时更改。无论如何我都会报告它,以防您没有其他选择:
// Objective-C
BOOL isRunningTestFlightBeta = [[[[NSBundle mainBundle] appStoreReceiptURL] lastPathComponent] isEqualToString:@"sandboxReceipt"];
// Swift
let isRunningTestFlightBeta = NSBundle.mainBundle().appStoreReceiptURL?.lastPathComponent=="sandboxReceipt"
来源:Detect if iOS App is Downloaded from Apple's 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
一个可能的 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
}
}
要点:GitHub - mvarie/WhereAmIRunning.swift
2016 年 12 月 9 日更新:
用户 halileohalilei 报告说“这不再适用于 iOS10 和 Xcode 8。”。我没有验证这一点,但请查看更新后的 HockeyKit 源代码(参见函数 bit_currentAppEnvironment):
来源:GitHub - bitstadium/HockeySDK-iOS - BITHockeyHelper.m
随着时间的推移,上面的类已经被修改,它似乎也可以处理iOS10。
2020 年 10 月 6 日更新:
曲棍球已被 Microsoft 的 AppCenter SDK 弃用/放弃并取而代之。
这是他们的 App Store / Testflight 构建检测类(代码下方的存储库链接):
MSUtility+Environment.h:
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#import <Foundation/Foundation.h>
#import "MSUtility.h"
/*
* Workaround for exporting symbols from category object files.
*/
extern NSString *MSUtilityEnvironmentCategory;
/**
* App environment
*/
typedef NS_ENUM(NSInteger, MSEnvironment) {
/**
* App has been downloaded from the AppStore.
*/
MSEnvironmentAppStore = 0,
/**
* App has been downloaded from TestFlight.
*/
MSEnvironmentTestFlight = 1,
/**
* App has been installed by some other mechanism.
* This could be Ad-Hoc, Enterprise, etc.
*/
MSEnvironmentOther = 99
};
/**
* Utility class that is used throughout the SDK.
* Environment part.
*/
@interface MSUtility (Environment)
/**
* Detect the environment that the app is running in.
*
* @return the MSEnvironment of the app.
*/
+ (MSEnvironment)currentAppEnvironment;
@end
MSUtility+Environment.m:
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#import "MSUtility+Environment.h"
/*
* Workaround for exporting symbols from category object files.
*/
NSString *MSUtilityEnvironmentCategory;
@implementation MSUtility (Environment)
+ (MSEnvironment)currentAppEnvironment {
#if TARGET_OS_SIMULATOR || TARGET_OS_OSX || TARGET_OS_MACCATALYST
return MSEnvironmentOther;
#else
// MobilePovision profiles are a clear indicator for Ad-Hoc distribution.
if ([self hasEmbeddedMobileProvision]) {
return MSEnvironmentOther;
}
/**
* TestFlight is only supported from iOS 8 onwards and as our deployment target is iOS 8, we don't have to do any checks for
* floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_6_1).
*/
if ([self isAppStoreReceiptSandbox]) {
return MSEnvironmentTestFlight;
}
return MSEnvironmentAppStore;
#endif
}
+ (BOOL)hasEmbeddedMobileProvision {
BOOL hasEmbeddedMobileProvision = !![[NSBundle mainBundle] pathForResource:@"embedded" ofType:@"mobileprovision"];
return hasEmbeddedMobileProvision;
}
+ (BOOL)isAppStoreReceiptSandbox {
#if TARGET_OS_SIMULATOR
return NO;
#else
if (![NSBundle.mainBundle respondsToSelector:@selector(appStoreReceiptURL)]) {
return NO;
}
NSURL *appStoreReceiptURL = NSBundle.mainBundle.appStoreReceiptURL;
NSString *appStoreReceiptLastComponent = appStoreReceiptURL.lastPathComponent;
BOOL isSandboxReceipt = [appStoreReceiptLastComponent isEqualToString:@"sandboxReceipt"];
return isSandboxReceipt;
#endif
}
@end
来源:GitHub - microsoft/appcenter-sdk-apple - MSUtility+Environment.m