Проверить, есть ли у моего приложения новая версия в AppStore

114

Я хотел бы вручную проверить, есть ли новые обновления для моего приложения, пока пользователь находится в нем, и предложить ему загрузить новую версию. Могу ли я сделать это, проверив версию своего приложения в магазине приложений - программно?

user542584
источник
7
Вы можете разместить случайную страницу на веб-сервере, которая возвращает только строковое представление последней версии. Загрузите его и сравните при запуске приложения и уведомите пользователя. (Быстрый и легкий способ)
LouwHopley
1
спасибо, но я надеялся на лучшее решение, такое как какой-то API, с помощью которого я могу вызывать функции магазина приложений, такие как поиск номера моего приложения и получение данных о версии. Экономит время на обслуживание веб-сервера только для этой цели, но в любом случае спасибо за указатель!
user542584 06
Я делаю то же самое, что и первый комментарий. Я написал список с одной записью: номер NSNumberверсии. Затем я загрузил его на свой сайт. Тот же веб-сайт, который я использую для поддержки своих приложений и веб-страниц приложений, затем viewDidLoadпроверяю номер версии на веб-сайте и проверяю текущую версию в своем приложении. Затем у меня есть готовый вариант, alertViewкоторый автоматически предлагает обновить приложение. Я могу предоставить код, если хотите.
Эндрю
спасибо, думаю, мне тоже стоит попробовать ..
user542584 07

Ответы:

89

Вот простой фрагмент кода, который позволяет узнать, отличается ли текущая версия

-(BOOL) needsUpdate{
    NSDictionary* infoDictionary = [[NSBundle mainBundle] infoDictionary];
    NSString* appID = infoDictionary[@"CFBundleIdentifier"];
    NSURL* url = [NSURL URLWithString:[NSString stringWithFormat:@"http://itunes.apple.com/lookup?bundleId=%@", appID]];
    NSData* data = [NSData dataWithContentsOfURL:url];
    NSDictionary* lookup = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];

    if ([lookup[@"resultCount"] integerValue] == 1){
        NSString* appStoreVersion = lookup[@"results"][0][@"version"];
        NSString* currentVersion = infoDictionary[@"CFBundleShortVersionString"];
        if (![appStoreVersion isEqualToString:currentVersion]){
            NSLog(@"Need to update [%@ != %@]", appStoreVersion, currentVersion);
            return YES;
        }
    }
    return NO;
}

Примечание. Убедитесь, что при вводе новой версии в iTunes она соответствует версии в приложении, которое вы выпускаете. Если нет, то приведенный выше код всегда будет возвращать ДА, независимо от того, обновляет ли пользователь.

datinc
источник
4
супер решение, которое я когда-либо
находил
1
@MobeenAfzal, я думаю, вы не поняли вопрос и решение. Приведенное выше решение сравнивает текущую версию с версией в магазине. Если они не совпадают, он перенастраивает ДА, иначе возвращает НЕТ. Независимо от истории в магазине приложений, указанный выше метод вернет ДА, если текущая версия отличается от версии магазина приложений. Как только пользователь обновит ... текущая версия будет равна версии магазина приложений. Вышеупомянутый метод всегда должен возвращать YES, если версия пользователя - 1.0, а версия магазина приложений - 1.2.
datinc
1
@MobeenAfzal Думаю, я понял то, что вы видите. В коде ваша версия - 1.7, но в iTunes вы загрузили версию как 1.6, чтобы ваши пользователи не знали, что вы пропустили версию. Так ли это? Если это так, то ... вам нужен сервер (DropBox подойдет) для обслуживания номера версии вашего приложения и изменения кода для доступа к этой конечной точке. Дайте мне знать, если это то, что вы видите, и я добавлю предупреждение в сообщение.
datinc
1
@MobeenAfzal, ваш комментарий вводит в заблуждение. Если версия на устройстве пользователя отделена от версии в магазине приложений, код вернет YES, как и ожидалось. Даже если вы выпустите версию 1.0, а затем версию 1.111, она все равно будет работать отлично.
datinc
1
Мы должны показывать обновление только тогда, когда версия appstore больше текущей версии, как показано ниже. if ([appStoreVersion compare: currentVersion options: NSNumericSearch] == NSOrderedDescending) {NSLog (@ "\ n \ nНужно обновить. Версия Appstore% @ больше, чем% @", appStoreVersion, currentVersion); }
Nitesh Borad 01
52

Версия Swift 3:

func isUpdateAvailable() throws -> Bool {
    guard let info = Bundle.main.infoDictionary,
        let currentVersion = info["CFBundleShortVersionString"] as? String,
        let identifier = info["CFBundleIdentifier"] as? String,
        let url = URL(string: "http://itunes.apple.com/lookup?bundleId=\(identifier)") else {
        throw VersionError.invalidBundleInfo
    }
    let data = try Data(contentsOf: url)
    guard let json = try JSONSerialization.jsonObject(with: data, options: [.allowFragments]) as? [String: Any] else {
        throw VersionError.invalidResponse
    }
    if let result = (json["results"] as? [Any])?.first as? [String: Any], let version = result["version"] as? String {
        return version != currentVersion
    }
    throw VersionError.invalidResponse
}

Я думаю, лучше выдать ошибку вместо возврата false, в этом случае я создал VersionError, но это может быть какой-то другой, который вы определяете, или NSError

enum VersionError: Error {
    case invalidResponse, invalidBundleInfo
}

Также рассмотрите возможность вызова этой функции из другого потока, если соединение медленное, оно может заблокировать текущий поток.

DispatchQueue.global().async {
    do {
        let update = try self.isUpdateAvailable()
        DispatchQueue.main.async {
            // show alert
        }
    } catch {
        print(error)
    }
}

Обновить

Использование URLSession:

Вместо использования Data(contentsOf: url)и блокировки потока мы можем использовать URLSession:

func isUpdateAvailable(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,
        let url = URL(string: "http://itunes.apple.com/lookup?bundleId=\(identifier)") else {
            throw VersionError.invalidBundleInfo
    }
    Log.debug(currentVersion)
    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 version = result["version"] as? String else {
                throw VersionError.invalidResponse
            }
            completion(version != currentVersion, nil)
        } catch {
            completion(nil, error)
        }
    }
    task.resume()
    return task
}

пример:

