Подтвердить что ты не робот

Как я могу определить, было ли установлено текущее приложение из магазина приложений?

Есть ли способ в iOS для программной проверки, было ли установлено текущее приложение из магазина приложений iOS? Это контрастирует с приложением, которое запускалось через Xcode, TestFlight или любой не официальный источник распространения.

Это в контексте SDK, который не имеет доступа к исходному коду приложения.

Чтобы быть ясным - я ищу некоторую подпись, если можно так выразиться, приложение (предположительно, Apple), которое будет независимо от любых препроцессорных флагов или других конфигураций сборки быть доступным для любого приложения во время выполнения.

4b9b3361

Ответ 1

Приложения, загруженные из магазина приложений, имеют файл 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, и, к сожалению, к нему нельзя получить доступ из приложения (разрешение отклонено)

Обновление 4 ноября 2015:

Похоже, что проверка имени квитанции может помочь. Следует отметить, что это решение немного отличается: оно не возвращает, работает ли приложение App Store, а работает ли приложение для бета-тестирования. Это может быть или не быть полезным в зависимости от вашего контекста.

Кроме того, это очень хрупкое решение, потому что имя получателя может измениться в любое время. Я сообщаю об этом в любом случае, если у вас нет других возможностей:

// Objective-C
BOOL isRunningTestFlightBeta = [[[[NSBundle mainBundle] appStoreReceiptURL] lastPathComponent] isEqualToString:@"sandboxReceipt"];

// Swift
let isRunningTestFlightBeta = NSBundle.mainBundle().appStoreReceiptURL?.lastPathComponent=="sandboxReceipt"

Источник: Обнаруживает, загружается ли приложение iOS из Apple 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
    }

}

Gist: GitHub - mvarie/WhereAmIRunning.swift

Обновление 9 декабря 2016 года:

Пользователь halileohalilei сообщает, что "это больше не работает с iOS10 и Xcode 8.". Я не проверял это, но, пожалуйста, проверьте обновленный источник HockeyKit (см. Функцию bit_currentAppEnvironment) по адресу:

Источник: GitHub - bitstadium/HockeySDK-iOS - BITHockeyHelper.m

Со временем этот класс был изменен и, похоже, также обрабатывает iOS10.

Ответ 2

Если вы говорите о своем собственном приложении, вы можете добавить состояние, которое возвращает true, если оно было создано как часть версии хранилища (например, условного компилятора) и false в каждом другом случае.

Если вы говорите о другом приложении, вам нелегко или просто (или, возможно, даже не возможно) запросить другие приложения за пределами вашей песочницы.

Ответ 3

Мое наблюдение - когда устройство подключено к Xcode, а затем мы открываем Organizer, переключитесь на панель "Устройства", в нем будут перечислены все Приложения, которые не установлены из App Store. Итак, что вам нужно сделать, это загрузить Xcode, затем подключить свое устройство, перейти на панель "Устройства" и посмотреть, какие приложения все установлены из источников, отличных от App Store. Это самое простое решение.

Ответ 4

Так как код @magma больше не работает IOS11.1 Вот немного долгое решение.

Мы проверяем версию приложения в магазине приложений и сравниваем ее с версией в пакете

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
}

Ответ 5

Вы можете использовать макрос препроцессора DEBUG, чтобы определить, было ли построено Xcode приложение или было ли оно создано для App Store.

BOOL isInAppStore = YES;

#ifdef DEBUG
    isInAppStore = NO;
#endif

Это должно установить BOOL NO для каждого случая, кроме случаев, когда он загружен из App Store.