如何检测当前运行的应用程序是否是从应用程序商店安装的? [英] How can I detect if the currently running app was installed from the app store?

查看:116
本文介绍了如何检测当前运行的应用程序是否是从应用程序商店安装的?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

Is there a way in iOS to programmatically check if the currently running app was installed from the iOS App Store? This is in contrast to an app that was run via Xcode, TestFlight, or any non-official distribution source.

这是在无权访问应用源代码的 SDK 的上下文中.

This is in the context of an SDK that doesn't have access to the app's source code.

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

To be clear - I am looking for some signature, so to speak, given to the app (presumably by Apple), that will, without dependence on any preprocessor flags or other build configurations, be accessible to any application at run time.

推荐答案

从 App Store 下载的应用有商店添加的 iTunesMetadata.plist 文件:

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.

更新:

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

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)

2015 年 11 月 4 日更新:

看来检查收据名称会有所帮助.必须注意的是,这个解决方案略有不同:它不会返回我们是否正在运行 App Store 应用程序,而是我们是否正在运行测试版 Testflight 应用程序.这可能有用也可能没用,具体取决于您的上下文.

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"

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

HockeyKit 是如何做到的

通过结合各种检查,您可以猜测应用是在模拟器、Testflight 版本还是 AppStore 版本中运行.

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.

这是来自 HockeyKit 的片段:

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

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

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

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

要点:GitHub - mvarie/WhereAmIRunning.swift

2016 年 12 月 9 日更新:

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

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:

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

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

Over time, the above class has been modified and it seems to handle iOS10 as well.

2020 年 10 月 6 日更新:

Hockey 已被弃用/弃用,取而代之的是 Microsoft 的 AppCenter SDK.

Hockey has been deprecated/abandoned and replaced by Microsoft's AppCenter SDK.

这是他们的 App Store/Testflight 构建检测类(链接到代码下方的存储库):

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

来源:GitHub - microsoft/appcenter-sdk-apple - MSUtility+Environment.m

这篇关于如何检测当前运行的应用程序是否是从应用程序商店安装的?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