_ = try? isUpdateAvailable { (update, error) in
    if let error = error {
        print(error)
    } else if let update = update {
        print(update)
    }
}
цзюанджо
источник
1
Этот ответ делает запрос синхронно. Это означает, что при плохом соединении ваше приложение может быть непригодным для использования в течение нескольких минут, пока запрос не вернется.
uliwitness 03
4
Я не согласен, DispatchQueue.global()дает вам фоновую очередь, данные загружаются в эту очередь и возвращаются в основную очередь только при загрузке данных.
juanjo 03
Упс. Почему-то я упустил из виду второй фрагмент кода. К сожалению, похоже, я не могу удалить отрицательный голос, пока ваш ответ не будет отредактирован снова :-( BTW - Учитывая dataWithContentsOfURL: фактически проходит через синхронные вызовы NSURLConnection, которые, в свою очередь, просто запускают асинхронный поток и блокируют, это, вероятно, будет меньше накладных расходов чтобы просто использовать асинхронные вызовы NSURLSession. Они даже перезвонят вам в основном потоке, как только вы закончите.
uliwitness
@juanjo ,,,, не работает для Swift 3.0.1, пожалуйста, можете ли вы загрузить обновленное для Swift ???
Kiran jadhav
2
Обратите внимание: если вы указаны только в определенном магазине, я обнаружил, что вам нужно добавить код страны в URL-адрес - например, GB itunes.apple.com/(countryCode)/… )
Райан
13

Спасибо Стиву Мозеру за его ссылку, вот мой код:

NSString *appInfoUrl = @"http://itunes.apple.com/en/lookup?bundleId=XXXXXXXXX";

NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
[request setURL:[NSURL URLWithString:appInfoUrl]];
[request setHTTPMethod:@"GET"];

NSURLResponse *response;
NSError *error;
NSData *data = [NSURLConnection  sendSynchronousRequest:request returningResponse: &response error: &error];
NSString *output = [NSString stringWithCString:[data bytes] length:[data length]];

NSError *e = nil;
NSData *jsonData = [output dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *jsonDict = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error: &e];

NSString *version = [[[jsonDict objectForKey:@"results"] objectAtIndex:0] objectForKey:@"version"];
Рузбех Забихоллахи
источник
1
очень хорошее и правильное решение, просто небольшое обновление относительно URL- адреса itunes.apple.com/en/lookup?bundleId=xxxxxxxxxx
SJ
Спасибо, ваш комментарий применен
Roozbeh Zabihollahi
4
На самом деле у меня не получилось с /en/подпутьем. После удаления все
заработало
Этот ответ делает запрос синхронно. Это означает, что при плохом соединении ваше приложение может быть непригодным для использования в течение нескольких минут, пока запрос не вернется.
uliwitness 03
1
Мне пришлось использовать с / en / itunes.apple.com/lookup?bundleId=xxxxxxx , спасибо @gasparuff
Фернандо Перес
13

Поскольку я столкнулся с той же проблемой, я нашел ответ, который дал Марио Хендрикс . Безусловно, когда я попытался применить его код в своем проекте, XCode пожаловался на проблемы с кастингом, сказав, что «MDLMaterialProperty не имеет элементов индекса». Его код пытался установить этот MDLMaterial ... как тип константы «lookupResult», что приводило к сбоям при приведении к «Int» каждый раз. Мое решение заключалось в том, чтобы предоставить NSDictionary аннотацию типа для моей переменной, чтобы было понятно, какое значение мне нужно. Благодаря этому я мог получить доступ к нужному мне значению «версия».

Замечания: для этого YOURBUNDLEID вы можете получить из своего проекта Xcode .... « Цели> Общие> Идентификация> Идентификатор пакета »

Итак, вот мой код с некоторыми упрощениями:

  func appUpdateAvailable() -> Bool
{
    let storeInfoURL: String = "http://itunes.apple.com/lookup?bundleId=YOURBUNDLEID"
    var upgradeAvailable = false
    // Get the main bundle of the app so that we can determine the app's version number
    let bundle = NSBundle.mainBundle()
    if let infoDictionary = bundle.infoDictionary {
        // The URL for this app on the iTunes store uses the Apple ID for the  This never changes, so it is a constant
        let urlOnAppStore = NSURL(string: storeInfoURL)
        if let dataInJSON = NSData(contentsOfURL: urlOnAppStore!) {
            // Try to deserialize the JSON that we got
            if let dict: NSDictionary = try? NSJSONSerialization.JSONObjectWithData(dataInJSON, options: NSJSONReadingOptions.AllowFragments) as! [String: AnyObject] {
                if let results:NSArray = dict["results"] as? NSArray {
                    if let version = results[0].valueForKey("version") as? String {
                        // Get the version number of the current version installed on device
                        if let currentVersion = infoDictionary["CFBundleShortVersionString"] as? String {
                            // Check if they are the same. If not, an upgrade is available.
                            print("\(version)")
                            if version != currentVersion {
                                upgradeAvailable = true
                            }
                        }
                    }
                }
            }
        }
    }
    return upgradeAvailable
}

Все предложения по улучшению этого кода приветствуются!

Яго Зардо
источник
Этот ответ делает запрос синхронно. Это означает, что при плохом соединении ваше приложение может быть непригодным для использования в течение нескольких минут, пока запрос не вернется.
uliwitness 03
@Yago Zardo, пожалуйста, используйте функцию сравнения, иначе, когда пользователь загружает приложение, приложение проверяет время, отображает предупреждение об обновлении или яблоко отклоняет ваше приложение
Джигар Дарджи
Привет, @Jigar, спасибо за совет. В настоящее время я больше не использую этот метод в своем приложении, потому что теперь мы контролируем все версии на нашем сервере. В любом случае, не могли бы вы лучше объяснить, что вы сказали? Я не понял, и это действительно полезно знать. Заранее спасибо.
Яго Зардо
Спасибо @uliwitness за подсказку, это действительно помогло мне улучшить мой код в целом, чтобы узнать об асинхронных и синхронных запросах.
Яго Зардо
Эта ссылка - жемчужина!
B3none
13

Просто используйте ATAppUpdater . Это 1 строка, потокобезопасный и быстрый. У него также есть методы делегата, если вы хотите отслеживать действия пользователя.

Вот пример:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    [[ATAppUpdater sharedUpdater] showUpdateWithConfirmation]; // 1 line of code
    // or
    [[ATAppUpdater sharedUpdater] showUpdateWithForce]; // 1 line of code

   return YES;
}

Необязательные методы делегата:

- (void)appUpdaterDidShowUpdateDialog;
- (void)appUpdaterUserDidLaunchAppStore;
- (void)appUpdaterUserDidCancel;
эмоциональность
источник
1
Будет ли это работать для бета-версий в Testflight? Если нет, есть ли какой-нибудь инструмент, который подойдет?
Лукаш Червински
Нет, не будет, он только сравнивает текущую версию с последней версией, которая есть в AppStore.
Эмоталитет
Можем ли мы использовать это со Swift?
Зорайр
11

Упрощен отличный ответ, опубликованный в этой теме. Использование Swift 4и Alamofire.

import Alamofire

class VersionCheck {

  public static let shared = VersionCheck()

