Apps downloaded from the App Store have a iTunesMetadata.plist
file added by the store:
NSString *file=[NSHomeDirectory() stringByAppendingPathComponent:@"iTunesMetadata.plist"];
if ([[NSFileManager defaultManager] fileExistsAtPath:file]) {
// probably a store app
}
Perhaps you might want to check if this file exists.
Update:
In iOS8, the application bundle has been moved. According to @silyevsk, the plist is now one level above [the new application main bundle path], at /private/var/mobile/Containers/Bundle/Application/4A74359F-E6CD-44C9-925D-AC82E????B5EA837/iTunesMetadata.plist, and unfortunately, this can't be accessed from the app (permission denied)
Update Nov 4th 2015:
It appears that checking the receipt name can help. It must be noted that this solution is slightly different: it doesn't return whether we're running an App Store app, but rather whether we're running a beta Testflight app. This might or might not be useful depending on your context.
On top of that, it's a very fragile solution because the receipt name could change at any time. I'm reporting it anyway, in case you have no other options:
// Objective-C
BOOL isRunningTestFlightBeta = [[[[NSBundle mainBundle] appStoreReceiptURL] lastPathComponent] isEqualToString:@"sandboxReceipt"];
// Swift
let isRunningTestFlightBeta = NSBundle.mainBundle().appStoreReceiptURL?.lastPathComponent=="sandboxReceipt"
Source: Detect if iOS App is Downloaded from Apple's Testflight
How HockeyKit does it
By combining the various checks you can guess whether the app is running in a Simulator, in a Testflight build, or in an AppStore build.
Here's a segment from 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;
}
Source: GitHub - bitstadium/HockeySDK-iOS - BITHockeyHelper.m
A possible Swift class, based on HockeyKit's class, could be:
//
// 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
Update Dec 9th 2016:
User halileohalilei reports that "This no longer works with iOS10 and Xcode 8.". I didn't verify this, but please check the updated HockeyKit source (see function bit_currentAppEnvironment
) at:
Source: GitHub - bitstadium/HockeySDK-iOS - BITHockeyHelper.m
Over time, the above class has been modified and it seems to handle iOS10 as well.
Update Oct 6th 2020:
Hockey has been deprecated/abandoned and replaced by Microsoft's AppCenter SDK.
This is their App Store / Testflight build detection class (link to repository below code):
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
Source: GitHub - microsoft/appcenter-sdk-apple - MSUtility+Environment.m