在iOS中是否有办法以编程方式检查当前正在运行的应用程序是否已从iOS App Store安装?这与通过Xcode,TestFlight或任何非官方发布源运行的应用程序形成对比。
这是在无法访问应用源代码的SDK的上下文中。
要明确 - 我正在寻找一些签名,可以这么说,给应用程序(可能是Apple),在不依赖任何预处理程序标志或其他构建配置的情况下,可以在运行时访问任何应用程序
答案 0 :(得分:27)
从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-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"
来源:Detect if iOS App is Downloaded from Apple's 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;
}
来源: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。
答案 1 :(得分:4)
如果您正在谈论自己的应用程序,则可以添加一个状态,如果它是作为Store版本的一部分构建(例如编译器条件),则返回true,而在其他所有情况下都是false。
如果您正在谈论另一个应用程序,那么查询沙箱之外的其他应用程序并不容易或直截了当(或者甚至可能不可能)。
答案 2 :(得分:1)
由于@magma的代码不再有效IOS11.1这是一个冗长的解决方案。
我们检查应用商店中的应用版本,并将其与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
}
答案 3 :(得分:0)
我的观察是当设备连接到Xcode,然后我们打开管理器,切换到设备窗格时,它将列出未从App Store安装的所有应用程序。因此,您需要做的是下载Xcode,然后连接您的设备,转到“设备”窗格,查看从非App Store源安装的所有应用程序。这是最简单的解决方案。
答案 4 :(得分:-3)
您可以使用DEBUG预处理器宏来确定Xcode是否构建了应用程序,或者它是否是为App Store构建的。
BOOL isInAppStore = YES;
#ifdef DEBUG
isInAppStore = NO;
#endif
除非从App Store下载,否则应将每个案例的BOOL设置为NO。