  func isUpdateAvailable(callback: @escaping (Bool)->Void) {
    let bundleId = Bundle.main.infoDictionary!["CFBundleIdentifier"] as! String
    Alamofire.request("https://itunes.apple.com/lookup?bundleId=\(bundleId)").responseJSON { response in
      if let json = response.result.value as? NSDictionary, let results = json["results"] as? NSArray, let entry = results.firstObject as? NSDictionary, let versionStore = entry["version"] as? String, let versionLocal = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String {
        let arrayStore = versionStore.split(separator: ".")
        let arrayLocal = versionLocal.split(separator: ".")

        if arrayLocal.count != arrayStore.count {
          callback(true) // different versioning system
        }

        // check each segment of the version
        for (key, value) in arrayLocal.enumerated() {
          if Int(value)! < Int(arrayStore[key])! {
            callback(true)
          }
        }
      }
      callback(false) // no new version or failed to fetch app store version
    }
  }

}

А потом использовать:

VersionCheck.shared.isUpdateAvailable() { hasUpdates in
  print("is update available: \(hasUpdates)")
}
Будидино
источник
2
Мое приложение находится в магазине, но тот же API не возвращает информацию о версии. Ответ:{ "resultCount":0, "results": [] }
technerd
Просто добавив примечание к сравнению версий, я бы предпочел, пусть serverVersion = "2.7" let localVersion = "2.6.5" let isUpdateAvailable = serverVersion.compare (localVersion, options: .numeric) == .orderedDescending вместо замены. с пустым.
Чайту,
@Chaitu спасибо за предложение. В итоге я переписал сравнительную часть кода
budidino
9

Обновлен код Swift 4 от Анупа Гупты.

Я внес некоторые изменения в этот код . Теперь функции вызываются из фоновой очереди, поскольку соединение может быть медленным и, следовательно, блокировать основной поток.

Я также сделал CFBundleName необязательным, поскольку в представленной версии было CFBundleDisplayName, которое, вероятно, не работало в моей версии. Итак, теперь, если его нет, он не выйдет из строя, а просто не отобразит имя приложения в предупреждении.

import UIKit

enum VersionError: Error {
    case invalidBundleInfo, invalidResponse
}

class LookupResult: Decodable {
    var results: [AppInfo]
}

class AppInfo: Decodable {
    var version: String
    var trackViewUrl: String
}

class AppUpdater: NSObject {

    private override init() {}
    static let shared = AppUpdater()

    func showUpdate(withConfirmation: Bool) {
        DispatchQueue.global().async {
            self.checkVersion(force : !withConfirmation)
        }
    }

    private  func checkVersion(force: Bool) {
        let info = Bundle.main.infoDictionary
        if let currentVersion = info?["CFBundleShortVersionString"] as? String {
            _ = getAppInfo { (info, error) in
                if let appStoreAppVersion = info?.version{
                    if let error = error {
                        print("error getting app store version: ", error)
                    } else if appStoreAppVersion == currentVersion {
                        print("Already on the last app version: ",currentVersion)
                    } else {
                        print("Needs update: AppStore Version: \(appStoreAppVersion) > Current version: ",currentVersion)
                        DispatchQueue.main.async {
                            let topController: UIViewController = UIApplication.shared.keyWindow!.rootViewController!
                            topController.showAppUpdateAlert(Version: (info?.version)!, Force: force, AppURL: (info?.trackViewUrl)!)
                        }
                    }
                }
            }
        }
    }

    private func getAppInfo(completion: @escaping (AppInfo?, Error?) -> Void) -> URLSessionDataTask? {
        guard let identifier = Bundle.main.infoDictionary?["CFBundleIdentifier"] as? String,
            let url = URL(string: "http://itunes.apple.com/lookup?bundleId=\(identifier)") else {
                DispatchQueue.main.async {
                    completion(nil, VersionError.invalidBundleInfo)
                }
                return nil
        }
        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 result = try JSONDecoder().decode(LookupResult.self, from: data)
                guard let info = result.results.first else { throw VersionError.invalidResponse }

                completion(info, nil)
            } catch {
                completion(nil, error)
            }
        }
        task.resume()
        return task
    }
}

extension UIViewController {
    @objc fileprivate func showAppUpdateAlert( Version : String, Force: Bool, AppURL: String) {
        let appName = Bundle.appName()

        let alertTitle = "New Version"
        let alertMessage = "\(appName) Version \(Version) is available on AppStore."

        let alertController = UIAlertController(title: alertTitle, message: alertMessage, preferredStyle: .alert)

        if !Force {
            let notNowButton = UIAlertAction(title: "Not Now", style: .default)
            alertController.addAction(notNowButton)
        }

        let updateButton = UIAlertAction(title: "Update", style: .default) { (action:UIAlertAction) in
            guard let url = URL(string: AppURL) else {
                return
            }
            if #available(iOS 10.0, *) {
                UIApplication.shared.open(url, options: [:], completionHandler: nil)
            } else {
                UIApplication.shared.openURL(url)
            }
        }

        alertController.addAction(updateButton)
        self.present(alertController, animated: true, completion: nil)
    }
}
extension Bundle {
    static func appName() -> String {
        guard let dictionary = Bundle.main.infoDictionary else {
            return ""
        }
        if let version : String = dictionary["CFBundleName"] as? String {
            return version
        } else {
            return ""
        }
    }
}

Я призываю также добавить кнопку подтверждения:

AppUpdater.shared.showUpdate(withConfirmation: true)

Или вызовите его, чтобы он назывался так, чтобы включить параметр принудительного обновления:

AppUpdater.shared.showUpdate(withConfirmation: false)
Васко
источник
Есть идеи, как это проверить? Если он не работает должным образом, единственный способ отладить его - это каким-то образом отладить более старую версию, чем та, которая есть в магазине приложений.
Дэвид Ректор
2
Ах, не говоря уже о вопросе. Я могу просто изменить свою локальную версию на «старую».
Дэвид Ректор
Я впечатлен вашим кодом @Vasco. Простой вопрос, почему вы использовали http вместо https в этом URL?
Master AgentX
Большое спасибо за то, что поделились этим решением @Vasco! Мне это нравится :) Почему вы не используете: let config = URLSessionConfiguration.background (withIdentifier: "com.example.MyExample.background") для URLSession для выполнения фонового запроса?
mc_plectrum
Вы также можете избавиться от принудительного развертывания, поскольку вы уже проверяете, разрешено ли appStoreAppVersion = info? .Version и то же самое для trackURL.
mc_plectrum
7

Вот моя версия с использованием Swift 4 и популярной библиотеки Alamofire (я все равно использую ее в своих приложениях). Запрос является асинхронным, и вы можете передать обратный вызов, чтобы получить уведомление, когда он будет выполнен.

import Alamofire

class VersionCheck {

    public static let shared = VersionCheck()

    var newVersionAvailable: Bool?
    var appStoreVersion: String?

    func checkAppStore(callback: ((_ versionAvailable: Bool?, _ version: String?)->Void)? = nil) {
        let ourBundleId = Bundle.main.infoDictionary!["CFBundleIdentifier"] as! String
        Alamofire.request("https://itunes.apple.com/lookup?bundleId=\(ourBundleId)").responseJSON { response in
            var isNew: Bool?
            var versionStr: String?

            if let json = response.result.value as? NSDictionary,
               let results = json["results"] as? NSArray,
               let entry = results.firstObject as? NSDictionary,
               let appVersion = entry["version"] as? String,
               let ourVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String
            {
                isNew = ourVersion != appVersion
                versionStr = appVersion
            }

            self.appStoreVersion = versionStr
            self.newVersionAvailable = isNew
            callback?(isNew, versionStr)
        }
    }
}

Простое использование:

VersionCheck.shared.checkAppStore() { isNew, version in
        print("IS NEW VERSION AVAILABLE: \(isNew), APP STORE VERSION: \(version)")
    }
Северный капитан
источник
1
Проблема с использованием ourVersion! = appVersion заключается в том, что он срабатывает, когда группа проверки App Store проверяет новую версию приложения. Мы конвертируем эти строки версий в числа, а затем isNew = appVersion> ourVersion.
budidino 05
@budidino, вы правы, я только что показал общий подход с использованием Alamofire. То, как вы интерпретируете версию, полностью зависит от вашего приложения и структуры версии.
Northern Captain
Просто добавив примечание к сравнению версий, я бы предпочел, пусть serverVersion = "2.7" let localVersion = "2.6.5" let isUpdateAvailable = serverVersion.compare (localVersion, options: .numeric) == .orderedDescending вместо сравнения с равным
Chaitu
6

Могу я предложить эту небольшую библиотеку: https://github.com/nicklockwood/iVersion

Его цель - упростить обработку удаленных списков для запуска уведомлений.

Андреа
источник
3
Вы можете проверить номер версии в App Store напрямую, вместо того, чтобы размещать где-нибудь файл plist. Посмотрите этот ответ: stackoverflow.com/a/6569307/142358
Стив Мозер,
1
iVersion теперь автоматически использует версию из магазина приложений - Plist необязателен, если вы хотите указать примечания к выпуску, отличные от тех, что есть в iTunes, но вам не нужно его использовать.
Ник Локвуд
1
В этом коде могут быть некоторые улучшения, но он намного лучше, чем другие ответы, отправляющие синхронный запрос. Тем не менее, использование потоков - плохой стиль. Я запишу проблемы на Github.
uliwitness 03
Проект устарел 😢
Зорайр
5

Swift 3.1

func needsUpdate() -> Bool {
    let infoDictionary = Bundle.main.infoDictionary
    let appID = infoDictionary!["CFBundleIdentifier"] as! String
    let url = URL(string: "http://itunes.apple.com/lookup?bundleId=\(appID)")
    guard let data = try? Data(contentsOf: url) else {
      print("There is an error!")
      return false;
    }
    let lookup = (try? JSONSerialization.jsonObject(with: data! , options: [])) as? [String: Any]
    if let resultCount = lookup!["resultCount"] as? Int, resultCount == 1 {
        if let results = lookup!["results"] as? [[String:Any]] {
            if let appStoreVersion = results[0]["version"] as? String{
                let currentVersion = infoDictionary!["CFBundleShortVersionString"] as? String
                if !(appStoreVersion == currentVersion) {
                    print("Need to update [\(appStoreVersion) != \(currentVersion)]")
                    return true
                }
            }
        }
    }
    return false
}
Кассем Итани
источник
Это вылетает, когда у вас нет подключения к Интернету. пусть данные = попробовать? Data (contentsOf: url!) Вернет nil, а в следующей строке вы сделаете данные!
Joris Mans
спасибо @JorisMans, я обновлю его, чтобы не было сбоев подключения к Интернету
Кассем Итани
Не делай этого. Используйте URLSession.
JAL
4

Этот ответ является модификацией ответа datinc https://stackoverflow.com/a/25210143/2735358 .

Функция datinc сравнивает версию путем сравнения строк. Таким образом, он не будет сравнивать версию больше или меньше чем.

Но эта модифицированная функция сравнивает версию по NSNumericSearch (числовое сравнение) .

- (void)checkForUpdateWithHandler:(void(^)(BOOL isUpdateAvailable))updateHandler {

    NSDictionary *infoDictionary = [[NSBundle mainBundle] infoDictionary];
    NSString *appID = infoDictionary[@"CFBundleIdentifier"];
    NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"http://itunes.apple.com/lookup?bundleId=%@", appID]];
    NSLog(@"iTunes Lookup URL for the app: %@", url.absoluteString);

    NSURLSession *session = [NSURLSession sharedSession];
    NSURLSessionDataTask *theTask = [session dataTaskWithRequest:[NSURLRequest requestWithURL:url]
                                               completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {

                                                   NSDictionary *lookup = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
                                                   NSLog(@"iTunes Lookup Data: %@", lookup);
                                                   if (lookup && [lookup[@"resultCount"] integerValue] == 1){
                                                       NSString *appStoreVersion = lookup[@"results"][0][@"version"];
                                                       NSString *currentVersion = infoDictionary[@"CFBundleShortVersionString"];

                                                       BOOL isUpdateAvailable = [appStoreVersion compare:currentVersion options:NSNumericSearch] == NSOrderedDescending;
                                                       if (isUpdateAvailable) {
                                                           NSLog(@"\n\nNeed to update. Appstore version %@ is greater than %@",appStoreVersion, currentVersion);
                                                       }
                                                       if (updateHandler) {
                                                           updateHandler(isUpdateAvailable);
                                                       }
                                                   }
                                               }];
    [theTask resume];
}

Использование:

[self checkForUpdateWithHandler:^(BOOL isUpdateAvailable) {
    if (isUpdateAvailable) {
        // show alert
    }
}];
Нитеш Борад
источник
3
Этот ответ делает запрос синхронно. Это означает, что при плохом соединении ваше приложение может быть непригодным для использования в течение нескольких минут, пока запрос не вернется.
uliwitness 03
NSURLSession работает с фоновыми потоками автоматически, если мы не укажем иное.
Себастьян Дворник
4

Я видел много способов проверить обновление приложения. поэтому на основе многих ответов я смешиваю их и создаю свое решение, которое доступно на GitHub. Если требуется какое- либо обновление, дайте мне знать. Этот код для Swift 4

Ссылка GitHub на этот код. https://github.com/anupgupta-arg/iOS-Swift-ArgAppUpdater

   import UIKit

enum VersionError: Error {
    case invalidBundleInfo, invalidResponse
}

class LookupResult: Decodable {
    var results: [AppInfo]
}

class AppInfo: Decodable {
    var version: String
    var trackViewUrl: String
    //let identifier = Bundle.main.infoDictionary?["CFBundleIdentifier"] as? String,
    // You can add many thing based on "http://itunes.apple.com/lookup?bundleId=\(identifier)"  response
    // here version and trackViewUrl are key of URL response
    // so you can add all key beased on your requirement.

}

class ArgAppUpdater: NSObject {
    private static var _instance: ArgAppUpdater?;

    private override init() {

    }

    public static func getSingleton() -> ArgAppUpdater {
        if (ArgAppUpdater._instance == nil) {
            ArgAppUpdater._instance = ArgAppUpdater.init();
        }
        return ArgAppUpdater._instance!;
    }

    private func getAppInfo(completion: @escaping (AppInfo?, Error?) -> Void) -> URLSessionDataTask? {
        guard let identifier = Bundle.main.infoDictionary?["CFBundleIdentifier"] as? String,
            let url = URL(string: "http://itunes.apple.com/lookup?bundleId=\(identifier)") else {
                DispatchQueue.main.async {
                    completion(nil, VersionError.invalidBundleInfo)
                }
                return nil
        }
        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 }

                print("Data:::",data)
                print("response###",response!)

                let result = try JSONDecoder().decode(LookupResult.self, from: data)

                let dictionary = try? JSONSerialization.jsonObject(with: data, options: .mutableLeaves)

                print("dictionary",dictionary!)


                guard let info = result.results.first else { throw VersionError.invalidResponse }
                print("result:::",result)
                completion(info, nil)
            } catch {
                completion(nil, error)
            }
        }
        task.resume()

        print("task ******", task)
        return task
    }
    private  func checkVersion(force: Bool) {
        let info = Bundle.main.infoDictionary
        let currentVersion = info?["CFBundleShortVersionString"] as? String
        _ = getAppInfo { (info, error) in

            let appStoreAppVersion = info?.version

            if let error = error {
                print(error)



            }else if appStoreAppVersion!.compare(currentVersion!, options: .numeric) == .orderedDescending {
                //                print("needs update")
               // print("hiiii")
                DispatchQueue.main.async {
                    let topController: UIViewController = UIApplication.shared.keyWindow!.rootViewController!

                    topController.showAppUpdateAlert(Version: (info?.version)!, Force: force, AppURL: (info?.trackViewUrl)!)
            }

            }
        }


    }

    func showUpdateWithConfirmation() {
        checkVersion(force : false)


    }

    func showUpdateWithForce() {
        checkVersion(force : true)
    }



}

extension UIViewController {


    fileprivate func showAppUpdateAlert( Version : String, Force: Bool, AppURL: String) {
        print("AppURL:::::",AppURL)

        let bundleName = Bundle.main.infoDictionary!["CFBundleDisplayName"] as! String;
        let alertMessage = "\(bundleName) Version \(Version) is available on AppStore."
        let alertTitle = "New Version"


        let alertController = UIAlertController(title: alertTitle, message: alertMessage, preferredStyle: .alert)


        if !Force {
            let notNowButton = UIAlertAction(title: "Not Now", style: .default) { (action:UIAlertAction) in
                print("Don't Call API");


            }
            alertController.addAction(notNowButton)
        }

        let updateButton = UIAlertAction(title: "Update", style: .default) { (action:UIAlertAction) in
            print("Call API");
            print("No update")
            guard let url = URL(string: AppURL) else {
                return
            }
            if #available(iOS 10.0, *) {
                UIApplication.shared.open(url, options: [:], completionHandler: nil)
            } else {
                UIApplication.shared.openURL(url)
            }

        }

        alertController.addAction(updateButton)
        self.present(alertController, animated: true, completion: nil)
    }
}

Ссылка: https://stackoverflow.com/a/48810541/5855888 И https://github.com/emotality/ATAppUpdater

Счастливого кодирования 👍 😊

Ануп Гупта
источник
@Rob, пожалуйста, проверьте ссылку на GitHub github.com/anupgupta-arg/iOS-Swift-ArgAppUpdater
Anup Gupta
3

ДЛЯ SWIFT 4 и 3.2:

Во-первых, нам нужно получить идентификатор пакета из словаря информации о пакете, установить isUpdaet как false.

    var isUpdate = false
    guard let bundleInfo = Bundle.main.infoDictionary,
        let currentVersion = bundleInfo["CFBundleShortVersionString"] as? String,
        //let identifier = bundleInfo["CFBundleIdentifier"] as? String,
        let url = URL(string: "http://itunes.apple.com/lookup?bundleId=\(identifier)")
        else{
        print("something wrong")
            completion(false)
        return
       }

Затем нам нужно вызвать вызов urlSession для получения версии из itunes.

    let task = URLSession.shared.dataTask(with: url) {
        (data, resopnse, error) in
        if error != nil{
             completion(false)
            print("something went wrong")
        }else{
            do{
                guard let reponseJson = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? [String:Any],
                let result = (reponseJson["results"] as? [Any])?.first as? [String: Any],
                let version = result["version"] as? String
                else{
                     completion(false)
                    return
                }
                print("Current Ver:\(currentVersion)")
                print("Prev version:\(version)")
                if currentVersion != version{
                    completion(true)
                }else{
                    completion(false)
                }
            }
            catch{
                 completion(false)
                print("Something went wrong")
            }
        }
    }
    task.resume()

ПОЛНЫЙ КОД БУДЕТ ТАК КАК ЭТО:

func checkForUpdate(completion:@escaping(Bool)->()){

    guard let bundleInfo = Bundle.main.infoDictionary,
        let currentVersion = bundleInfo["CFBundleShortVersionString"] as? String,
        //let identifier = bundleInfo["CFBundleIdentifier"] as? String,
        let url = URL(string: "http://itunes.apple.com/lookup?bundleId=\(identifier)")
        else{
        print("some thing wrong")
            completion(false)
        return
       }

    let task = URLSession.shared.dataTask(with: url) {
        (data, resopnse, error) in
        if error != nil{
             completion(false)
            print("something went wrong")
        }else{
            do{
                guard let reponseJson = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? [String:Any],
                let result = (reponseJson["results"] as? [Any])?.first as? [String: Any],
                let version = result["version"] as? String
                else{
                     completion(false)
                    return
                }
                print("Current Ver:\(currentVersion)")
                print("Prev version:\(version)")
                if currentVersion != version{
                    completion(true)
                }else{
                    completion(false)
                }
            }
            catch{
                 completion(false)
                print("Something went wrong")
            }
        }
    }
    task.resume()
}

Затем мы можем вызвать функцию из любого нужного нам программного обеспечения.

    checkForUpdate { (isUpdate) in
        print("Update needed:\(isUpdate)")
        if isUpdate{
            DispatchQueue.main.async {
                print("new update Available")
            }
        }
    }
Санду
источник
3

Попробуйте это с помощью одного вызова функции:

func showAppStoreVersionUpdateAlert(isForceUpdate: Bool) {

    do {
        //Get Bundle Identifire from Info.plist
        guard let bundleIdentifire = Bundle.main.infoDictionary?["CFBundleIdentifier"] as? String else {
            print("No Bundle Info found.")
            throw CustomError.invalidIdentifires
        }

        // Build App Store URL
        guard let url = URL(string:"http://itunes.apple.com/lookup?bundleId=" + bundleIdentifire) else {
            print("Isse with generating URL.")
            throw CustomError.invalidURL
        }

        let serviceTask = URLSession.shared.dataTask(with: url) { (responseData, response, error) in

            do {
                // Check error
                if let error = error { throw error }
                //Parse response
                guard let data = responseData else { throw CustomError.jsonReading }
                let result = try? JSONSerialization.jsonObject(with: data, options: .allowFragments)
                let itunes = ItunesAppInfoItunes.init(fromDictionary: result as! [String : Any])
                print(itunes.results)
                if let itunesResult = itunes.results.first {
                    print("App Store Varsion: ",itunesResult.version)

                    //Get Bundle Version from Info.plist
                    guard let appShortVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String else {
                        print("No Short Version Info found.")
                        throw CustomError.invalidVersion
                    }

                    if appShortVersion == itunesResult.version {
                        //App Store & Local App Have same Version.
                        print("Same Version at both side")
                    } else {
                        //Show Update alert
                        var message = ""
                        //Get Bundle Version from Info.plist
                        if let appName = Bundle.main.infoDictionary?["CFBundleName"] as? String {
                            message = "\(appName) has new version(\(itunesResult.version!)) available on App Store."
                        } else {
                            message = "This app has new version(\(itunesResult.version!)) available on App Store."
                        }

                        //Show Alert on the main thread
                        DispatchQueue.main.async {
                            self.showUpdateAlert(message: message, appStoreURL: itunesResult.trackViewUrl, isForceUpdate: isForceUpdate)
                        }
                    }
                }
            } catch {
                print(error)
            }
        }
        serviceTask.resume()
    } catch {
        print(error)
    }
}

Функция оповещения для открытия URL-адреса AppStore:

func showUpdateAlert(message : String, appStoreURL: String, isForceUpdate: Bool) {

    let controller = UIAlertController(title: "New Version", message: message, preferredStyle: .alert)

    //Optional Button
    if !isForceUpdate {
        controller.addAction(UIAlertAction(title: "Later", style: .cancel, handler: { (_) in }))
    }

    controller.addAction(UIAlertAction(title: "Update", style: .default, handler: { (_) in
        guard let url = URL(string: appStoreURL) else {
            return
        }
        if #available(iOS 10.0, *) {
            UIApplication.shared.open(url, options: [:], completionHandler: nil)
        } else {
            UIApplication.shared.openURL(url)
        }

    }))

    let applicationDelegate = UIApplication.shared.delegate as? AppDelegate
    applicationDelegate?.window?.rootViewController?.present(controller, animated: true)

}

Как вызвать указанную выше функцию:

AppStoreUpdate.shared.showAppStoreVersionUpdateAlert(isForceUpdate: false/true)

Для более подробной информации попробуйте ссылку ниже с полным кодом:

AppStoreUpdate.swift

ItunesAppInfoResult.swift

ItunesAppInfoItunes.swift

Надеюсь, это поможет!

CodeChanger
источник
2

Вот быстрый метод, который делает то, что предлагают некоторые ответы Objective-C. Очевидно, что как только вы получите информацию из JSON магазина приложений, вы можете извлечь примечания к выпуску, если хотите.

func appUpdateAvailable(storeInfoURL: String) -> Bool
{
    var upgradeAvailable = false

    // Get the main bundle of the app so that we can determine the app's version number
    let bundle = NSBundle.mainBundle()
    if let infoDictionary = bundle.infoDictionary {
        // The URL for this app on the iTunes store uses the Apple ID for the  This never changes, so it is a constant
        let urlOnAppStore = NSURL(string: storeInfoURL)
        if let dataInJSON = NSData(contentsOfURL: urlOnAppStore!) {
            // Try to deserialize the JSON that we got
            if let lookupResults = try? NSJSONSerialization.JSONObjectWithData(dataInJSON, options: NSJSONReadingOptions()) {
                // Determine how many results we got. There should be exactly one, but will be zero if the URL was wrong
                if let resultCount = lookupResults["resultCount"] as? Int {
                    if resultCount == 1 {
                        // Get the version number of the version in the App Store
                        if let appStoreVersion = lookupResults["results"]!![0]["version"] as? String {
                            // Get the version number of the current version
                            if let currentVersion = infoDictionary["CFBundleShortVersionString"] as? String {
                                // Check if they are the same. If not, an upgrade is available.
                                if appStoreVersion != currentVersion {
                                    upgradeAvailable = true                      
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    return upgradeAvailable
}
Марио Хендрикс
источник
storeInfoURL - это URL-адрес приложения в магазине приложений?
iamthevoid
@Mario Hendricks это не работает в быстром 3. Выдает некоторые ошибки. Не могли бы вы обновить Swift 3?
Джордж Асда
Этот ответ делает запрос синхронно. Это означает, что при плохом соединении ваше приложение может быть непригодным для использования в течение нескольких минут, пока запрос не вернется.
uliwitness 03
2

Если вы не устанавливаете тип контента в NSUrlRequest, тогда вы наверняка не получите ответа, поэтому попробуйте приведенный ниже код, он отлично работает для меня. Надеюсь, поможет....

-(BOOL) isUpdateAvailable{
    NSDictionary* infoDictionary = [[NSBundle mainBundle] infoDictionary];
    NSString* appID = infoDictionary[@"CFBundleIdentifier"];
    NSString *urlString = [NSString stringWithFormat:@"https://itunes.apple.com/lookup?bundleId=%@",appID];

    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
    [request setURL:[NSURL URLWithString:urlString]];
    [request setHTTPMethod:@"GET"];
    [request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];

    NSURLResponse *response;
    NSError *error;
    NSData *data = [NSURLConnection  sendSynchronousRequest:request returningResponse: &response error: &error];
    NSError *e = nil;
    NSDictionary *jsonDict = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error: &e];

    self.versionInAppStore = [[[jsonDict objectForKey:@"results"] objectAtIndex:0] objectForKey:@"version"];

    self.localAppVersion = infoDictionary[@"CFBundleShortVersionString"];

    if ([self.versionInAppStore compare:self.localAppVersion options:NSNumericSearch] == NSOrderedDescending) {
        // currentVersion is lower than the version
        return YES;
    }
    return NO;
}
ганка
источник
Этот ответ делает запрос синхронно. Это означает, что при плохом соединении ваше приложение может быть непригодным для использования в течение нескольких минут, пока запрос не вернется.
uliwitness 03
2

Исходя из гибридного приложения POV, это пример javascript, у меня есть нижний колонтитул для обновления в моем главном меню. Если доступно обновление (т. Е. Номер моей версии в файле конфигурации меньше, чем полученная версия, отобразить нижний колонтитул), это направит пользователя в магазин приложений, где пользователь затем может нажать кнопку обновления.

Я также получаю новые данные (например, примечания к выпуску) и отображаю их в модальном окне при входе в систему, если это первый раз в этой версии.

Метод «Доступно обновление» можно запускать сколь угодно часто. Мой запускается каждый раз, когда пользователь переходит на главный экран.

function isUpdateAvailable() {
        $.ajax('https://itunes.apple.com/lookup?bundleId=BUNDLEID', {
            type: "GET",
            cache: false,
            dataType: 'json'
        }).done(function (data) {
            _isUpdateAvailable(data.results[0]);
        }).fail(function (jqXHR, textStatus, errorThrown) {
            commsErrorHandler(jqXHR, textStatus, false);
        });

}

Обратный вызов: у Apple есть API, поэтому его очень легко получить

function isUpdateAvailable_iOS (data) {
    var storeVersion = data.version;
    var releaseNotes = data.releaseNotes;
    // Check store Version Against My App Version ('1.14.3' -> 1143)
    var _storeV = parseInt(storeVersion.replace(/\./g, ''));
    var _appV = parseInt(appVersion.substring(1).replace(/\./g, ''));
    $('#ft-main-menu-btn').off();
    if (_storeV > _appV) {
        // Update Available
        $('#ft-main-menu-btn').text('Update Available');
        $('#ft-main-menu-btn').click(function () {
           // Open Store      
           window.open('https://itunes.apple.com/us/app/appname/idUniqueID', '_system');
        });

    } else {
        $('#ft-main-menu-btn').html('&nbsp;');
        // Release Notes
        settings.updateReleaseNotes('v' + storeVersion, releaseNotes);
    }
}
tyler_mitchell
источник
2

Предупреждение: большинство приведенных ответов извлекают URL-адрес синхронно (с использованием -dataWithContentsOfURL:или -sendSynchronousRequest:. Это плохо, поскольку это означает, что ваше приложение не будет отвечать в течение нескольких минут, если мобильное соединение разорвется во время выполнения запроса. Никогда не выполняйте синхронный доступ к Интернету на основной поток.

Правильный ответ - использовать асинхронный API:

    NSDictionary* infoDictionary = [[NSBundle mainBundle] infoDictionary];
    NSString* appID = infoDictionary[@"CFBundleIdentifier"];
    NSURL* url = [NSURL URLWithString:[NSString stringWithFormat:@"http://itunes.apple.com/lookup?bundleId=%@", appID]];
    NSURLSession         *  session = [NSURLSession sharedSession];
    NSURLSessionDataTask *  theTask = [session dataTaskWithRequest: [NSURLRequest requestWithURL: url] completionHandler:
    ^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error)
    {
        NSDictionary<NSString*,NSArray*>* lookup = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
        if ([lookup[@"resultCount"] integerValue] == 1)
        {
            NSString* appStoreVersion = lookup[@"results"].firstObject[@"version"];
           NSString* currentVersion = infoDictionary[@"CFBundleShortVersionString"];

            if ([appStoreVersion compare:currentVersion options:NSNumericSearch] == NSOrderedDescending) {
                // *** Present alert about updating to user ***
            }
        }
    }];
    [theTask resume];

Тайм-аут по умолчанию для сетевых подключений составляет несколько минут, и даже если запрос проходит, он может быть достаточно медленным из-за плохого EDGE-подключения, чтобы занять столько времени. В этом случае вы не хотите, чтобы ваше приложение было непригодным для использования. Чтобы протестировать подобные вещи, полезно запустить сетевой код с помощью Apple Network Link Conditioner.

uliwitness
источник
Спасибо за то, что
оставили
2
func isUpdateAvailable() -> Bool {
    guard
        let info = Bundle.main.infoDictionary,
        let identifier = info["CFBundleIdentifier"] as? String,
        let url = URL(string: "http://itunes.apple.com/lookup?bundleId=\(identifier)"),
        let data = try? Data(contentsOf: url),
        let json = try? JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [String: Any],
        let results = json?["results"] as? [[String: Any]],
        results.count > 0,
        let versionString = results[0]["version"] as? String
        else {
            return false
    }

    return AppVersion(versionString) > AppVersion.marketingVersion
}

для сравнения строки версии:

https://github.com/eure/AppVersionMonitor

Lova
источник
2

Swift 4

Мы можем использовать новое JSONDecoderдля синтаксического анализа ответа с itunes.apple.com/lookup и представления его с помощью классов или структур Decodable:

class LookupResult: Decodable {
    var results: [AppInfo]
}

class AppInfo: Decodable {
    var version: String
}

Мы также можем добавить другие свойства, AppInfoесли нам понадобится это releaseNotesили какое-то другое свойство.

Теперь мы можем сделать асинхронный запрос, используя URLSession:

func getAppInfo(completion: @escaping (AppInfo?, Error?) -> Void) -> URLSessionDataTask? {
    guard let identifier = Bundle.main.infoDictionary?["CFBundleIdentifier"] as? String,
          let url = URL(string: "http://itunes.apple.com/lookup?bundleId=\(identifier)") else {
            DispatchQueue.main.async {
                completion(nil, VersionError.invalidBundleInfo)
            }
            return nil
    }
    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 result = try JSONDecoder().decode(LookupResult.self, from: data)
            guard let info = result.results.first else { throw VersionError.invalidResponse }

            completion(info, nil)
        } catch {
            completion(nil, error)
        }
    }
    task.resume()
    return task
}

enum VersionError: Error {
    case invalidBundleInfo, invalidResponse
}

эта функция получает завершение завершения, которое будет вызываться, когда запрос будет завершено, и возвращает URLSessionDataTaskв случае, если нам нужно отменить запрос, и может вызываться следующим образом:

func checkVersion() {
    let info = Bundle.main.infoDictionary
    let currentVersion = info?["CFBundleShortVersionString"] as? String
    _ = getAppInfo { (info, error) in
        if let error = error {
            print(error)
        } else if info?.version == currentVersion {
            print("updated")
        } else {
            print("needs update")
        }
    }
}
цзюанджо
источник
Куда вы положили этот код? Я вижу, что вы установили LookupResult и AppInfo как декодируемые, но я не вижу их нигде сохраненных. Что мне здесь не хватает?
jessi 08
Вы декларировать LookupResultи AppInfoклассы где - то в проекте, в отдельном файле , предпочтительно: Они используются , когда вы расшифровать ответ: JSONDecoder().decode(LookupResult.self, from: data)они содержат строку версии
Juanjo
Основываясь на вашем ответе, я создаю один файл, используя ваш код. Убедитесь, что iOS-Swift-ArgAppUpdater
Anup Gupta
@jessi, пожалуйста, проверьте Мой код на GitHub. Я разместил там ваше решение
Ануп Гупта,
2

Эквивалент @datinc в C # в том, что касается получения версии Apple App Store. Включенный код для получения версии как для пакета, так и для файла AssemblyInfo.

РЕДАКТИРОВАТЬ :: Обратите внимание на регион "/ us /", включенный в urlString. Этот код страны необходимо будет обработать / изменить соответствующим образом.

string GetAppStoreVersion()
{
    string version = "";

    NSDictionary infoDictionary = NSBundle
        .MainBundle
        .InfoDictionary;

    String appID = infoDictionary["CFBundleIdentifier"].ToString();

    NSString urlString = 
        new NSString(@"http://itunes.apple.com/us/lookup?bundleId=" + appID);
    NSUrl url = new NSUrl(new System.Uri(urlString).AbsoluteUri);

    NSData data = NSData.FromUrl(url);

    if (data == null)
    {
        /* <-- error obtaining data from url --> */
        return "";
    }

    NSError e = null;
    NSDictionary lookup = (NSDictionary)NSJsonSerialization
        .Deserialize(data, NSJsonReadingOptions.AllowFragments, out e);

    if (lookup == null)
    {
        /* <-- error, most probably no internet or bad connectivity --> */
        return "";
    }

    if (lookup["resultCount"].Description.Equals("1"))
    {
        NSObject nsObject = lookup["results"];
        NSString nsString = new NSString("version");
        String line = nsObject
            .ValueForKey(nsString)
            .Description;

        /* <-- format string --> */
        string[] digits = Regex.Split(line, @"\D+");
        for (int i = 0; i < digits.Length; i++)
        {
            if (int.TryParse(digits[i], out int intTest))
            {
                if (version.Length > 0)
                    version += "." + digits[i];
                else
                    version += digits[i];
            }
        }
    }

    return version;
}

string GetBundleVersion()
{
        return NSBundle
            .MainBundle
            .InfoDictionary["CFBundleShortVersionString"]
            .ToString();
}

string GetAssemblyInfoVersion()
{
        var assembly = typeof(App).GetTypeInfo().Assembly;
        var assemblyName = new AssemblyName(assembly.FullName);
        return assemblyName.Version.ToString();
}
jtth
источник
1

Этот вопрос был задан в 2011 году, я нашел его в 2018 году, когда искал способ не только проверить новую версию приложения в App Store, но и уведомить об этом пользователя.

После небольшого исследования я пришел к выводу, что ответ juanjo (связанный со Swift 3) https://stackoverflow.com/a/40939740/1218405 является оптимальным решением, если вы хотите сделать это в коде самостоятельно.

Также могу предложить два отличных проекта на GitHub (2300+ звезд каждый)

Пример для сирены (AppDelegate.swift)

  func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {

      let siren = Siren.shared
      siren.checkVersion(checkType: .immediately)

      return true
    }
  • Вы также можете отображать различные типы предупреждений о новой версии (позволяющие пропустить версию или заставить пользователя обновиться).
  • Вы можете указать, как часто должна выполняться проверка версии (ежедневно / еженедельно / немедленно)
  • Вы можете указать, через сколько дней после выпуска новой версии в магазине приложений должно появиться предупреждение
Moonvader
источник
Ссылки на существующий ответ не являются ответами. Кроме того, ссылки на библиотеки также не являются ответами, если вы явно не добавите, как ссылка отвечает на вопрос, к вашему ответу (добавьте примеры кода и т. Д.).
JAL
0

Мое предложение кода. На основе ответов @datinc и @ Mario-Hendricks

Вы, конечно, должны заменить dlog_Errorвызовом функции ведения журнала.

Такая структура кода должна предотвратить сбой вашего приложения в случае ошибки. Получение файла appStoreAppVersionне является обязательным и не должно приводить к фатальным ошибкам. И все же с такой структурой кода вы все равно будете регистрировать нефатальную ошибку.

class func appStoreAppVersion() -> String?
{
    guard let bundleInfo = NSBundle.mainBundle().infoDictionary else {
        dlog_Error("Counldn't fetch bundleInfo.")
        return nil
    }
    let bundleId = bundleInfo[kCFBundleIdentifierKey as String] as! String
    // dbug__print("bundleId = \(bundleId)")

    let address = "http://itunes.apple.com/lookup?bundleId=\(bundleId)"
    // dbug__print("address = \(address)")

    guard let url = NSURLComponents.init(string: address)?.URL else {
        dlog_Error("Malformed internet address: \(address)")
        return nil
    }
    guard let data = NSData.init(contentsOfURL: url) else {
        if Util.isInternetAvailable() {
            dlog_MajorWarning("Web server request failed. Yet internet is reachable. Url was: \(address)")
        }// else: internet is unreachable. All ok. It is of course impossible to fetch the appStoreAppVersion like this.
        return nil
    }
    // dbug__print("data.length = \(data.length)")

    if data.length < 100 { //: We got 42 for a wrong address. And aproximately 4684 for a good response
        dlog_MajorWarning("Web server message is unexpectedly short: \(data.length) bytes")
    }

    guard let response = try? NSJSONSerialization.JSONObjectWithData(data, options: []) else {
        dlog_Error("Failed to parse server response.")
        return nil
    }
    guard let responseDic = response as? [String: AnyObject] else {
        dlog_Error("Not a dictionary keyed with strings. Response with unexpected format.")
        return nil
    }
    guard let resultCount = responseDic["resultCount"] else {
        dlog_Error("No resultCount found.")
        return nil
    }
    guard let count = resultCount as? Int else { //: Swift will handle NSNumber.integerValue
        dlog_Error("Server response resultCount is not an NSNumber.integer.")
        return nil
    }
    //:~ Determine how many results we got. There should be exactly one, but will be zero if the URL was wrong
    guard count == 1 else {
        dlog_Error("Server response resultCount=\(count), but was expected to be 1. URL (\(address)) must be wrong or something.")
        return nil
    }
    guard let rawResults = responseDic["results"] else {
        dlog_Error("Response does not contain a field called results. Results with unexpected format.")
        return nil
    }
    guard let resultsArray = rawResults as? [AnyObject] else {
        dlog_Error("Not an array of results. Results with unexpected format.")
        return nil
    }
    guard let resultsDic = resultsArray[0] as? [String: AnyObject] else {
        dlog_Error("Not a dictionary keyed with strings. Results with unexpected format.")
        return nil
    }
    guard let rawVersion = resultsDic["version"] else {
        dlog_Error("The key version is not part of the results")
        return nil
    }
    guard let versionStr = rawVersion as? String else {
        dlog_Error("Version is not a String")
        return nil
    }
    return versionStr.e_trimmed()
}

extension String {
    func e_trimmed() -> String
    {
        return stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet())
    }
}
SirEnder
источник
1
Этот ответ делает запрос синхронно. Это означает, что при плохом соединении ваше приложение может быть непригодным для использования в течение нескольких минут, пока запрос не вернется.
uliwitness 03
-1

Обновлено для Swift 3:

если вы хотите проверить текущую версию своего приложения, используйте простой код ниже:

 let object = Bundle.main.infoDictionary?["CFBundleShortVersionString"]

  let version = object as! String
  print("version: \(version)")
Киран Джадхав
источник